win32和MFC逆向总结

win32和MFC的程序逆向已经是以前的东西,这次翻出来,进行一下总结,理论性的东西也快忘了,hhhhh,熟悉下也好。
前提:程序既没有加壳,也没有混淆,就是直接使用(正常)编译器编译好的程序。

win32程序

先说一下win32程序逆向,win32程序分为两种:1.CUI控制台类型 2.GUI窗口类型。

CUI控制台类型

这类的题目,程序的关键代码一般都比较好定位,由于ida打开就是main函数,只要找到输入输出,确定程序流,一步一步逆向下就好。常用手段:1.根据程序的输入和输出的字符串,使用字符串进行定位,ida的alt + A可以切换搜索字符串类型,比如ascii和unicode。 2.在导入表里寻找类似scanf和printf的输入输出函数。

GUI窗口类型

GUI类型的题目,比CUI控制台定位要复杂些。手段:1.同CUI程序,搜索字符串 2.定位导入表的函数,如果有弹窗,定位MessageBox;如果有文本框,定位GetDlgItem,GetDlgItemText,GetWindowText等函数; 3.当然了,前面的方法都只能治标不治本,最重要的就是对WM_COMMAND消息下断,WM_COMMAND = 0x111,即273,ida使用alt + I搜索0x111。因为win32的窗口程序是自己自定义的,窗口函数也是由自己来写的,所有的按钮点击等操作的msg = WM_COMMAND,后面的参数诸如wParam保存的是控件的ID。下面是win32程序的一个大概框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR CmdLine,int nCmdShow)
{
WNDCLASSEX wc = { };
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.lpszClassName = lps_cl;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&wc);
hwnd=CreateWindowEx(WS_EX_WINDOWEDGE,
lps_cl,
wd_text,
WS_OVERLAPPEDWINDOW,
700,
500,
500,
100,
NULL,
NULL,
hInstance,
NULL);
if(hwnd == NULL) return -1;

ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
hg_app = hInstance;
//上面的示例代码是窗口的创建

MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
//上面是win32消息处理的三个必须且固定的函数,消息Dispatch给对应的窗口处理函数(回调函数)
}
/**************窗口回调函数***********/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
{
edit_input=CreateWindow(L"edit",NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
35, 20, 350, 25, hwnd, NULL, NULL, NULL);
check_Button=CreateWindow(L"Button", L"check", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
400, 20, 50, 25, hwnd, (HMENU)IDB_ONE, hg_app, NULL);
}
return 0;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDB_ONE:
hThread=(HANDLE)_beginthreadex(NULL,0,&FirstThreadFunc,NULL,0,&threadID1);
break;
case IDB_TWO:
MessageBox(hwnd,L"You are failed!",L"FAIL",MB_OK | MB_ICONINFORMATION);
exit_fun();
break;
case IDB_THREE:
MessageBox(hwnd,L"Only a little!",L"FAIL",MB_OK | MB_ICONINFORMATION);
exit_fun();
break;
case IDB_FOUR:
MessageBox(hwnd,L"Just a little bit!",L"FAIL",MB_OK | MB_ICONINFORMATION);
exit_fun();
case IDB_FIVE:
MessageBox(hwnd,L"Succeed!",L"Congratulation!",MB_OK | MB_ICONINFORMATION);
default:
break;
}
}
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}

有兴趣进一步学习的同学可以看看windows系统的消息机制相关内容,参考书籍《windows核心编程》《深入浅出MFC》。

MFC程序

这几个链接我觉得讲MFC程序逆向讲的很好了,原理什么的也讲的很清楚。
使用IDA定位基于MFC的CrackMe的按钮函数—–理论篇
使用IDA定位基于MFC的CrackMe的按钮函数—–实践篇(一)
使用IDA定位基于MFC的CrackMe的按钮函数—–实践篇(二)
我总结的大概也不会比这个好了,233333。在win32中窗口过程标准的消息处理变成了找消息映射表,对,就是这个消息映射表,我们来看一下它的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct AFX_MSGMAP  
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT_PTR nSig;
AFX_PMSG pfn;
};

实际上,MFC程序的每个按钮怎么处理,都有一个函数控制,写过MFC程序的应该都知道,这个处理函数就是AFX_PMSG pfn,指向对应消息,对应控件ID的消息处理函数,而一个MFC程序有很多这样的消息处理函数,我们只要找到AFX_MSGMAP消息映射表,然后定位lpEntries指针所指位置,这个位置一般就在指针下面,hhhhh,紧接着就是数个AFX_MSGMAP_ENTRY结构,我们只要根据控件的ID,找到相应的消息处理函数,进入就是关键代码了。
说了这么多,还不如耐心看上面的三个链接,看了就懂了,大概就是这么个道理。因此,我一般定位MFC程序,先使用Resource_hacker打开程序,查看相应控件的ID,在ida里alt + I,输入ID值,查看所有出现的位置,可能会有很多地方,千万别被吓到了,那个关键位置其实一眼就看出来啦,找到.rdata段的地方,自己试试就知道了。定位关键代码也就完成了。
当然了,你也可以选择使用工具来定位,xspy,相信不少伙伴都在用这个工具,我习惯使用资源查找的方法了,所以就没用了。

总结

我知道的大概就那么多,许多地方可能含糊不清,仅仅是一个总结,有兴趣的朋友可以查阅相关资料进行学习,我的总结只是起个抛砖引玉的作用罢了!

-