初学者的静态分析挑战writeup5

题目来源至 https://www.malwaretech.com/beginner-malware-reversing-challenges

所有挑战都是在不使用调试器的情况下完成的,你的目标应该是能够在不运行exe的情况下完成每个挑战。

shellcode2.exe

包含存储在可执行文件中的flag。运行时,程序将输出flag的MD5哈希值,但不输出原始值。

按照惯例,使用IDA打开。
初学者的静态分析挑战writeup5
发现了涉及hash的函数,接着找输入参数。

push edx ; char * 这里是通过edx传入参数

lea edx, [ebp+str[0]] ;edx保存了一个字符数组的首地址。

因为之前已做过了,这里已经手工对连续排列的参数进行创建数组的处理。

猜测数组保存的应该就是输入的flag,查看上述流程里对该数组做了哪些操作。

call [ebp+Dst]

依据前面的经验,这里Dst为堆空间地址,之后通过mencpy复制了内容,所以所在的地址为代码,所以现在找到这个地址查看下,

也就是push offset sub_404040 ; Src 中的sub_404040。

双击进入,如果发现是db … 等显示,可以按字母c转换为代码格式。
初学者的静态分析挑战writeup5
这里已经转换为代码格式,按F5让IDA自动生成伪代码,看看这个函数的作用。
初学者的静态分析挑战writeup5
翻到下面,看看具体逻辑。
初学者的静态分析挑战writeup5
这里已经重命名了很多代码,当然主要的知识点就是 LoadLibraryA与GetProcAddress。

通过这个两个API函数,可以调用系统dll里具体的方法。

v78 = LoadLibraryA(&msvcrt.dll);
v27 = LoadLibraryA(&kernel32.dll);

_GetModuleFileNameA = (void (__stdcall *)(_DWORD, char *, signed int))GetProcAddress(v27, &GetModuleFileNameA);
_fopen = (int (__cdecl *)(char *, char *))GetProcAddress(v78, &fopen);
_fseek = (void (__cdecl *)(int, signed int, _DWORD))GetProcAddress(v78, &fseek);
_fread = (void (__cdecl *)(char *, signed int, signed int, int))GetProcAddress(v78, &fread);
_fclose = (int (__cdecl *)(int))GetProcAddress(v78, &fclose);

这里加载了两个dll,之后获取了5个函数的内存实际地址。

_GetModuleFileNameA(0, &v11, 260); // v11=当前exe执行的绝对路径
ptr_file = _fopen(&v11, &rb);
_fseek(ptr_file, 78, 0); // 移动文件指针至离文件开始位置78个字节处
_fread(v79, 38, 1, ptr_file); // 读取38个字节放入v79数组中
// This program cannot be run in DOS mode
result = _fclose(ptr_file);
v2_36 = *(_DWORD *)(heap_str + 12); // 36(十进制)
v3_str = *(_DWORD *)(heap_str + 8); // str字符串的地址

获取到当前exe执行程序的绝对路径,之后设置文件指针,移动到离文件开始位置78个字节处,接着读取38个字节的内容
放入v79数组中。实际读取的内容为“This program cannot be run in DOS mode”。
初学者的静态分析挑战writeup5
之后关闭文件,取出之前申请的堆空间里的两个数值进行赋值。

v4_0 = 0;
do
{
LOBYTE(result) = v79[v4_0];
*(_BYTE *)(v3_str + v4_0++) ^= result;
}
while ( v4_0 != v2_36 );
return result;

这里就是最后的计算逻辑,result等于v79数组的首地址,也就是之前“This program cannot be run in DOS mode”的内容,

v3_str为[ebp+str[0]至[ebp+str[0]+23h]的数组内容,两个数组之间每个索引值进行异或操作赋值给v3_str(具体要看实际汇编指令,翻译成的伪代码不一定正确),循环36次后结束。

text:0040227F call ??0MD5@@QAE@XZ ; MD5::MD5(void)
.text:00402284 mov [ebp+str[0]], 12h
.text:00402288 mov [ebp+str[0]+1], 24h
.text:0040228C mov [ebp+str[0]+2], 28h
.text:00402290 mov [ebp+str[0]+3], 34h
.text:00402294 mov [ebp+str[0]+4], 5Bh
.text:00402298 mov [ebp+str[0]+5], 23h
.text:0040229C mov [ebp+str[0]+6], 26h
.text:004022A0 mov [ebp+str[0]+7], 20h
.text:004022A4 mov [ebp+str[0]+8], 35h
.text:004022A8 mov [ebp+str[0]+9], 37h
.text:004022AC mov [ebp+str[0]+0Ah], 4Ch
.text:004022B0 mov [ebp+str[0]+0Bh], 28h
.text:004022B4 mov [ebp+str[0]+0Ch], 76h
.text:004022B8 mov [ebp+str[0]+0Dh], 26h
.text:004022BC mov [ebp+str[0]+0Eh], 33h
.text:004022C0 mov [ebp+str[0]+0Fh], 37h
.text:004022C4 mov [ebp+str[0]+10h], 3Ah
.text:004022C8 mov [ebp+str[0]+11h], 27h
.text:004022CC mov [ebp+str[0]+12h], 3Dh
.text:004022D0 mov [ebp+str[0]+13h], 6Eh
.text:004022D4 mov [ebp+str[0]+14h], 25h
.text:004022D8 mov [ebp+str[0]+15h], 48h
.text:004022DC mov [ebp+str[0]+16h], 6Fh
.text:004022E0 mov [ebp+str[0]+17h], 3Ch
.text:004022E4 mov [ebp+str[0]+18h], 58h
.text:004022E8 mov [ebp+str[0]+19h], 3Ah
.text:004022EC mov [ebp+str[0]+1Ah], 68h
.text:004022F0 mov [ebp+str[0]+1Bh], 2Ch
.text:004022F4 mov [ebp+str[0]+1Ch], 43h
.text:004022F8 mov [ebp+str[0]+1Dh], 73h
.text:004022FC mov [ebp+str[0]+1Eh], 10h
.text:00402300 mov [ebp+str[0]+1Fh], 0Eh
.text:00402304 mov [ebp+str[0]+20h], 10h
.text:00402308 mov [ebp+str[0]+21h], 6Bh
.text:0040230C mov [ebp+str[0]+22h], 10h
.text:00402310 mov [ebp+str[0]+23h], 6Fh

算出flag为FLAG{STORE-EVERYTHING-ON-THE-STACK}

上一篇:栈帧ebp,esp详解


下一篇:针对JCC指令练习的堆栈图