题目来源至 https://www.malwaretech.com/beginner-malware-reversing-challenges
所有挑战都是在不使用调试器的情况下完成的,你的目标应该是能够在不运行exe的情况下完成每个挑战。
这些挑战利用与位置无关的代码来解密flag,弄清楚shellcode的作用并自己解密。
shellcode1.exe
包含存储在可执行文件中的flag。运行时,程序将输出flag的MD5哈希值,但不输出原始值,难度等级1星。
本地解压后:
IDA打开,如下:
还是先找一下生成hash的方法,可以确定 call ?digestString@MD5@@QAEPADPAD@Z ; MD5::digestString(char *) 为生成hash的方法。
找这个函数的参数,发现前面存在一个push操作,为 push offset Str ; “2b\n:蹥B*bb”
发现Str为一个字符串的地址,
可以先将字符串当做flag输入进行检测,经过实践后,发现这里的字符串并不是真正的flag。
说明这个地址保存的字符串在前面的流程中进行了改写,我们继续往上查看汇编代码。
call [ebp+Dst]
直接调用了一个地址,首先这个地址应该是一个函数的开始地址。那么如何得到这个地址的?
继续往上看
push 10h
push 0
call ds:GetProcessHeap
push eax
call ds:HeapAlloc
mov [ebp+var_4], eax
mov eax, [ebp+var_4]
mov dword ptr [eax], offset Str ; "2b\n:蹥B*bb"
push offset Str ; "2b\n:蹥B*bb"
call strlen
add esp, 4
mov ecx, [ebp+var_4]
mov [ecx+4], eax
push 40h
push 1000h
push 0Dh
push 0
call ds:VirtualAlloc
mov [ebp+Dst], eax
push 0Dh ; MaxCount
push offset unk_404068 ; Src
mov edx, [ebp+Dst]
push edx ; Dst
call memcpy
add esp, 0Ch
mov esi, [ebp+var_4]
call [ebp+Dst]
首先往上发现了memcpy方法,属于C库函数。
void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字符到存储区 str1。
edx为str1,unk_404068为str2,0Dh为n
这里发现将unk_404068的内容复制到了Dst地址处,恰巧Dst为下面call调用的地址。
push 40h
push 1000h
push 0Dh
push 0
call ds:VirtualAlloc
mov [ebp+Dst], eax
这里使用VirtualAlloc生成了一块虚拟内存,并且设置为可执行,说明[ebp+Dst]处的地址是可执行的,具体可以参考MSDN此函数的用法。
我们跟进unk_404068看看,双击unk_404068进入如下:
目前是以数据显示,猜测这里应该是代码(不然call调用了无法执行)。所以选中unk_404068,Edit 选中Code,生成代码。
可以发现,IDA分析后确实生成了代码。
call [ebp+Dst] 这里已经解决了,接着查找这个函数的输入参数。
往上发现了mov esi, [ebp+var_4],而函数内部也对这个esi寄存器进行了操作,判断这里是寄存器传递参数。
现在的问题变成了(ebp+var_4)地址处的值是什么?
push 10h
push 0
call ds:GetProcessHeap
push eax
call ds:HeapAlloc
mov [ebp+var_4], eax
首先来到第一次对[ebp+var_4]进行操作的地方,这段汇编的意思是调用HeapAlloc生成了一段堆空间,并将起始地址赋值给了
[ebp+var_4]
mov eax, [ebp+var_4]
mov dword ptr [eax], offset Str ; "2b\n:蹥B*bb"
push offset Str ; "2b\n:蹥B*bb"
call strlen
add esp, 4
mov ecx, [ebp+var_4]
mov [ecx+4], eax
接着[ebp+var_4]赋值给了eax,而eax地址处的存储值变成了Str字符串的起始地址。
Str字符串地址入栈,调用了strlen C库函数。将字符串长度eax赋值给了[ecx+4]。
[ecx+4]为[ebp+var_4][1]
现在我们发现
[ebp+var_4][0]=Str[0]
[ebp+var_4][1]=eax=strlen(Str)
进一步,[ebp+var_4]目前的值为Str存储的字符串的地址。
而上面我们发现,esi的值为[ebp+var_4]的值。
所以现在进入 call [ebp+Dst] 函数里进行分析
sub_404068 proc near ; DATA XREF: start+5E↑o
.data:00404068 mov edi, [esi]
.data:0040406A mov ecx, [esi+4]
.data:0040406D
.data:0040406D loc_40406D: ; CODE XREF: sub_404068+A↓j
.data:0040406D rol byte ptr [edi+ecx-1], 5
.data:00404072 loop loc_40406D
.data:00404074 retn
.data:00404074 sub_404068 endp
mov esi, [ebp+var_4]
call [ebp+Dst]
call strlen
add esp, 4
mov ecx, [ebp+var_4]
mov [ecx+4], eax
esi为Str字符串的起始地址,[esi+4]为eax的值,eax的值是strlen的结果,所以是字符串的长度。
rol byte ptr [edi+ecx-1], 5
这句汇编的含义是将Str的每个字符串循环左移5位之后再放回原位,所以最后的值是Str字符串。
之后可计算出flag
FLAG{SHELLCODE-ISNT-JUST-FOR-EXPLOITS}