您的位置:首页
栈溢出笔记1.6 地址问题(1)
2017-07-31 13:22
169 查看
前面的Shellcode中,我使用的都是自己XP机器上的硬编码地址。不论什么时候在Shellcode中使用硬编码地址都不是个好主意,这一点与动态库的重定位相似,一旦系统环境和程序编译设置发生变化。Shellcode差点儿肯定会失效。因此。我们要找到更好一点的方法。
前面的Shellcode中,我用到了例如以下几个硬编码地址。它们的含义例如以下:
当中。LoadLibraryA的作用比較特殊,我们用它来载入user32.dll库。
如今我们要换掉这些硬编码地址。那么,怎样得到这些API函数的地址呢?在动态链接库中获取函数地址有一个专门的函数——GetProcAddress,这个函数的原型例如以下:
这个函数第一个參数为模块的句柄,能够调用LoadLibraryA获得,第二个參数为函数名称。
这种话。MessageBoxA和ExitProcess都能够使用这种方式来获取。可是这依赖于两个函数:LoadLibraryA和GetProcAddress,那这两个函数的地址又怎么得到呢?
这两个函数都位于kernel32.dll。kernel32.dll肯定会载入。不须要我们自己载入。因此,如今的问题就是怎样从kernel32.dll中找到LoadLibraryA和GetProcAddress的地址?
接下来就须要一点Windows内核和PE文件格式的知识了。我们知道,kernel32.dll为PE格式的动态链接库,要导出的函数放在PE文件的导出表中,因此,为了获取LoadLibraryA和GetProcAddress的地址,我们须要手动解析kernel32.dll的导出表。但在这之前,我们须要知道kernel32.dll在内存中的位置,也就是kernel32.dll的基地址。
因此。问题总结为下面:
(1)怎样获取kernel32.dll的基地址?
(2)怎样在kernel32.dll的导出表中找到LoadLibraryA和GetProcAddress的地址?
先来解决第一个问题。我们知道。一个进程执行的时候。除了载入exe文件外,所依赖的.dll也会映射到进程的虚拟地址空间中。那么。这些载入的dll也是进程的財产。因此,进程会保存它们的信息。进程的信息都保存在PEB结构中。当中的Ldr(偏移为0xc0)指向一个PEB_LDR_DATA结构,这个结构保存的进程已载入模块的信息。
这个结构例如以下:
Windows并未全然公开(文档化)此结构。这是网上的版本号,我们也能够通过Windbg来得到,这是我XP SP3机器上的该结构:
图37
这个结构的重点在于后面三个链表:InLoadOrderModuleList、InMemoryOrderModuleList和InInitializationOrderModuleList。从名称上看,这三个队列都是模块链表,第一个是按载入的先后顺序。第二个是按在虚拟空间中的位置,第三个是按初始化的顺序。
第二个easy理解,可是第一个和第三个有什么差别呢?载入是先于初始化的,载入就是完毕虚拟空间的映射和与exe的链接。载入完毕后的DLL会挂入InInitializationOrderModuleList。进行初始化,初始化就是调用其DLLMain。能够看到的一点是InLoadOrderModuleList中有exe本身的模块,而InInitializationOrderModuleList中仅仅有exe依赖的DLL。(当然。这随着系统版本号在发生变化)
我们要使用的是InInitializationOrderModuleList,挂入该链表中的为LDR_DATA_TABLE_ENTRY。也就是说。每一个载入的模块相应于一个LDR_DATA_TABLE_ENTRY,该结构例如以下:
相同。在我的机器上为:
前面三个链表相应于该结构挂入的三个链表,重点在于第四个成员DllBase,这是载入模块的基地址。因此,我们仅仅须要顺着InInitializationOrderModuleList链表找到kernel32.dll的PLDR_DATA_TABLE_ENTRY。然后通过其DllBase成员。就知道了kernel32.dll载入的地址。
那么InInitializationOrderModuleList链表中哪一个kernel32.dll呢?最保险的方法是解析FullDllName成员,这样代码会比較复杂。实际上在特定版本号的系统中,动态库初始化的顺序是一定的。第一个为ntdll.dll,第二个就是kernel32.dll。Vista 以后第二个是kernelbase.dll。第三个是kernel32.dll。因此,能够避免解析FullDllName成员。
如今。我们要找到进程的PEB结构地址,PEB结构保存于线程的TEB结构中的peb成员,而Windows系统中,寄存器fs总是指向当前线程的TEB。因此。获取kernel32.dll基地址的整个流程例如以下:
图38
写代码之前。先通过调试器顺着该顺序看一看,我用windbg载入的是example_1。输入:
d fs:[0x30]查看当前peb的地址:
图39
地址为0x7ffda000。输入:!peb。验证一下:
图40
是相同的。
输入: d 7ffda000+0x0c,查看PEB_LDR_DATA结构的地址:
图41
输入: d 00251ea0+0x1c,查看InInitializationOrderModuleList链表:
图42
先列一下LIST_ENTRY结构。Flink 指向下一个节点:
因此。0x00251f58是第一个元素。输入:d 0x00251f58查看下面其内容:
基地址为0x7c920000,可是右边显示为kernel32.dll???第一个元素不是ntdll.dll吗?
来看看下一个元素:
基地址为0x7c800000。显示为uer32.dll,看来是出了一些问题,输入!peb来看看:
这里的基地址和名称相应才是对的。
下面来写获取kernel32.dll基地址的代码:
结果例如以下:
图43
这里是一种方法,其他方法(包含Windows 7)请看:
http://blog.harmonysecurity.com/2009_06_01_archive.html
前面的Shellcode中,我用到了例如以下几个硬编码地址。它们的含义例如以下:
当中。LoadLibraryA的作用比較特殊,我们用它来载入user32.dll库。
如今我们要换掉这些硬编码地址。那么,怎样得到这些API函数的地址呢?在动态链接库中获取函数地址有一个专门的函数——GetProcAddress,这个函数的原型例如以下:
/*****************************************************************************/ FARPROC WINAPI GetProcAddress( _In_ HMODULE hModule, _In_ LPCSTR lpProcName ); /*****************************************************************************/
这个函数第一个參数为模块的句柄,能够调用LoadLibraryA获得,第二个參数为函数名称。
这种话。MessageBoxA和ExitProcess都能够使用这种方式来获取。可是这依赖于两个函数:LoadLibraryA和GetProcAddress,那这两个函数的地址又怎么得到呢?
这两个函数都位于kernel32.dll。kernel32.dll肯定会载入。不须要我们自己载入。因此,如今的问题就是怎样从kernel32.dll中找到LoadLibraryA和GetProcAddress的地址?
接下来就须要一点Windows内核和PE文件格式的知识了。我们知道,kernel32.dll为PE格式的动态链接库,要导出的函数放在PE文件的导出表中,因此,为了获取LoadLibraryA和GetProcAddress的地址,我们须要手动解析kernel32.dll的导出表。但在这之前,我们须要知道kernel32.dll在内存中的位置,也就是kernel32.dll的基地址。
因此。问题总结为下面:
(1)怎样获取kernel32.dll的基地址?
(2)怎样在kernel32.dll的导出表中找到LoadLibraryA和GetProcAddress的地址?
先来解决第一个问题。我们知道。一个进程执行的时候。除了载入exe文件外,所依赖的.dll也会映射到进程的虚拟地址空间中。那么。这些载入的dll也是进程的財产。因此,进程会保存它们的信息。进程的信息都保存在PEB结构中。当中的Ldr(偏移为0xc0)指向一个PEB_LDR_DATA结构,这个结构保存的进程已载入模块的信息。
这个结构例如以下:
/*****************************************************************************/ typedef struct _PEB_LDR_DATA { ULONG Length; // +0x00 BOOLEAN Initialized; // +0x04 PVOID SsHandle; // +0x08 LIST_ENTRY InLoadOrderModuleList; // +0x0c LIST_ENTRY InMemoryOrderModuleList; // +0x14 LIST_ENTRY InInitializationOrderModuleList; // +0x1c } PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24 /*****************************************************************************/
Windows并未全然公开(文档化)此结构。这是网上的版本号,我们也能够通过Windbg来得到,这是我XP SP3机器上的该结构:
图37
这个结构的重点在于后面三个链表:InLoadOrderModuleList、InMemoryOrderModuleList和InInitializationOrderModuleList。从名称上看,这三个队列都是模块链表,第一个是按载入的先后顺序。第二个是按在虚拟空间中的位置,第三个是按初始化的顺序。
第二个easy理解,可是第一个和第三个有什么差别呢?载入是先于初始化的,载入就是完毕虚拟空间的映射和与exe的链接。载入完毕后的DLL会挂入InInitializationOrderModuleList。进行初始化,初始化就是调用其DLLMain。能够看到的一点是InLoadOrderModuleList中有exe本身的模块,而InInitializationOrderModuleList中仅仅有exe依赖的DLL。(当然。这随着系统版本号在发生变化)
我们要使用的是InInitializationOrderModuleList,挂入该链表中的为LDR_DATA_TABLE_ENTRY。也就是说。每一个载入的模块相应于一个LDR_DATA_TABLE_ENTRY,该结构例如以下:
/*****************************************************************************/ typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; DWORD SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; DWORD Flags; WORD LoadCount; WORD TlsIndex; LIST_ENTRY HashLinks; PVOID SectionPointer; DWORD CheckSum; DWORD TimeDateStamp; PVOID LoadedImports; PVOID EntryPointActivationContext; PVOID PatchInformation; }LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY; /*****************************************************************************/
相同。在我的机器上为:
前面三个链表相应于该结构挂入的三个链表,重点在于第四个成员DllBase,这是载入模块的基地址。因此,我们仅仅须要顺着InInitializationOrderModuleList链表找到kernel32.dll的PLDR_DATA_TABLE_ENTRY。然后通过其DllBase成员。就知道了kernel32.dll载入的地址。
那么InInitializationOrderModuleList链表中哪一个kernel32.dll呢?最保险的方法是解析FullDllName成员,这样代码会比較复杂。实际上在特定版本号的系统中,动态库初始化的顺序是一定的。第一个为ntdll.dll,第二个就是kernel32.dll。Vista 以后第二个是kernelbase.dll。第三个是kernel32.dll。因此,能够避免解析FullDllName成员。
如今。我们要找到进程的PEB结构地址,PEB结构保存于线程的TEB结构中的peb成员,而Windows系统中,寄存器fs总是指向当前线程的TEB。因此。获取kernel32.dll基地址的整个流程例如以下:
图38
写代码之前。先通过调试器顺着该顺序看一看,我用windbg载入的是example_1。输入:
d fs:[0x30]查看当前peb的地址:
图39
地址为0x7ffda000。输入:!peb。验证一下:
图40
是相同的。
输入: d 7ffda000+0x0c,查看PEB_LDR_DATA结构的地址:
图41
输入: d 00251ea0+0x1c,查看InInitializationOrderModuleList链表:
图42
先列一下LIST_ENTRY结构。Flink 指向下一个节点:
/*****************************************************************************/ typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY; /*****************************************************************************/
因此。0x00251f58是第一个元素。输入:d 0x00251f58查看下面其内容:
基地址为0x7c920000,可是右边显示为kernel32.dll???第一个元素不是ntdll.dll吗?
来看看下一个元素:
基地址为0x7c800000。显示为uer32.dll,看来是出了一些问题,输入!peb来看看:
这里的基地址和名称相应才是对的。
下面来写获取kernel32.dll基地址的代码:
/*****************************************************************************/ // example_8 获取kernel32.dll的基地址 #include <stdio.h> // 获取kernel32.dll的基地址 int get_kernel32_base() { __asm { mov eax, fs:[0x30] // PEB mov eax, [eax+0x0c] // PEB->Ldr mov eax, [eax+0x1c] // PEB->Ldr.InInitializationOrderModuleList.Flink(指向第一个元素) mov eax, [eax] // 指向第二个元素 mov eax, [eax+0x08] // kernel32.dll基地址 } } int main() { printf("0x%x\n", get_kernel32_base()); return 0; } /*****************************************************************************/
结果例如以下:
图43
这里是一种方法,其他方法(包含Windows 7)请看:
http://blog.harmonysecurity.com/2009_06_01_archive.html
相关文章推荐
- 栈溢出笔记1.6 地址问题(1)
- 栈溢出笔记1.6 地址问题(1)
- 栈溢出笔记1.7 地址问题(2)
- 【个人笔记重点,不作为参考】主题:详解基于node的前端项目编译时内存溢出问题
- 学习笔记——Cache访问地址问题
- 学习笔记:万能密码生成器,解决溢出问题
- Servlet学习笔记---重写URl地址(编码问题)
- 计算广告学习笔记1.6 广告的基础知识-计算广告核心问题和挑战
- C语言学习笔记之奇怪的地址问题
- 计算广告学习笔记1.6 广告的基础知识-计算广告核心问题和挑战
- 深入理解Java虚拟机笔记--JVM内存模型及溢出问题总结
- 安卓学习笔记---使用AndroidStudio出现的问题错误(错误: -source 1.6 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算)
- Java Puzzlers笔记--Puzzle 3: Long Division 溢出问题
- 栈溢出笔记1.7 地址问题(2)
- C语言的整型溢出问题(未看)
- PuzzleGame-问题笔记(第一篇+感悟)
- C++学习笔记(8)——继承中的二义性问题和虚基类
- strcpy(d,s)的溢出问题
- [汇编学习笔记][第八章数据处理的两个基本问题]
- 【做个笔记】访问Oracle数据库无监听的问题