知识是一宝库,而实践就是开启宝库大门的钥匙。
——Fuller
如果说要讲dump的原理,那么我们最简单的办法就是自己写一个象LordPE那样的dump程序。下面我就来详细说明一下,一个dump程序是如何工作的,如何把内存中的数据保存到文件中的。
一.分析
对于dump来说,他的英文翻译就是“转存”。也就是说把内存中或者其他的输入转存到另一个位置,当然对于我们现在说的dump就是把内存中运行的PE进程的数据,从内存中抓取出来,然后在用文件的形式保存下来。
根据上面的分析我们基本上得到了一个这样的思维。Dump程序要做的事分几个基本的步骤:
1. 在系统中找到目标进程
2. 在进程中确定进程的大小imagesize
3. 把进程中的数据保存到文件
二.实现
好了,现在我们就可以具体来实现各个部分的功能了。
1. 在系统中找到目标进程就是实现一个基本的任务管理器的把进程列出的功能。
在这个实现中我们采用了BOOL GetProcessListFunc(HWND hDlg,HWND hWindList);这个函数里面调用了几个基本的函数:CreateToolhelp32Snapshot,Process32First, Process32Next
这个列出进程的功能就轻松的实现了。(具体的请看我的代码Lenus_dump_1.cpp)
2. 在进程中确定要dump的大小是一件很有意思的事情,我打算慢慢的讲解寻找这数据的办法,这一节我们先用一个基本的方法。
我们先用一个笨一点的办法,根据PE文件格式可以知道在距离PE文件头(IMAGE_NT_SIGNATURE)0x50的位置是存放imageofsize的地方。所以在int GetSizeOfImage(HWND hDlg,HANDLE hProcess);这个函数中,我们找到了进程以后打开目标进程,然后用ReadProcessMemory来读取imageofsize就可以了。
在这里我还用BOOL CheckPEFunc(HWND hDlg,HANDLE hProcess);函数简单的检查了了两个PE文件的标志(IMAGE_DOS_SIGNATURE和IMAGE_NT_SIGNATURE)
3. 在把进程中的数据保存到硬盘的文件的步骤中,我使用了一个比较笨的办法。
首先,我们用LPCTSTR SaveAsFunc(HWND hDlg);函数把打开一个save as的通用控件,获得要保存的文件名,默认的是dumped.exe。接着,使用前面在获得imageofsize的同时已经用GlobalAlloc函数申请了同样大小的堆空间,把目标进程的数据读到这个空间里面。这里面就有一个空间大小的问题,如果进程过大,我怀疑会申请失败。在这里还有尤其的注意一个地方,就是我们在申请空间的时候用了sizeoffile而不是sizeofimage,这是因为,有的时候sizeofimage的大小并不是一个文件对齐度的大小,而为了让他对齐,我使用下面的算法。
代码:
if(!(sizeofimage%0x1000)) //如果是文件对齐度的整数倍的时候就不处理
sizeoffile=sizeofimage;
else
sizeoffile=(sizeofimage/0x1000+1)*0x1000; //如果不是就增加一个文件对齐度
这样就能保证,imageofsize转化为一个具有文件对齐度的数值。
最后,是使用BOOL CreateDumpFile(HWND hDlg,LPCTSTR Dump_Name,HGLOBAL hMem);这个函数,这个函数的作用是在磁盘中申请一个文件,把从刚刚读到内存空间的数据又全部的写到磁盘上,所以就用了两个函数CreateFile和WriteFile。
三.试验
写好了程序就如下图一样
下面用他来实战使用一下吧
运行用upx压缩过的win98的notepad
让我把他在oep处dump下来
嘿嘿,dump下来才发现,图表都没有了。呵呵,这是因为我们没有把相对虚拟地址(RVA)和文件地址对齐(RA)。
科普一下:在文件中要节约空间,把数据紧密的存储在一切,而靠节表在load到内存中的时候,把在文件中的不同数据分别隐射到不空内存空间中,而不足的地方就用0填充。但是当我把这些数据从内存中完完整整的dump下来的时候,我们同样把这些0也dump了出来。所以我们要做的首先应该是调整节表,让他的RA=RVA。这样我们实际上做的就是把RA这个指针移动过了若干个0的空间,从而指向正确的数据。
下面我们还是用LoadPE打开它,把RA和SIZE修改过来吧!
这么样,回到桌面刷新一下就会发现图标又回来了吧!
好了,现在运行dumped.exe文件,还是不行的。对了,IAT还没有修复呢,我们停在OEP的时候文件的IAT已经全部释放了出来,而IAT只是对于本计算机的函数地址,每一个计算机都可能不一样。这样我们就要用ImportRECl来重新根据这个IAT地址反向构造一个输入表。OK,使用ImportREC修复了以后就可以运行了,不过dump出来的文件大出了好多。我们可以知道原因了。
1. 因为文件对齐的原因我们把残留的0也dump出来了。
2. 因为使用ImportREC他帮我们重新创建了一个区段存放输入表
所以,我们使用LordPE的ReBuiltPe其中的一个功能就是可以把这些残留的0,自动的删除,然后修改节表让他们对齐。
四.小结
经过了一番的讲解,大家应该知道,LordPE的dump功能大概是怎么运作的了吧,当然从我们的程序从还有很多的不足,从上面例子也能知道了。LordPE在dump下程序的同时,直接将节表中的RA=RVA ,RS=RVS。那么在以后我们将继续改进我们的dump程序,让它增加这些功能。万事开头难,如果你有足够的耐心读完了这一章那么下面的章节就很简单了,不要怕继续吧!