网络安全 频道

脱壳高级篇Import表的重建

我们需要怎么做

现在,为了让这个可执行文件运行,我们需要重建 FirstThunk 部分的内容,让它们指向我们在输入表第三部分看到的函数名字符串。这不是一项很困难的任务,但是,我们需要知道哪个IAT 对应哪个函数,而函数字符串和 FirstThunk 内容并不采用同样的存储方法。所以,对于每一个 IAT,我们需要验证它对应的是哪个函数名(事实上,根据 IMAGE_IMPORT_DESCRIPTOR.Name DWord 我们已经有了 DLL 名称,这些并没有被改变)。

如何验证每一个函数

正向我们上面所见到的,在内存中,每一个被破坏的 IAT 都有一个函数地址的 RVA。这些地址并没有被破坏,所以,我们只要重新找回指向错误 IAT 的函数地址,把它们指向函数名字符串。

为此,在 Kernel32.dll 中有一个非常有用的 API:GetProcAddress。它允许你得到给定函数的地址。这里是它的描述:

GetProcAddress(



HMODULE hModule, // DLL 模块的句柄

LPCSTR lpProcName // 函数名

);

所以,对于每一个被破坏的 IAT,在 GetProcAddress 返回我们寻找的函数地址之前,只需要分析包含在输入表第三部分的所有函数名。

- hModule 参数是 DLL 模块的句柄(也就是说,模块映象在内存中的基址),我们可以通过 GetModuleHandleA API 得到:

HMODULE GetModuleHandle(

LPCTSTR lpModuleName // 返回模块名地址句柄

);

(lpModuleName 只需要指向我们从 IMAGE_IMPORT_DESCRIPTOR.Name 部分得到的 DLL 文件名字符串)

- lpProcName 仅指向函数名字符串。

注意,有时候函数是按序号输入的。这些序号是在每个 [ 函数名偏移量-2 ] 处的单字(WORDS)。所以,你在分析程序时需要检查函数是按名称还是按序号输入的。

使用上面输入表的实例

针对上面输入表的例子,我将说明如何修复第一个输入 DLL 的第一个输入函数。

1. 我们来看第一个 IMAGE_IMPORT_DESCRIPTOR 结构部分(C1E8h),.Name 部分(C1E4h,指向C1BAh)指出了 DLL 名。我们看到,那是 USER32.dll。

2. 我们来看 .FirstThunk 部分,它们指向 IAT 部分;每个对应一个这个 DLL(user32.dll)的输入函数。在这里是 C1F8h,指向 C238h。所以,在 C238h,我们可以修复被破坏的 IATs。(你会注意到,这个 IAT 部分包含二个 DWords,所以,这个 DLL 有二个函数输入)

3. 我们得到了第一个被破坏的 IAT。它的值是 77E7897Fh。这是函数在内存中的地址。

4. 对每一个输入表第三部分中的函数,我们调用 GetProcAddress API。当该 API 返回 7E7897Fh时就意味着,我们到达了正确的函数。所以我们让被破坏的 IAT 指向正确函数名。(在本例中为 ''wsprintfA'')。

5. 现在我们只需要将 IAT 指向:偏移量(函数名字符串)-2。为什么是 -2 ?因为有时候使用了函数序列。

所以在本例中,我们改变地址 C238h,让它指向 C26Ah(以代替 77E7897Fh)。

6. 就这样,这个函数被修复了,下面你只需要对所有的 IATs 重复这个过程就可以了。

后记

我描述的是一般的操作过程。当然只有在 DLLs 被正常调入内存后才能够这样做。对于其他情况,你需要将它们调入,或者你需要仔细研究它们的输出表才能找到正确的函数地址。

0
相关文章