自修改代码,程序在执行某段代码的过程中会对程序的代码进行修改,只有在修改后的代码才是可汇编,可执行的。在程序未对该段代码进行修改之前,在静态分析状态下,均是不可读的字节码。
在使用IDA进行调试的过程中可能会遇到Create Process Fail这个错误,这是因为被调试程序的路径不是全英文的。
选择动态调试。
选择 Local Windows debugger。
在主函数的第一条指令下一个断点。
点击开始调试图标进行调试
此时程序停在主函数第一条指令上面,按F5反编译成伪C代码,如下:
int wmain()
{
char *v0; // eax
int v1; // ST24_4
__int16 v2; // ST28_2
char v4; // [esp+1Ch] [ebp-40h]
char v5; // [esp+36h] [ebp-26h]
int v6; // [esp+38h] [ebp-24h]
__int16 v7; // [esp+3Ch] [ebp-20h]
int v8; // [esp+40h] [ebp-1Ch]
__int16 v9; // [esp+44h] [ebp-18h]
int v10; // [esp+48h] [ebp-14h]
char *v11; // [esp+4Ch] [ebp-10h]
int i; // [esp+50h] [ebp-Ch]
int v13; // [esp+54h] [ebp-8h]
__int16 v14; // [esp+58h] [ebp-4h]
i = 0;
printf("Welcome to AFCTF,I hope you have a good time!\n");
scanf("%s", &v4);
if ( strlen(&v4) == 27 )
{
if ( !strncmp(&v4, "afctf{", 6u) && v5 == 125 )
{
v5 = 0;
v0 = strtok(&v4, "_");
v11 = v0;
v0 += 6;
v8 = *(_DWORD *)v0;
v9 = *((_WORD *)v0 + 2);
v13 = *(_DWORD *)(v11 + 6);
v14 = *((_WORD *)v11 + 5);
v11 = strtok(0, "_");
v6 = *(_DWORD *)v11;
v7 = *((_WORD *)v11 + 2);
v11 = strtok(0, "_");
v1 = *(_DWORD *)v11;
v2 = *((_WORD *)v11 + 2);
dword_122EF70 = (int)dword_1237F50;
if ( ((int (__cdecl *)(int *))dword_1237F50[0])(&v8) )
{
v10 = SHIBYTE(v14) ^ (char)v13 ^ (char)v14 ^ SHIBYTE(v13) ^ SBYTE2(v13) ^ SBYTE1(v13);
for ( i = 256; i < 496; ++i )
byte_1238048[i] = v10 ^ *(_BYTE *)(i + 19103816);
JUMPOUT(__CS__, &byte_1238048[256]);
}
printf("Wrong\n");
}
else
{
printf("Wrong\n");
}
}
else
{
printf("Wrong\n");
}
return 0;
}
这段代码的意思是:首先判断输入的flag长度是否为27、开头6个字母是否为“afctff{”、结尾字符是否为“}”;接下来将afctf{……}中间的内容取出来,以’_’为分界符将其分成三段,每段6个字节。然后进入if判断。
在39行下一个断点,按F9执行到这里,在终端随便输入一个符合格式的字符串:afctf{123456_123456_123456},按下F7跟进验证函数。
接下来看到汇编代码如下:
.data:01237F50 add esi, 43AEF761h
.data:01237F56 call $+5
.data:01237F5B pop esi
.data:01237F5C push edi
.data:01237F5D xor edi, edi
.data:01237F5F
.data:01237F5F loc_1237F5F: ; CODE XREF: .data:01237F75↓j
.data:01237F5F cmp edi, 0C0h
.data:01237F65 jg short loc_1237F7B
.data:01237F67 mov bl, [esi+edi+25h]
.data:01237F6B xor bl, 73h
.data:01237F6E mov [esi+edi+25h], bl
.data:01237F72 add edi, 1
.data:01237F75 jmp short loc_1237F5F
.data:01237F75 ; ---------------------------------------------------------------------------
.data:01237F77 db 65h ; e
.data:01237F78 db 61h ; a
.data:01237F79 db 73h ; s
.data:01237F7A db 79h ; y
.data:01237F7B ; ---------------------------------------------------------------------------
.data:01237F7B
.data:01237F7B loc_1237F7B: ; CODE XREF: .data:01237F65↑j
.data:01237F7B pop edi
.data:01237F7C nop
.data:01237F7D nop
.data:01237F7E nop
.data:01237F7F nop
.data:01237F80 db 26h
.data:01237F80 clc
.data:01237F82 lahf
.data:01237F83 lock lahf
.data:01237F85 imul esp, ds:0F8336B5h, -4Bh
.data:01237F8C adc byte ptr ss:[edx], 0B5h
.data:01237F90 sub dword ptr ss:[ebx-4Bh], 0B5698036h
.data:01237F98 xchg edi, ss:[edx]
.data:01237F9B mov ch, 36h
.data:01237F9D xchg cl, [edx-4Bh]
.data:01237FA0 mov edi, ss:[edi]
.data:01237FA3 mov ch, 36h
.data:01237FA5 mov cl, [edi-4Bh]
.data:01237FA8 mov ss:[ecx-4Bh], esp
.data:01237FAC mov ss:[edi], dh
.data:01237FAF mov ch, 36h
call $+5; pop esi;这两句指令获得pop esi;这句指令的地址并存入esi寄存器中,即01237F5B。
loc_1237F5F中代码是一个循环,这段汇编的意思是将 01237F80 地址开始,长度为0xC0的所有数据全部异或0x73,从而实现对关键代码的解密。按下01237F7F处的指令,F4执行到这里。选中从 01237F80 地址开始,长度为0xC0的代码段,按C键重新分析。ALT键+L可以进行选中。然后在01237F80 位置按P键建立函数,按下F5反汇编成伪代码:
BOOL __cdecl sub_1027F80(int a1)
{
signed int i; // [esp+4h] [ebp-18h]
int v3; // [esp+8h] [ebp-14h]
char v4; // [esp+Ch] [ebp-10h]
char v5; // [esp+Dh] [ebp-Fh]
char v6; // [esp+Eh] [ebp-Eh]
char v7; // [esp+Fh] [ebp-Dh]
char v8; // [esp+10h] [ebp-Ch]
char v9; // [esp+11h] [ebp-Bh]
char v10; // [esp+14h] [ebp-8h]
char v11; // [esp+15h] [ebp-7h]
char v12; // [esp+16h] [ebp-6h]
char v13; // [esp+17h] [ebp-5h]
char v14; // [esp+18h] [ebp-4h]
char v15; // [esp+19h] [ebp-3h]
v4 = 124;
v5 = 97;
v6 = 24;
v7 = 26;
v8 = 73;
v9 = 57;
v10 = 76;
v11 = 60;
v12 = 18;
v13 = 68;
v14 = 92;
v15 = 42;
v3 = 0;
for ( i = 0; i < 6; ++i )
{
*(_BYTE *)(i + a1) ^= *(&v4 + (i + 2) % 6) - 18;
if ( *(char *)(i + a1) == *(&v10 + i) )
++v3;
}
return v3 == 6;
}
根据这段算法写出解题代码:
arr1=[124,97,24,26,73,57]
arr2=[76,60,18,68,92,42]
s1=""
for i in range(6):
s1+=chr(arr2[i]^(arr1[(i+2)%6]-18))
print(s1)
输出第一段为:J4%c6e
继续调试,发现输出wrong,重新调试,并把第一段改成正确的:afctf{J4%c6e_123456_123456}。
此时由于第一段已经解出来了,我们可以直接找到ret然后F4、F8跳过第一段的解密代码,进入第二段。
.text:0100114F movsx ecx, byte ptr [ebp+var_8+1]
.text:01001153 movsx edx, byte ptr [ebp+var_8+2]
.text:01001157 xor ecx, edx
.text:01001159 movsx eax, byte ptr [ebp+var_8+3]
.text:0100115D xor ecx, eax
.text:0100115F movsx edx, byte ptr [ebp+var_4]
.text:01001163 xor ecx, edx
.text:01001165 movsx eax, byte ptr [ebp+var_8]
.text:01001169 xor ecx, eax
.text:0100116B movsx edx, byte ptr [ebp+var_4+1]
.text:0100116F xor ecx, edx
.text:01001171 mov [ebp+var_14], ecx
.text:01001174 mov [ebp+var_C], 100h
.text:0100117B jmp short loc_1001186
.text:0100117D ; ---------------------------------------------------------------------------
.text:0100117D
.text:0100117D loc_100117D: ; CODE XREF: _wmain+1A5↓j
.text:0100117D mov eax, [ebp+var_C]
.text:01001180 add eax, 1
.text:01001183 mov [ebp+var_C], eax
.text:01001186
.text:01001186 loc_1001186: ; CODE XREF: _wmain+17B↑j
.text:01001186 cmp [ebp+var_C], 1F0h
.text:0100118D jge short loc_10011A7
.text:0100118F mov ecx, [ebp+var_C]
.text:01001192 movsx edx, byte ptr [ecx+1028048h]
.text:01001199 xor edx, [ebp+var_14]
.text:0100119C mov eax, [ebp+var_C]
.text:0100119F mov byte_1028048[eax], dl
.text:010011A5 jmp short loc_100117D
.text:010011A7 ; ---------------------------------------------------------------------------
.text:010011A7
.text:010011A7 loc_10011A7: ; CODE XREF: _wmain+18D↑j
.text:010011A7 push eax
.text:010011A8 push ecx
.text:010011A9 push esi
.text:010011AA call $+5
.text:010011AF pop ecx
.text:010011B0 lea eax, byte_1028048
.text:010011B6 lea esi, [esp+68h+var_4C]
.text:010011BA add eax, 100h
.text:010011BF jmp eax
这段代码从地址0100114F到01001171的意思是将刚刚已解出的6个字符一起异或后放在[ebp+var_14]中,然后将在100h+1028048h=1028148h处存放的长度为1F0h-100h=F0h的代码与[ebp+var_14]中的数据进行异或还原出第二段的解密代码。然后通过 jmp eax 跳到解密代码。
.data:01028148 mov esi, [esi]
.data:0102814A xor edi, edi
.data:0102814C push ecx
.data:0102814D
.data:0102814D loc_102814D: ; CODE XREF: .data:0102816F↓j
.data:0102814D cmp edi, 6
.data:01028150 jge short loc_1028173
.data:01028152 xor ecx, ecx
.data:01028154 mov cl, [esi+edi]
.data:01028157 add cl, 1
.data:0102815A ror cl, 2
.data:0102815D jmp short loc_1028171
.data:0102815D ; ---------------------------------------------------------------------------
.data:0102815F db 12h
.data:01028160 db 34h ; 4
.data:01028161 db 56h ; V
.data:01028162 db 78h ; x
.data:01028163 db 90h
.data:01028164 db 0ABh
.data:01028165 ; ---------------------------------------------------------------------------
.data:01028165
.data:01028165 loc_1028165: ; CODE XREF: .data:loc_1028171↓j
.data:01028165 xor cl, [eax+edi+17h]
.data:01028169 mov [esi+edi], cl
.data:0102816C add edi, 1
.data:0102816F jmp short loc_102814D
.data:01028171 ; ---------------------------------------------------------------------------
.data:01028171
.data:01028171 loc_1028171: ; CODE XREF: .data:0102815D↑j
.data:01028171 jmp short loc_1028165
.data:01028173 ; ---------------------------------------------------------------------------
.data:01028173
.data:01028173 loc_1028173: ; CODE XREF: .data:01028150↑j
.data:01028173 xor edi, edi
.data:01028175
.data:01028175 loc_1028175: ; CODE XREF: .data:010281B9↓j
.data:01028175 cmp edi, 6
.data:01028178 jge short loc_10281BB
.data:0102817A mov cl, [esi+edi]
.data:0102817D mov ch, cl
.data:0102817F and ch, 0F0h
.data:01028182 ror ch, 4
.data:01028185 and cl, 0Fh
.data:01028188 sub eax, 100h
.data:0102818D rol cl, 4
.data:01028190 push ebx
.data:01028191 mov bl, cl
.data:01028193 add bl, ch
.data:01028195 and ebx, 0FFh
.data:0102819B mov bl, [eax+ebx]
.data:0102819E jmp short loc_10281A7
.data:0102819E ; ---------------------------------------------------------------------------
.data:010281A0 db 91h
.data:010281A1 db 3Eh ; >
.data:010281A2 db 16h
.data:010281A3 db 64h ; d
.data:010281A4 db 0CDh
.data:010281A5 db 86h
.data:010281A6 db 90h
.data:010281A7 ; ---------------------------------------------------------------------------
.data:010281A7
.data:010281A7 loc_10281A7: ; CODE XREF: .data:0102819E↑j
.data:010281A7 cmp bl, [eax+edi+158h]
.data:010281AE jnz short loc_10281C6
.data:010281B0 pop ebx
.data:010281B1 add edi, 1
.data:010281B4 add eax, 100h
.data:010281B9 jmp short loc_1028175
.data:010281BB ; ---------------------------------------------------------------------------
.data:010281BB
.data:010281BB loc_10281BB: ; CODE XREF: .data:01028178↑j
.data:010281BB pop ecx
.data:010281BC mov eax, 1
.data:010281C1 lea ecx, [ecx+12h]
.data:010281C4 jmp ecx
.data:010281C6 ; ---------------------------------------------------------------------------
.data:010281C6
.data:010281C6 loc_10281C6: ; CODE XREF: .data:010281AE↑j
.data:010281C6 xor eax, eax
.data:010281C8 pop ecx
.data:010281C9 pop ecx
.data:010281CA add ecx, 12h
.data:010281CD jmp ecx
这段代码从地址0102814D到0102816F是将输入的6个字节每个都加1且不带符号循环右移2位,然后再按顺序与[eax+edi+17h]中的数据异或。从地址01028175到是将每个字节的高四位与第四位进行位置交换,然后在地址1028048处进行查表替换,然后依次与[eax+edi+158h]中的数据进行对比,如果相等就通过第二段。
计算过程:值为:91h,3Eh,16h,64h,0CDh,86h得数据在表中的位置依次为:98h,ABh,B9h,67h,DCh,7Eh,将高四位与第四位交换后得到:89h,BAh,9Bh,76h,CDh,E7h,与[eax+edi+17h]中的数据异或后得到:0x9b,0x8e,0xcd,0x0e,0x5d,0x4c,循环左移后得到:6Eh,3Ah,37h,38h,75h,31h,减一后得到:6Dh,39h,36h,37h,74h,30h,即第二段为“m967t0”。
重新调试,正确输入前两段,进入第三段,代码如下:
.text:010011DC push 6 ; size_t
.text:010011DE push offset a50sa ; "50Sa*^"
.text:010011E3 lea ecx, [ebp+var_48]
.text:010011E6 push ecx ; char *
.text:010011E7 call _strncmp
.text:010011EC add esp, 0Ch
.text:010011EF test eax, eax
.text:010011F1 jz short loc_1001202
.text:010011F3 push offset aWrong ; "Wrong\n"
.text:010011F8 call _printf
.text:010011FD add esp, 4
.text:01001200 jmp short loc_1001217
.text:01001202 ; ---------------------------------------------------------------------------
.text:01001202
.text:01001202 loc_1001202: ; CODE XREF: _wmain+1F1↑j
.text:01001202 push offset aCongYouWin ; "Cong,You win!"
.text:01001207 call _printf
.text:0100120C add esp, 4
.text:0100120F xor edx, edx
.text:01001211 jnz loc_100102C
.text:01001217
.text:01001217 loc_1001217: ; CODE XREF: _wmain+69↑j
.text:01001217 ; _wmain+9B↑j ...
.text:01001217 xor eax, eax
.text:01001219 pop esi
.text:0100121A mov esp, ebp
.text:0100121C pop ebp
.text:0100121D retn
.text:0100121D _wmain endp
第三段代码很简单,就是跟字符串”50Sa*^”做匹配就好了,正确会输出”Cong,You win!”。
于是综合三段可以得到flag为:afctf{J4%c6e_m967t0_50Sa*^}
F4的作用是跳到指定地址,但是其工作过程是先在指定地址处下硬件断点,程序运行到该断点处停止,最后再把该处的硬件断点删掉。
在本次调试过程中,因为在一个循环的第一条指令处手动下了一个断点,然后再结尾jmp指令处使用F4。这个错误的过程导致按下F4之后并没有执行jmp跳转,而是直接停在了第一条指令这里。