关于dll逆向的一些实战技术
在CTF比赛中,或许我们并不经常去逆向一个dll,因为它需要我们加载然后运行。
当用IDA调试dll时,会遇到这样的问题。不像exe那样调试比较容易,我们需要写个exe调用dll,再进行动态调试分析。
这道国外的dll逆向,为我们进行dll逆向提供了一个思路。
初步分析
使用IDA打开,发现了许多challenge函数
因为是dll,我们需要分析它导出了哪些函数
发现导出了52个函数,很可疑
进入最后一个导出函数,导出的函数,正是刚才看到的challenge函数
而其他的函数
返回范围 [1-48] 的值
sub_10001000
分析发现是类似解密的一个函数
猜测跟前面的challenge函数调用顺序有关,因为他们的返回值很可疑
使用IDA的signsrch插件
关于安装方法可以自行搜索,这里使用signsrch来搜索相关的加密算法,帮助我们分析
发现是CAST128加密算法
分析导出函数
经过分析发现调用sub_10001000函数有两个,导出序号51和导出序号50的函数
经过对比,发现解密的秘钥和密文不同
现在总结一下:
导出函数51被导出函数1-48所引用,(导出函数1-48修改了byte_10007014秘钥值建立了引用联系)
导出函数50使用了导出函数49,但是导出函数50,并没有被其他函数引用
到这里,我们应该建立了一个思路,猜测走导出函数51这条路可以得到flag
同时,我们应该注意到导出函数1-48,返回了某一范围的值,或许这是暗示调用顺序。
获得dll导出函数调用顺序
使用ida-python
startaddr=0x10001C70
endaddr=0x10002E10
funcs=idautils.Functions(startaddr,endaddr)
for i in funcs:
m = re.match(rptn, idc.get_func_name(i))
if m != None and m.group(1) != "51":
#通过这样方式,得到返回序号
nextordinal = int(idc.print_operand(i+0x7,1).replace("h",""),16) ^ int(idc.print_operand(i+0xe,1).replace("h",""),16)
print(nextordinal)
ret.append(nextordinal)
ret = list(set(ret))
ret.sort()
for i in range(1, 49):
if ret[i-1] != i:
print(i)
break
得到这样的顺序
48
20
1
21
33
25
22
16
14
40
2
38
26
6
13
29
36
47
35
37
42
4
18
3
51
43
7
32
46
5
39
19
24
28
27
9
34
31
10
23
11
12
17
8
15
44
45
30
发现导出函数序号30为base call
现在,我们已经拥有了编写一个exe程序所需的一切,该程序可以按正确的顺序调用导出函数, 将解密的数据dump到文件中以供进一步分析
编写exe调试dll
#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
typedef uint32_t (*EXP)(VOID);
int main(int argc, char **argv)
{
HMODULE hLib;
HANDLE hFile;
EXP exp;
uint32_t plaintxt;
uint32_t nextord;
hLib = LoadLibraryA((LPCSTR)argv[1]);
nextord = 48; // our starting point
// decrypt key
while (nextord != 51)
{
exp = (EXP)GetProcAddress(hLib, MAKEINTRESOURCE(ret));
nextord = exp();
}
exp = (EXP)GetProcAddress(hLib, MAKEINTRESOURCE(51));
// decrypt plaintxt
exp();
// dump plaintext to file
plaintxt = *(uint32_t *)((uint32_t)exp + 0x2e);
hFile = CreateFile("out", GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
WriteFile(hFile, (LPCVOID)plaintxt, 0x1A10, 0, 0);
CloseHandle(hFile);
return 0;
}
输出flag
#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
typedef VOID (*BEEPBEEP)(
DWORD dwFreq,
DWORD dwDuration);
int main(int argc, char **argv)
{
HMODULE hLib;
BEEPBEEP beep;
hLib = LoadLibraryA((LPCSTR)argv[1]);
beep = (BEEPBEEP)GetProcAddress(hLib, MAKEINTRESOURCE(50));
beep(440, 500);
beep(440, 500);
beep(440, 500);
beep(349, 350);
beep(523, 150);
beep(440, 500);
beep(349, 350);
beep(523, 150);
beep(440, 1000);
beep(659, 500);
beep(659, 500);
beep(659, 500);
beep(698, 350);
beep(523, 150);
beep(415, 500);
beep(349, 350);
beep(523, 150);
beep(440, 1000);
return 0;
}
或者运行python
from ctypes import *
mydll = cdll.LoadLibrary("./flareon2016challenge.dll")
i = 30
while True:
i = mydll[i]()
if i == 51:
break
mydll[51]()
mydll[50](0x1B8, 0x1F4)
mydll[50](0x1B8, 0x1F4)
mydll[50](0x1B8, 0x1F4)
mydll[50](0x15D, 0x15E)
mydll[50](0x20B, 0x96)
mydll[50](0x1B8, 0x1F4)
mydll[50](0x15D, 0x15E)
mydll[50](0x20B, 0x96)
mydll[50](0x1B8, 0x3E8)
mydll[50](0x293, 0x1F4)
mydll[50](0x293, 0x1F4)
mydll[50](0x293, 0x1F4)
mydll[50](0x2BA, 0x15E)
mydll[50](0x20B, 0x96)
mydll[50](0x19F, 0x1F4)
mydll[50](0x15D, 0x15E)
mydll[50](0x20B, 0x96)
mydll[50](0x1B8, 0x3E8)
拿到flag:
0 条评论
可输入 255 字