文件概况
Linux的file命令:
ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b4876bc9fb492a1f16139a56b897b06f8e68d2c6, stripped
去除花指令
从798处开始就是一大堆被误认为数据的汇编代码。难道我的逆向生涯到此为止了吗?不!右键地址798处的第一个字节,左上角菜单Edit -> Code
转化为代码。
作者:hans774882968以及hans774882968
此时我们肉眼可以找到4处花指令:
.text:000000000000098B jb short near ptr loc_98F+1
.text:000000000000098D jnb short near ptr loc_98F+1
.text:0000000000000AD8 jb short near ptr loc_ADC+1
.text:0000000000000ADA jnb short near ptr loc_ADC+1
.text:0000000000000ADC loc_ADC:
.text:0000000000000ADC
.text:0000000000000ADC loop near ptr loc_B24+2
.text:0000000000000B4F jb short near ptr loc_B53+1
.text:0000000000000B51 jnb short near ptr loc_B53+1
.text:0000000000000B53
.text:0000000000000B53 loc_B53:
.text:0000000000000B53
.text:0000000000000B53 jrcxz near ptr loc_B9C+1
.text:0000000000000E0F jb short near ptr loc_E13+1
.text:0000000000000E11 jnb short near ptr loc_E13+1
.text:0000000000000E13
.text:0000000000000E13 loc_E13:
.text:0000000000000E13
.text:0000000000000E13 in al, 0C7h
冷姿势:这几处花指令对应的二进制数据都是72 03 73 01 xx
。把这5字节数据都用nop填充(换成90)即可。操作:左上角菜单Edit -> Patch program -> Change byte...
。
在loc_798
处右键,Create function
,并改名为main。此时可以F5了。
第一关
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax@22
__int64 v4; // rcx@29
char v5; // [sp+Fh] [bp-151h]@3
int v6; // [sp+10h] [bp-150h]@1
signed int v7; // [sp+14h] [bp-14Ch]@1
char *v8; // [sp+18h] [bp-148h]@1
__int64 v9; // [sp+26h] [bp-13Ah]@1
__int16 v10; // [sp+2Eh] [bp-132h]@1
__int64 v11; // [sp+30h] [bp-130h]@1
__int64 v12; // [sp+38h] [bp-128h]@1
int v13; // [sp+40h] [bp-120h]@1
__int64 v14; // [sp+50h] [bp-110h]@1
__int64 v15; // [sp+58h] [bp-108h]@1
__int64 v16; // [sp+60h] [bp-100h]@1
char v17; // [sp+68h] [bp-F8h]@1
__int64 v18; // [sp+70h] [bp-F0h]@1
__int64 v19; // [sp+78h] [bp-E8h]@1
__int64 v20; // [sp+80h] [bp-E0h]@1
char v21; // [sp+88h] [bp-D8h]@1
__int64 v22; // [sp+90h] [bp-D0h]@1
__int64 v23; // [sp+98h] [bp-C8h]@1
__int64 v24; // [sp+A0h] [bp-C0h]@1
__int64 v25; // [sp+A8h] [bp-B8h]@1
__int64 v26; // [sp+B0h] [bp-B0h]@1
__int64 v27; // [sp+B8h] [bp-A8h]@1
__int16 v28; // [sp+C0h] [bp-A0h]@1
__int64 v29; // [sp+D0h] [bp-90h]@1
__int64 v30; // [sp+D8h] [bp-88h]@1
__int64 v31; // [sp+E0h] [bp-80h]@1
__int64 v32; // [sp+E8h] [bp-78h]@1
__int64 v33; // [sp+F0h] [bp-70h]@1
__int64 v34; // [sp+F8h] [bp-68h]@1
__int64 v35; // [sp+100h] [bp-60h]@1
__int64 v36; // [sp+108h] [bp-58h]@1
__int64 v37; // [sp+110h] [bp-50h]@1
__int64 v38; // [sp+118h] [bp-48h]@1
__int64 v39; // [sp+120h] [bp-40h]@1
__int64 v40; // [sp+128h] [bp-38h]@1
__int64 v41; // [sp+130h] [bp-30h]@1
__int64 v42; // [sp+138h] [bp-28h]@1
__int64 v43; // [sp+140h] [bp-20h]@1
int v44; // [sp+148h] [bp-18h]@1
__int16 v45; // [sp+14Ch] [bp-14h]@1
__int64 v46; // [sp+158h] [bp-8h]@1
v46 = *MK_FP(__FS__, 40LL);
v6 = 0;
v14 = 0LL;
v15 = 0LL;
v16 = 0LL;
v17 = 0;
v18 = 0LL;
v19 = 0LL;
v20 = 0LL;
v21 = 0;
v22 = 0LL;
v23 = 0LL;
v24 = 0LL;
v25 = 0LL;
v26 = 0LL;
v27 = 0LL;
v28 = 0;
v29 = 3038287259199220266LL;
v30 = 3039413159106062890LL;
v31 = 3347065308717918762LL;
v32 = 3038287259266591278LL;
v33 = 3326517635351194154LL;
v34 = 3038287259199220266LL;
v35 = 3038287276446198314LL;
v36 = 3036321349588626990LL;
v37 = 3039417557152575022LL;
v38 = 3038287259199482410LL;
v39 = 3038287259199220266LL;
v40 = 3326517635350932010LL;
v41 = 3038287259266591274LL;
v42 = 3326517652597910058LL;
v43 = 3039413176353041966LL;
v44 = 774515246;
v45 = 42;
v11 = 0LL;
v12 = 0LL;
v13 = 0;
v8 = (char *)&v31 + 6;
v9 = 3472619869582943091LL;
v10 = 50;
puts("plz tell me the shortest password1:");
scanf("%s", &v22);
v7 = 1;
while ( v7 )
{
v5 = *((_BYTE *)&v22 + v6);
switch ( v5 )
{
case 'w':
v8 -= 5;
break;
case 's':
v8 += 5;
break;
case 'd':
++v8;
break;
case 'a':
--v8;
break;
case 'x':
v8 += 25;
break;
case 'y':
v8 -= 25;
break;
default:
v7 = 0;
break;
}
++v6;
if ( *v8 != '.' && *v8 != '#' )
v7 = 0;
if ( *v8 == '#' )
{
puts("good!you find the right way!\nBut there is another challenge!");
break;
}
}
if ( v7 )
{
puts("plz tell me the password2:");
scanf("%s", &v14);
sub_C22(&v14, &v18);
if ( sub_F67(&v18, &v9) == 1 )
{
puts("Congratulation!");
puts("Now,this is the last!");
puts("plz tell me the password3:");
scanf("%s", &v11);
if ( sub_FFA(&v11) == 1 )
{
puts("Congratulation!Here is your flag!:");
printf("sctf{%s-%s(%s)}", &v22, &v14, &v11);
}
else
{
printf("something srong...");
}
result = 0;
}
else
{
printf("sorry,somthing wrong...");
result = 0;
}
}
else
{
printf("sorry,is't not a right way...");
result = 0;
}
v4 = *MK_FP(__FS__, 40LL) ^ v46;
return result;
}
上面有些不明所以的v29 = 3038287259199220266LL
之类的数据,让我深深怀疑花指令没除干净……它们的汇编长这样(只截取了一小部分)
; ...
.text:0000000000000857 mov rax, 2A2A2A2A2A2A2A2Ah
.text:0000000000000861 mov rdx, 2A2E2A2A2A2A2A2Ah
.text:000000000000086B mov [rbp+var_90], rax
.text:0000000000000872 mov [rbp+var_88], rdx
.text:0000000000000879 mov rax, 2E732A2A2E2A2A2Ah
.text:0000000000000883 mov rdx, 2A2A2A2A2E2E2A2Eh
; ...
但随后我鼠标右键发现,可以转为字符串,这才意识到这玩意就是迷宫数据。在反汇编窗口右键数据,点击Char,即可看到迷宫:
v29 = '********';
v30 = '*.******';
v31 = '.s**.***';
v32 = '****..*.';
v33 = '.****.**';
v34 = '********';
v35 = '***..***';
v36 = '*#..**..';
v37 = '*..***..';
v38 = '*****.**';
v39 = '********';
v40 = '.*******';
v41 = '****..**';
v42 = '.**..***';
v43 = '*.*..*..';
v44 = '.**.';
v45 = '*';
解迷宫代码如下:
def solve_maze(mp):
delta = [
[0,-1,"a"],[0,1,"d"],[1,0,"s"],
[-1,0,"w"],[5,0,"x"],[-5,0,"y"]
]
path = ""
N,M = 25,5#迷宫行列数
st = mp.index("s")
ed = mp.index("#")
ID = lambda x,y: x * M + y
vis = [[False] * M for _ in range(N)]
def dfs(x,y,cPath = ""):
nonlocal path
if x < 0 or x >= N or y < 0 or y >= M or vis[x][y] or mp[ID(x,y)] == "*":
return False
if ID(x,y) == ed:
path = cPath
return True
vis[x][y] = True
for i in range(6):
fl = dfs(x + delta[i][0],y + delta[i][1],cPath + delta[i][2])
if fl:
vis[x][y] = False
return True
vis[x][y] = False
return False
dfs(st // M,st % M)
return path
def main():
maze_raw = '''
v29 = '********';
v30 = '*.******';
v31 = '.s**.***';
v32 = '****..*.';
v33 = '.****.**';
v34 = '********';
v35 = '***..***';
v36 = '*#..**..';
v37 = '*..***..';
v38 = '*****.**';
v39 = '********';
v40 = '.*******';
v41 = '****..**';
v42 = '.**..***';
v43 = '*.*..*..';
v44 = '.**.';
v45 = '*';
'''.split("\n")
mp = ""
for ln in maze_raw:
ln = ln.strip()
if not ln: continue
mp += ln[ln.index("'")+1:-2][::-1]
print(mp,len(mp))#dbg
for i in range(25): print(mp[i*5:i*5+5])#dbg
path = solve_maze(mp)
print(path)#ddwwxxssxaxwwaasasyywwdd
if __name__ == "__main__":
main()
值得注意的点:
- 这题没有特判,只接受字典序最小的路径:
ddwwxxssxaxwwaasasyywwdd
。虽然saxssd
也是合法的,但它在buuoj里就不对。 - 如何求字典序最小路径?路径尝试顺序按字典序升序排序即可。
saxssd
也是合法路径的证明:
// 在虚拟机(Ubuntu)中运行:
plz tell me the shortest password1:
saxssd
good!you find the right way!
But there is another challenge!
plz tell me the password2:
c2N0Zl85MTAy
Congratulation!
Now,this is the last!
plz tell me the password3:
fl4g_is_s0_ug1y!
Congratulation!Here is your flag!:
sctf{saxssd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}
第二关
__int64 __fastcall sub_C22(char *a1, __int64 a2)
{
signed __int64 v2; // rcx@1
__int64 v3; // rdi@1
_UNKNOWN *v4; // rsi@1
bool v5; // al@12
int v6; // eax@21
int v7; // eax@23
int v8; // eax@25
__int64 out; // [sp+0h] [bp-260h]@1
char *s; // [sp+8h] [bp-258h]@1
int v12; // [sp+14h] [bp-24Ch]@4
signed int v13; // [sp+18h] [bp-248h]@4
int v14; // [sp+1Ch] [bp-244h]@17
int v15; // [sp+20h] [bp-240h]@4
int v16; // [sp+24h] [bp-23Ch]@4
int sl; // [sp+28h] [bp-238h]@4
int v18; // [sp+2Ch] [bp-234h]@5
char *v19; // [sp+48h] [bp-218h]@4
int v20[130]; // [sp+50h] [bp-210h]@1
__int64 v21; // [sp+258h] [bp-8h]@1
s = a1;
out = a2;
v21 = *MK_FP(__FS__, 40LL);
v2 = 64LL;
v3 = (__int64)v20;
v4 = &unk_1740;
// v20单纯地从unk_1740常量数组取值
while ( v2 )
{
*(_QWORD *)v3 = *(_QWORD *)v4;
v4 = (char *)v4 + 8;
v3 += 8LL;
--v2;
}
v13 = 3;
v12 = 0;
v15 = 0;
v16 = 0;
sl = strlen(s);
v19 = s;
while ( 1 )
{
v18 = 0;
if ( v15 < sl )
break;
LABEL_16:
if ( v15 >= sl )
goto LABEL_17;
}
do
{
if ( s[v15] != 25 )
break;
++v15;
++v18;
}
while ( v15 < sl );
if ( v15 != sl )
{
if ( sl - v15 > 1 )
{
v5 = v15 == 19 && s[20] == 16;
s[v5];
}
++v15;
goto LABEL_16;
}
LABEL_17:
v14 = 0;
// v19即输入串
while ( sl > 0 )
{
v13 -= v20[*v19] == 0x40;
v12 = v20[*v19] & 0x3F | (v12 << 6);
++v14;
if ( v14 == 4 )
{
v14 = 0;
if ( v13 )
{
v6 = v16++;
*(_BYTE *)(v6 + out) = v12 >> 16;
}
if ( v13 > 1 )
{
v7 = v16++;
*(_BYTE *)(v7 + out) = BYTE1(v12);
}
if ( v13 > 2 )
{
v8 = v16++;
*(_BYTE *)(v8 + out) = v12;
}
}
++v19;
--sl;
}
return *MK_FP(__FS__, 40LL) ^ v21;
}
sub_F67(&v18, &v9)
就是判定v18
和v9
字符串是否相等。把v18改名out,out
等于字符串sctf_9102
即可。分析过程如下:
- 别忘了低地址低位,
v9
右键转为Char后记得反转。 - main函数的
v10
和v9
相邻,且类型为int16,初值为50(即'2'
),高位的0构成了结尾字符'\0'
。
接下来看sub_C22的这段代码:
int v20[130]; // [sp+50h] [bp-210h]@1
v2 = 64LL;
v3 = (__int64)v20;
v4 = &unk_1740;
// v20单纯地从unk_1740常量数组取值
while ( v2 )
{
*(_QWORD *)v3 = *(_QWORD *)v4;
v4 = (char *)v4 + 8;
v3 += 8LL;
--v2;
}
这段代码实际上就是memcpy,但我误以为它是每隔8字节取一个值呜呜呜……
如何从unk_1740
常量数组取值:选中512个字节,shift+E,然后写一小段python,每隔4字节取一个值。
sub_C22逆过程的python代码如下:
def main():
dat = [127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 127, 127, 127, 127, 127]
s = "sctf_9102"
ans = ""
for i in range(0,9,3):
v = (ord(s[i]) << 16) | (ord(s[i+1]) << 8) | ord(s[i+2])
for j in range(18,-1,-6):
ans += chr(dat.index(v >> j & 0x3f))
print(ans)# c2N0Zl85MTAy
if __name__ == "__main__":
main()
实际上,看到那段代码“每处理4个字符,输出3个字符”,也可以猜想是base64。并假定没有换码表,也得c2N0Zl85MTAy
。
第三关
signed __int64 __fastcall sub_FFA(__int64 a1)
{
int v1; // ST24_4@1
int v2; // ST28_4@1
int v3; // ST2C_4@1
signed __int64 result; // rax@6
__int64 v5; // rcx@9
signed int v6; // [sp+18h] [bp-158h]@1
signed int i; // [sp+18h] [bp-158h]@3
int v8; // [sp+1Ch] [bp-154h]@1
int v9; // [sp+30h] [bp-140h]@1
int v10; // [sp+34h] [bp-13Ch]@1
int v11; // [sp+38h] [bp-138h]@1
int v12; // [sp+3Ch] [bp-134h]@1
int v13; // [sp+40h] [bp-130h]@1
int v14; // [sp+44h] [bp-12Ch]@1
int v15; // [sp+48h] [bp-128h]@1
int v16; // [sp+4Ch] [bp-124h]@1
int v17; // [sp+50h] [bp-120h]@1
int v18; // [sp+54h] [bp-11Ch]@1
int v19; // [sp+58h] [bp-118h]@1
int v20; // [sp+5Ch] [bp-114h]@1
int v21; // [sp+60h] [bp-110h]@1
int v22; // [sp+64h] [bp-10Ch]@1
int v23; // [sp+68h] [bp-108h]@1
int v24; // [sp+6Ch] [bp-104h]@1
unsigned int v25; // [sp+70h] [bp-100h]@3
int v26; // [sp+74h] [bp-FCh]@3
int v27; // [sp+78h] [bp-F8h]@3
int v28; // [sp+7Ch] [bp-F4h]@3
unsigned int v29; // [sp+80h] [bp-F0h]@3
int v30; // [sp+84h] [bp-ECh]@3
int v31; // [sp+88h] [bp-E8h]@3
int v32; // [sp+8Ch] [bp-E4h]@3
unsigned int v33; // [sp+90h] [bp-E0h]@3
int v34; // [sp+94h] [bp-DCh]@3
int v35; // [sp+98h] [bp-D8h]@3
int v36; // [sp+9Ch] [bp-D4h]@3
unsigned int v37; // [sp+A0h] [bp-D0h]@3
int v38; // [sp+A4h] [bp-CCh]@3
int v39; // [sp+A8h] [bp-C8h]@3
int v40; // [sp+ACh] [bp-C4h]@3
int v41; // [sp+B0h] [bp-C0h]@1
int v42; // [sp+B4h] [bp-BCh]@1
int v43; // [sp+B8h] [bp-B8h]@1
int v44; // [sp+BCh] [bp-B4h]@1
unsigned int v45; // [sp+118h] [bp-58h]@3
unsigned int v46; // [sp+11Ch] [bp-54h]@3
unsigned int v47; // [sp+120h] [bp-50h]@3
unsigned int v48; // [sp+124h] [bp-4Ch]@3
__int64 v49; // [sp+168h] [bp-8h]@1
v49 = *MK_FP(__FS__, 40LL);
v9 = 190;
v10 = 4;
v11 = 6;
v12 = 128;
v13 = 197;
v14 = 175;
v15 = 118;
v16 = 71;
v17 = 159;
v18 = 204;
v19 = 64;
v20 = 31;
v21 = 216;
v22 = 191;
v23 = 146;
v24 = 239;
v1 = (*(_BYTE *)(a1 + 6) << 8) | (*(_BYTE *)(a1 + 5) << 16) | (*(_BYTE *)(a1 + 4) << 24) | *(_BYTE *)(a1 + 7);
v2 = (*(_BYTE *)(a1 + 10) << 8) | (*(_BYTE *)(a1 + 9) << 16) | (*(_BYTE *)(a1 + 8) << 24) | *(_BYTE *)(a1 + 11);
v3 = (*(_BYTE *)(a1 + 14) << 8) | (*(_BYTE *)(a1 + 13) << 16) | (*(_BYTE *)(a1 + 12) << 24) | *(_BYTE *)(a1 + 15);
v8 = 0;
v6 = 4;
v41 = sub_78A((*(_BYTE *)(a1 + 2) << 8) | (*(_BYTE *)(a1 + 1) << 16) | (*(_BYTE *)a1 << 24) | (unsigned int)*(_BYTE *)(a1 + 3));
v42 = sub_78A((unsigned int)v1);
v43 = sub_78A((unsigned int)v2);
v44 = sub_78A((unsigned int)v3);
// 低地址高位,再按字节颠倒,故以上代码相当于直接从输入串复制前16个字符,低地址低位。
do
{
*(&v41 + v6) = sub_143B(
(unsigned int)*(&v41 + v8),
(unsigned int)*(&v41 + v8 + 1),
(unsigned int)*(&v41 + v8 + 2),
(unsigned int)*(&v41 + v8 + 3));
++v8;
++v6;
}
while ( v6 <= 29 );
v25 = v45 >> 24;
v26 = (unsigned __int8)(v45 >> 16);
v27 = BYTE1(v45);
v28 = (unsigned __int8)v45;
v29 = v46 >> 24;
v30 = (unsigned __int8)(v46 >> 16);
v31 = BYTE1(v46);
v32 = (unsigned __int8)v46;
v33 = v47 >> 24;
v34 = (unsigned __int8)(v47 >> 16);
v35 = BYTE1(v47);
v36 = (unsigned __int8)v47;
v37 = v48 >> 24;
v38 = (unsigned __int8)(v48 >> 16);
v39 = BYTE1(v48);
v40 = (unsigned __int8)v48;
// v9和v25是两个整数数组
for ( i = 0; i <= 15; ++i )
{
if ( *(&v25 + i) != *(&v9 + i) )
{
result = 0xFFFFFFFFLL;
goto LABEL_9;
}
}
result = 1LL;
LABEL_9:
v5 = *MK_FP(__FS__, 40LL) ^ v49;
return result;
}
__int64 __fastcall sub_143B(int a1, int a2, int a3, unsigned int a4)
{
return a1 ^ (unsigned int)sub_1464(a2 ^ a3 ^ a4);
}
__int64 __fastcall sub_1464(unsigned int a1)
{
signed __int64 v1; // rcx@1
__int64 v2; // rdi@1
_UNKNOWN *v3; // rsi@1
int v4; // ST18_4@4
int v5; // eax@4
int v6; // edx@4
int v7; // eax@4
int v8; // edx@4
int v9; // eax@4
int v10; // edx@4
unsigned int v11; // eax@4
__int64 result; // rax@4
__int64 v13; // rcx@4
unsigned int v14; // [sp+Ch] [bp-4A4h]@1
int v15[290]; // [sp+20h] [bp-490h]@1
__int64 v16; // [sp+4A8h] [bp-8h]@1
v14 = a1;
v16 = *MK_FP(__FS__, 40LL);
v1 = 144LL;
v2 = (__int64)v15;
v3 = &unk_1940;
while ( v1 )
{
*(_QWORD *)v2 = *(_QWORD *)v3;
v3 = (char *)v3 + 8;
v2 += 8LL;
--v1;
}
v4 = (v15[(unsigned __int64)(unsigned __int8)(v14 >> 16)] << 16) | v15[(unsigned __int64)(unsigned __int8)v14] | (v15[(unsigned __int64)BYTE1(v14)] << 8) | (v15[(unsigned __int64)(v14 >> 24)] << 24);
v5 = __ROR4__(v4, 2);
v6 = v5;
v7 = __ROL4__(v4, 8);
v8 = v7 ^ v6;
v9 = __ROL4__(v4, 12);
v10 = v9 ^ v8;
v11 = __ROR4__(v4, 6);
result = v10 ^ v11;
v13 = *MK_FP(__FS__, 40LL) ^ v16;
return result;
}
先看
v1 = (*(_BYTE *)(a1 + 6) << 8) | (*(_BYTE *)(a1 + 5) << 16) | (*(_BYTE *)(a1 + 4) << 24) | *(_BYTE *)(a1 + 7);
v2 = (*(_BYTE *)(a1 + 10) << 8) | (*(_BYTE *)(a1 + 9) << 16) | (*(_BYTE *)(a1 + 8) << 24) | *(_BYTE *)(a1 + 11);
v3 = (*(_BYTE *)(a1 + 14) << 8) | (*(_BYTE *)(a1 + 13) << 16) | (*(_BYTE *)(a1 + 12) << 24) | *(_BYTE *)(a1 + 15);
v8 = 0;
v6 = 4;
v41 = sub_78A((*(_BYTE *)(a1 + 2) << 8) | (*(_BYTE *)(a1 + 1) << 16) | (*(_BYTE *)a1 << 24) | (unsigned int)*(_BYTE *)(a1 + 3));
v42 = sub_78A((unsigned int)v1);
v43 = sub_78A((unsigned int)v2);
v44 = sub_78A((unsigned int)v3);
// 低地址高位,再按字节颠倒,故以上代码相当于直接从输入串复制前16个字符,低地址低位。
根据参考链接1,_byteswap_ulong
(即sub_78A
的返回值)以字节为单位颠倒。因此有以上注释的结论。
逆向思路
v25和v9是两个int数组,v41也是int数组,v45~v48
正是v41数组的最后4个下标。我们已经知道了v45~v48
,(记al = v41.size()
)而v41[al - 5] = v45 ^ (unsigned int)sub_1464(v46 ^ v47 ^ v48)
故能推出v41[al - 5]
。依此类推,倒序遍历即可获得v41~v44
。于是可得输入串。
sub_1464
很复杂,因此建议使用cpp来实现逆向过程。注意点:
defs.h
可以在IDA的安装路径中找到。- 只有v14变量必须为unsigned int,因为需要保证下标为非负,并且int和unsigned int只有数据解释方式的差别。
#include <bits/stdc++.h>
#include "defs.h"
using namespace std;
typedef long long LL;
typedef unsigned int ui;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
LL sub_1464 (int a1) {
LL v2; // rdi@1
int v4; // ST18_4@4
int v5; // eax@4
int v6; // edx@4
int v7; // eax@4
int v8; // edx@4
int v9; // eax@4
int v10; // edx@4
int v11; // eax@4
LL result; // rax@4
LL v13; // rcx@4
ui v14; // [sp+Ch] [bp-4A4h]@1
int v15[290] = {
214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72, 198, 186, 177, 163, 80, 51, 170, 86, 151, 145, 125, 103, 220, 34, 112, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}; // [sp+20h] [bp-490h]@1
LL v16; // [sp+4A8h] [bp-8h]@1
v14 = a1;
v4 = (v15[ (unsigned __int8) (v14 >> 16)] << 16) | v15[ (unsigned __int8) v14] | (v15[BYTE1 (v14)] << 8) | (v15[ (v14 >> 24)] << 24);
v5 = __ROR4__ (v4, 2);
v6 = v5;
v7 = __ROL4__ (v4, 8);
v8 = v7 ^ v6;
v9 = __ROL4__ (v4, 12);
v10 = v9 ^ v8;
v11 = __ROR4__ (v4, 6);
result = v10 ^ v11;
return result;
}
int getV (int v1, int v2, int v3, int v4) {
return (v1 << 24) | (v2 << 16) | (v3 << 8) | v4;
}
int main() {
vector<int> a = {
getV (216, 191, 146, 239), getV (159, 204, 64, 31),
getV (197, 175, 118, 71), getV (190, 4, 6, 128)
};
dwn (i, 25, 0) {
int al = a.size();
a.push_back (a[al - 4] ^ sub_1464 (a[al - 3] ^ a[al - 2] ^ a[al - 1]) );
}
int al = a.size();
string ans;
rep (i, 1, 4) {
for (int j = 0; j < 32; j += 8)
dbg (i, j, a[al - i] >> j & 0xff), ans += char (a[al - i] >> j & 0xff);
}
dbg (ans);
return 0;
}
flag:flag{ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}
参考链接
- _byteswap_ulong:docs.microsoft.com/zh-cn/cpp/c…