昨晚躺尸的时候看到群里大佬发了一张DDCTF战况的图就起床做了一下,也就有了这个题解,RE2跟RE1其实核心思想是差不多的,只是多了一些移位操作罢了,算是弥补上个月0CTF、“西湖论剑”逆向零的突破吧
终究还是太菜了,另外一道CPP逆向也是看得头疼
——2019.4.13
0x00 查壳
一看是UPX壳,修复一下然后直接脱!
0x01 分析
1.载入IDA X86
- main函数伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax@2
char v4; // [sp+4h] [bp-804h]@1
char v5; // [sp+5h] [bp-803h]@1
char v6; // [sp+404h] [bp-404h]@1
char Dst; // [sp+405h] [bp-403h]@1
v6 = 0;
memset(&Dst, 0, 0x3FFu);
v4 = 0;
memset(&v5, 0, 0x3FFu);
printf("please input code:");
scanf("%s", &v6);
sub_401000(&v6);
if ( !strcmp(&v4, "DDCTF{reverseME}") )
{
printf("You've got it!!%s\n", &v4);
result = 0;
}
else
{
printf("Try again later.\n");
result = 0;
}
return result;
}
- sub_401000函数伪代码
unsigned int __cdecl sub_401000(const char *a1)
{
_BYTE *v1; // ecx@0
unsigned int v2; // edi@1
unsigned int result; // eax@1
int v4; // ebx@2
v2 = 0;
result = strlen(a1);
if ( result )
{
v4 = a1 - v1;
do
{
*v1 = byte_402FF8[v1[v4]];
++v2;
++v1;
result = strlen(a1);
}
while ( v2 < result );
}
return result;
}
程序大致就是将输入字符串在函数sub_401000中进行简单变换后与"DDCTF{reverseME}"比较
2.载入OD
直接在变换函数前断点
然后动态调试
伪代码中的变换就是401020~40103E的内容
核心代码无非就三条
……
movsx eax,byte ptr ds:[ebx+eax]//ds:[ebx+eax]寻址获得的数据即为将要处理的数据,并将其字扩展存入eax中
mov dl,byte ptr ds:[eax+0x402FF8]//通过eax和0x402FF8相对寄存器存址获得的字节数据存入dl中
……
mov byte ptr ds:[ecx],dl//dl数据写入数据段
……
//循环执行
所以该函数就是将输入的字符串逐个字符的hex作为偏移量在数据段中寻址获得的字符逐个拼接并写入数据段
即伪代码是
flag=""
for i in input
flag+=ds:[hex(i)+0x402FF8]
查看地址为0x402FF8起的数据段内容
.rdata:00402FF8 ; char byte_402FF8[]
.rdata:00402FF8 byte_402FF8 db ? ; DATA XREF: sub_401000+24�r
.rdata:00402FF9 align 10h
.rdata:00402FF9 _rdata ends
.rdata:00402FF9
.data:00403000 ; Section 3. (virtual address 00003000)
.data:00403000 ; Virtual size : 000003E4 ( 996.)
.data:00403000 ; Section size in file : 00000200 ( 512.)
.data:00403000 ; Offset to raw data for section: 00001600
.data:00403000 ; Flags C0000040: Data Readable Writable
.data:00403000 ; Alignment : default
.data:00403000 ; ===========================================================================
.data:00403000
.data:00403000 ; Segment type: Pure data
.data:00403000 ; Segment permissions: Read/Write
.data:00403000 _data segment para public 'DATA' use32
.data:00403000 assume cs:_data
.data:00403000 ;org 403000h
.data:00403000 ___security_cookie dd 0BB40E64Eh ; DATA XREF: _main+6�r
.data:00403000 ; __security_check_cookie(x)�r ...
.data:00403004 dword_403004 dd 44BF19B1h ; DATA XREF: ___report_gsfailure+B0�r
.data:00403004 ; ___security_init_cookie+2B�w ...
.data:00403008 db 0FFh
.data:00403009 db 0FFh
.data:0040300A db 0FFh
.data:0040300B db 0FFh
.data:0040300C db 0FFh
.data:0040300D db 0FFh
.data:0040300E db 0FFh
.data:0040300F db 0FFh
.data:00403010 dword_403010 dd 0FFFFFFFEh ; DATA XREF: .text:004013E2�r
.data:00403014 dword_403014 dd 1 ; DATA XREF: .text:004013C8�r
.data:00403018 aZyxwvutsrqponm db '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'
.data:00403018 db '=<;:9876543210/.-,+*)(',27h,'&%$#"! ',0
.data:00403078 ; int argc
注意到0x403018起定义了这么一段字符
~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'=<;:9876543210/.-,+*)(',27h,'&%$#"!
到此就可以解题了,直接逆算法遍历目标字符串获得输入
dst="DDCTF{reverseME}"
str="~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>'=<;:9876543210/.-,+*)(',27h,'&%$#\"!"
flag=""
for i in dst:
index=str.find(i)
flag+=chr(index+(0x403018-0x402FF8))
print("flag:"+flag)
获得flag是ZZ[JX#,9(9,+9QY!
最后包裹上DDCTF{}提交即可