通过 PEB 隐藏导入表

`

有时候分析样本查看不到其中的导入表,可是没有导入表如何调用系统函数呢?这有可能是通过TEB、PEB动态获取,达到隐藏的目的。主要原理是通过FS:[0x30]所指向的PEB结构体入手,得到ntdll.dll或kernel32.dll的基址。

如下新建一个最简单的控制台程序,release编译。
通过 PEB 隐藏导入表_第1张图片

可以看到VC.dll、KERNEL32.dll和其他一些系统函数,其中VC和系统函数可以去掉。
通过 PEB 隐藏导入表_第2张图片

在编译时候将项目属性设置如下,表示将运行库代码静态编译进程序中,然后重新编译再次打开可以看到只剩下 KERNEL32.dll
通过 PEB 隐藏导入表_第3张图片通过 PEB 隐藏导入表_第4张图片

然而程序是一个C标准程序,运行时肯定会用到C函数库,C函数库底层还是会调用系统API,所以最终还是得导入KERNEL32.dll。解决方法就是不使用C函数库,也就是代码中不要include 任何C语言的库,同时自定义程序入口,再次编译后就不会导入任何DLL。新建一个空白工程进行测试,如下只有函数名,没有参数、返回值、函数体。
通过 PEB 隐藏导入表_第5张图片
项目属性设置如下。
通过 PEB 隐藏导入表_第6张图片
通过 PEB 隐藏导入表_第7张图片
现在再来看导入表已经没了。
通过 PEB 隐藏导入表_第8张图片

可是现在如何去调用实现功能的API呢
现在连scanf、printf也用不了了,总不可能所有功能自己从无到有去实现。

想要加载DLL,一般就是用到KERNEL32.dll的LoadLibrary函数,该函数内部调用了LoadLibraryEx,LoadLibraryEx内部会调用LdrLoadDll函数(这个函数在ntdll.dll中导出),最终LdrLoadDll会调用LdrpLoadDll函数来加载DLL。

我们知道每个程序的地址空间中默认会有KERNEL32.dll和NTDLL.dll,那么就可以选择其中一个来实现要加载DLL的目标,比如这里用NTDLL.dll来实现,KERNEL32.dll的好处是不用考虑UNICODE字符编码的问题。

如何得到LdrLoadDll函数的地址呢?可以先得到NTDLL.dll的地址,然后通过解析他的PE结构中的导出表来获取LdrLoadDll的地址。如何得到NTDLL.dll的地址呢?这里就要用到PEB了,可以通过解析PEB(进程环境块,是个结构体)来获取到NTDLL.dll的地址,PEB就在FS:[0x30]处,很熟悉吧。

下面将一个EXE文件拖入OllyDbg,看到FS段寄存器的值为0x3B ,二进制为 0000 0000 0011 1011 ,低两位11表示3,那么RPL就是3,第三位是0说明表指示器就是GDT,四五六位是111,十进制为7,所以就是说从GDT这个表中读取第7位。可用PC Hunter–内核–GDT来查看。
通过 PEB 隐藏导入表_第9张图片

可以看到0x7的基址为 0x7FFDF000,而OD中FS后面对应为 0X7FFDE000,这说明了FS在内核层和用户层指向了不同的内存空间,现在只关心用户层,就是OD显示的 0x7FFDE000。
通过 PEB 隐藏导入表_第10张图片
回到OD,在数据窗口跟随0x7FFDE000,那么这里其实就是TEB结构体的地址(线程环境块)。
通过 PEB 隐藏导入表_第11张图片
TEB结构体本身很大,主要的成员如下,0x00处是TIB结构体,0x30偏移处就是指向PEB结构体。
通过 PEB 隐藏导入表_第12张图片
第一个成员 NT_TIB 结构体,其成员如下。可以看到NT_TIB第一项就指向了SEH链表入口(与OD对应),最后一项指向了自身。NT_TIB就是TIB(线程信息块)。
通过 PEB 隐藏导入表_第13张图片
回到OD往下看可以看到TIB结构、指向PEB等信息。
通过 PEB 隐藏导入表_第14张图片
TEB、TIB、PEB三者关系如下:
通过 PEB 隐藏导入表_第15张图片

至此下面可以使用Windbg来跟随查看一个个的结构体,从TEB结构体开始,一步步找到NTDLL.dll或者KERNEL32.dll的地址。首先在windbg中附加一个EXE。

通过 PEB 隐藏导入表_第16张图片

如果没加载pdb符号信息,需要手动设置一下符号下载服务器:
查看当前符号路径:.sympath
设置符号路径:.symfix d:\symbols
重新加载符号: .reload

设置完毕输入命令!teb查看TEB结构:可知TEB结构体的地址为7ffde000,即FS寄存器中的值为7ffde000(OD中可查看)。
通过 PEB 隐藏导入表_第17张图片
输入dt _TEB可以查看TEB的具体结构,可以看到0x30处指向了PEB结构
通过 PEB 隐藏导入表_第18张图片
在这里插入图片描述
可以继续查看PEB的结构,能够看到偏移c处为Ldr,该成员为_PEB_LDR_DATA结构体
通过 PEB 隐藏导入表_第19张图片
那么用dt _PEB_LDR_DATA命令查看_PEB_LDR_DATA结构体的结构,偏移1C指向哪里呢,可以看到指向了InInitializationOrderModuleList,这个成员是个_LIST_ENTRY结构体。
在这里插入图片描述
通过 PEB 隐藏导入表_第20张图片
通过图看下PEB_LDR_DATA的成员,通俗来说就是有三个主要的LIST_ENTRY:门。上面指向了第三个门也就是下图中的0x1c处的InInitializationOrderModuleList。
通过 PEB 隐藏导入表_第21张图片
那么接下来的两次连续取0也就是
在这里插入图片描述
通过 PEB 隐藏导入表_第22张图片
用windbg查看:
在这里插入图片描述
最后取到0x8
通过 PEB 隐藏导入表_第23张图片
那么这样也就知道了这个壳确实是通过查找TEB结构来查找所有的导入函数的(参考编写遍历导入表时候的思路)
在这里插入图片描述
获取NTDLL.dll和KERNEL32.dll原理相同,假设这里获取到了NTDLL.dll的地址。接下来如何获取LdrLoadDll函数的地址呢。此时可以解析NTDLL.dll的PE结构,得到他的导出表,获得LdrLoadDll、GetProcAddress的地址。

参考:
https://www.52pojie.cn/forum.php?mod=viewthread&tid=455554(PEB的简单应用-隐藏导入表)
https://weiyiling.cn/one/packer_import_hide(记两次相似的脱壳过程

https://blog.csdn.net/misterliwei/article/details/4391580/(Windows中FS段寄存器 V2)
https://blog.csdn.net/zcc1414/article/details/9746747(PEB 和 TIB结构)

你可能感兴趣的:(Windows编程,Windows原理)