• 标 题:MFC 程式的跟踪 (一) (6千字)
  • 作 者:WinDos2K
  • 时 间:2001-5-1 13:19:23
  • 链 接:http://bbs.pediy.com

MFC  程式的跟踪 (一)

工具(选用最新版本)
1    Win32Dasm
2    IDA Pro
3    HexEditor
4    SICE

目标
http://soft.269.net/SoftWareView.asp?SoftWareID=21721
这东西如何用还未知道,只知道是做图表的,但一些工能被停用了,会跳出Demo版无此工能的对活框

目的
去除Demo对活框,恢复原有工能

小知识
MFC程式有两种,一是所谓静态库(static library),另一种是分享动态连接库(shared dll)
第一种程式比较完整,所有工能码都附在程式内,另一种工能码跟别人分享,所以
会有很多转跳,上面程式应是第二种,而分享动态连接库是MFC42.DLL,又MS把这连接库
的名字都用序号,Ordinal:09CE之类,令人混淆不清.

正题
打开程式,隋意做些图表,打开File选单,可看到存档(Save/Save AS子选单没有丧失工能),按Save As一下,Demo 指示跳出.那麽把Demo框去除,跳回Save As子程式,那不是很容易吗?正是!整个过程就是做这个事情.但到底什样做?
Demo框是目标之一,就拿它下手,用W32Dasm把主程式反汇一下(这是我一贯做法),分析後发现这Demo框字串出现有二十三处之多,也弄不清是那一个对应Save As选单,就把每个对应地址记下,打开ICE  Loader加载主程式,当停在程式入口处的时候,把记下的地址一一 bpx XXXXXXXX 加入断点,F5运行程式,再选Save AS, 程式中断在4C2940,返回W32Dasm,逆流而上,看看源头是什么样,结果令人失望.
:0040187A E941A61100              jmp 0051BEC0
:0040187F E94CE91700              jmp 005801D0
:00401884 E977D20400              jmp 0044EB00 ß--其中一起始点

这起始点就是Save As子程式的入口点,很明显是没有被任何直接呼叫.

小知识
在x86汇编的定址模式中,有直接定址和间接定址, 直接定址易明不用多说了, 间接定址也经常在PE程式中出现如 Call dword ptr [edx+000000A0],这种定址模式反汇编是无法反的.在这例子中,edx是这表的最底部,而00A0是偏移量,注意这模式,後面会用到.

静态分析碰钉,改动态分析, 打开ICE  Loader加载主程式,当停在程式入口处的时候,下bpx 4C2940,当经过一轮鼠工之後,程式断在4C2940处,

:004C2940 6AFF                    push FFFFFFFF
:004C2942 6A00                    push 00000000

* Possible Reference to String Resource ID=00144: "This function is not available in the demo."
                                  |
:004C2944 6890000000              push 00000090

* Reference To: MFC42.Ordinal:04AF, Ord:04AFh
                                  |
:004C2949 E874981200        Call 005EC1C2 ß- MessageBox ???
:004C294E 33C0                    xor eax, eax
:004C2950 C20800                ret 0008

这时在4C2950处又下一断点,F5运行程式,Demo框字串出现,按OK,又中断在4C2950处,这时按一下F8,程式转到下面6C3D0A64的MFC42.DLL里

Exported fn(): Ordinal:1209 - Ord:1209h
:6C3D0A58     8B01                      mov eax, dword ptr [ecx]
:6C3D0A5A     6A01                      push 00000001
:6C3D0A5C     6A00                    push 00000000
:6C3D0A5E     FF90A0000000  call dword ptr [eax+000000A0]
:6C3D0A64     C3                        ret

好了,那就是说6C3D0A5E这个Call产生Demo框了,跟踪一下这个Call [eax+000000A0]=Call [62D5BC+A0]àCall [62D65C]==Call 402F7C.

:00402F7C E9BFF90B00              jmp 004C2940

从MFC42.DLL反回主程式,又碰钉了,但是奇怪的为什么要从MFC42.DLL反回主程式?看看
这62D65C(RVA)== 22D65C(File)的位置


0022D640  3B 9D 40 00 10 C8 5E     00  0A C8 5E 00 04 C8 5E 00 
0022D650  57 72 40 00 E2 CB 5E      00  F2 C7 5E 00 7C 2F 40 00 
0022D660  E6 C7 5E 00 E0 C7 5E       00  DC CB 5E 00 D6 CB 5E 00 

就是地址列表(Relocation table),但都是从005EXXXX 开始,只有00402F7C有点奇怪,看看
005EC804 是什么样


* Reference To: MFC42.Ordinal:15C9, Ord:15C9h
                                  |
:005EC804 FF25FC736800            Jmp dword ptr [006873FC]

又看看006873FC是什么样

:00620FF6 00000000000000000000    BYTE 10 DUP(0)

oops W32Dasm 最多到00621000 ,没有选择,用IDA Pro 反一下


06873E8 ; public: virtual int __thiscall CDocument::DoFileSave(void)
006873E8                extrn ?DoFileSave@CDocument@@UAEHXZ:dword
006873E8                ; DATA XREF:CDocument::DoFileSave(void) r
006873EC ; public: virtual int __thiscall CDocument::DoSave(char const *,int)
006873EC                extrn ?DoSave@CDocument@@UAEHPBDH@Z:dword
006873EC                ; DATA XREF: CDocument::DoSave(char const *,int) r
006873F0 ; public: virtual void __thiscall COleDocument::PreCloseFrame(class CFrameWnd *)
006873F0                extrn ?PreCloseFrame@COleDocument@@UAEXPAVCFrameWnd@@@Z:dword
006873F0                ; DATA XREF: COleDocument::PreCloseFrame(CFrameWnd *) r
006873F4 ; public: virtual int __thiscall COleDocument::SaveModified(void)
006873F4                extrn ?SaveModified@COleDocument@@UAEHXZ:dword
006873F4                ; DATA XREF: COleDocument::SaveModified(void) r
006873F8 ; public: virtual int __thiscall CDocument::CanCloseFrame(class CFrameWnd *)
006873F8                extrn ?CanCloseFrame@CDocument@@UAEHPAVCFrameWnd@@@Z:dword
006873F8                ; DATA XREF: CDocument::CanCloseFrame(CFrameWnd *) r
006873FC ; public: virtual void __thiscall CDocument::ReleaseFile(class CFile *,int)
006873FC                extrn ?ReleaseFile@CDocument@@UAEXPAVCFile@@H@Z:dword



看到这些字是不是很有意思,DoFileSave,DoSave等等,都是跟保存文件有密切关系。

小知识
在「深入浅出 MFC」,Dissecting MFC  第四部分 第八章. Document View 深入探讨 里,对於程式的读写都讲解得很详细,这是MFC四大天王其中之一是本很好的书. http://www.csdn.net/expert/jjhou/


既然MFC42.DLL里别有洞天,我们便把这DLL反一下, 详细的看一看.

Exported fn(): Ordinal:1208 - Ord:1208h
:6C3D0A50 8B01                    mov eax, dword ptr [ecx]
:6C3D0A52 FFA0A4000000            jmp dword ptr [eax+000000A4]

Exported fn(): Ordinal:1209 - Ord:1209h
:6C3D0A58 8B01                    mov eax, dword ptr [ecx]
:6C3D0A5A 6A01                    push 00000001
:6C3D0A5C 6A00                    push 00000000
:6C3D0A5E FF90A0000000            call dword ptr [eax+000000A0]
:6C3D0A64 C3                      ret

在上面Ord:1208h及Ord:1209h便是MFC OnFileSave( )及OnFileSaveAs( )的入口点,最後会转到DoSave( ) Ord:09EEh上,详细情况可看 深入浅出 MFC一书.

Exported fn(): Ordinal:09EE - Ord:09EEh
:6C3D0AA0 B86F3F406C              mov eax, 6C403F6F

* Reference To: MSVCRT._EH_prolog, Ord:0042h
                                  |
:6C3D0AA5 E8D305FAFF              Call 6C37107D

你会问为什么我会知道这些入口点的名称,答案==IDA Pro+学作+练习

现在程式的来龙去脉都清楚了,只要把call [eax+000000A0]从显现Demo框改到DoSave( )上便可存档,什么样改就不用说了,最後打印是用OnFilePrint( ).

结语
主要修改为下
存档    在22D65C改402F7C为5EC7EC
打印    在22E6C4改40A961为5ECC8A
只要把MFC42.DLL跟主程式作一体化,MFC程式还是可跟踪的.
还有其它工能有机会再写.