【逆向】ShellCode分析基础

1、shellcode经常与漏洞利用一起使用,或者被病毒等恶意代码用于进程注入。所以它们总是在一个程序中被植入运行。
2、shellcode是一段与位置无关的代码。因为它在内存中的位置是随机不可控的,所以在编写shellcode时应该避开对内存地址进行硬编码。
3、shellcode中包含数据和代码。所以在以位置无关的方式访问数据时,需要有一个基础地址(基址)加上或减去偏移的方式来访问。shellcode通常使用当前指令指针(EIP)作为基础地址来使用。为了在代码执行时获取EIP的值,可以根据call/pop指令执行原理来变相获取EIP值
4、当一个call指令执行时,处理器会将下一条指令地址(函数返回地址)保存到栈上,shellcode可以在一个call指令后面立刻执行pop指令,从栈上取出指令地址作为基础地址来使用。通过基础地址就可以对代码中的数据进行自由访问了。

5、shellcode因为不能硬编码内存地址,所以它在通过API与系统进行交互时,必须自己动态加载获取需要的模块和API地址。为了完成这个任务,shellcode经常使用LoadLibraryA和GetProcess函数。如果shellcode有这两个函数的访问权限,它就可以加载任意模块到系统中并获取导出函数地址。这两个函数都是从Kernel32.dll中导出的,所以shellcode必须在内存中找到Kernel32.dll,并解析Kernel32.dll的PE文件,搜索并获取以上函数的地址。
6、想要在内存中找到Kernel32.dll基址我们需要用到下图中的一些数据结构。

 1 typedef struct _PEB_LDR_DATA
 2 {
 3  ULONG Length;                                 // +0x00
 4  BOOLEAN Initialized;                          // +0x04
 5  PVOID SsHandle;                               // +0x08
 6  LIST_ENTRY InLoadOrderModuleList;             // +0x0c 模块加载顺序
 7  LIST_ENTRY InMemoryOrderModuleList;           // +0x14 模块在内存中的顺序
 8  LIST_ENTRY InInitializationOrderModuleList;   // +0x1c 模块初始化时的顺序
 9 } PEB_LDR_DATA,*PPEB_LDR_DATA;
10 // 该结构体包含了三个双向链表(_LIST_ENTRY),它们分别指向了_LDR_DATA_TABLE_ENTRY结构体。
1 typedef struct _LIST_ENTRY 
2 {
3    struct _LIST_ENTRY *Flink;                    // +0x00
4    struct _LIST_ENTRY *Blink;                    // +0x04
5 } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
6 // 这个双向链表指向了进程中加载的模块,结构中的每个指针,都指向了一个LDR_DATA_TABLE_ENTRY结构体。
 1 typedef struct _LDR_DATA_TABLE_ENTRY
 2 {
 3     LIST_ENTRY InLoadOrderLinks;
 4     LIST_ENTRY InMemoryOrderLinks;
 5     LIST_ENTRY InInitializationOrderLinks;
 6     PVOID DllBase;                              // 镜像基址
 7     PVOID EntryPoint;                           // 入口点
 8     ULONG SizeOfImage;                          // 镜像大小
 9     UNICODE_STRING FullDllName;                 // 模块全路径字符串
10     UNICODE_STRING BaseDllName;                 // 模块名称字符串
11     ULONG Flags;
12     WORD LoadCount;
13     WORD TlsIndex;
14     union
15     {
16         LIST_ENTRY HashLinks;
17         struct
18         {
19             PVOID SectionPointer;
20             ULONG CheckSum;
21         };
22     };
23     union
24     {
25         ULONG TimeDateStamp;
26         PVOID LoadedImports;
27     };
28     _ACTIVATION_CONTEXT * EntryPointActivationContext;
29     PVOID PatchInformation;
30     LIST_ENTRY ForwarderLinks;
31     LIST_ENTRY ServiceTagLinks;
32     LIST_ENTRY StaticLinks;
33 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
34 // 每一个被加载的模块都对应一个该结构体,结构体中保存了该模块的一些信息。

Windows XP Professional Service Pack 3 (x86) (5.1, Build 2600)

 1 lkd> dt -b _LDR_DATA_TABLE_ENTRY
 2 nt!_LDR_DATA_TABLE_ENTRY
 3    +0x000 InLoadOrderLinks               // 加载时的顺序
 4       +0x000 Flink                       //  
 5       +0x004 Blink                       // 
 6    +0x008 InMemoryOrderLinks             // 内存中的顺序
 7       +0x000 Flink                       // 
 8       +0x004 Blink                       // 
 9    +0x010 InInitializationOrderLinks     // 初始化的顺序
10       +0x000 Flink                       //  
11       +0x004 Blink                       // 
12    +0x018 DllBase                        // 镜像基址
13    +0x01c EntryPoint                     // 入口点 
14    +0x020 SizeOfImage                    // 镜像大小
15    +0x024 FullDllName                    // 模块路径
16       +0x000 Length                      // 
17       +0x002 MaximumLength               // 
18       +0x004 Buffer                      // 
19    +0x02c BaseDllName                    // 模块名称
20       +0x000 Length                      // 
21       +0x002 MaximumLength               // 
22       +0x004 Buffer                      //  
23    +0x034 Flags                          // 
24    +0x038 LoadCount                      // 
25    +0x03a TlsIndex                       // 
26    +0x03c HashLinks                      // 
27       +0x000 Flink                       //  
28       +0x004 Blink                       // 
29    +0x03c SectionPointer                 // 
30    +0x040 CheckSum                       // 
31    +0x044 TimeDateStamp                  // 
32    +0x044 LoadedImports                  // 
33    +0x048 EntryPointActivationContext    // 
34    +0x04c PatchInformation               // 
View Code

当通过PEB_LDR_DATA结构体中的三个双向链表遍历模块时,它们总是指向下一个或上一个LDR_DATA_TABLE_ENTRY结构体的同样位置
例如:
PEB_LDR_DATA->InLoadOrderModuleList遍历时,指针指向下一个LDR_DATA_TABLE_ENTRY->InLoadOrderModuleList位置。
PEB_LDR_DATA->InMemoryOrderModuleList遍历时,指针指向下一个LDR_DATA_TABLE_ENTRY->InMemoryOrderModuleList位置。
PEB_LDR_DATA->InInitializationOrderModuleList遍历时,指针指向下一个LDR_DATA_TABLE_ENTRY->InInitializationOrderModuleList位置。

示例代码:

1 00404830  mov eax,dword ptr fs:[0x30]           //  PEB = FS:[0x30]
2 00404836  mov eax,dword ptr ds:[eax+0xC]        //  PEB_LDR_DATA = [PEB+0xC]
3 00404839  mov eax,dword ptr ds:[eax+0xC]        //  InLoadOrderModuleList 使用模块加载顺序遍历
4 0040483C  mov eax,dword ptr ds:[eax+0x18]       //  eax = 00400000(LDR_DATA_TABLE_ENTRY.DllBase) 第一个DLL模块基址
1 00404830  mov eax,dword ptr fs:[0x30]           //  PEB = FS:[0x30]
2 00404836  mov eax,dword ptr ds:[eax+0xC]        //  PEB_LDR_DATA = [PEB+0xC]
3 00404839  mov eax,dword ptr ds:[eax+0x14]       //  InMemoryOrderModuleList 使用模块在内存中的顺序遍历
4 0040483C  mov eax,dword ptr ds:[eax+0x10]       //  eax = 00400000(LDR_DATA_TABLE_ENTRY.DllBase) 第一个DLL模块基址
1 00404830  mov eax,dword ptr fs:[0x30]           //  PEB = FS:[0x30]
2 00404836  mov eax,dword ptr ds:[eax+0xC]        //  PEB_LDR_DATA = [PEB+0xC]
3 00404839  mov eax,dword ptr ds:[eax+0x1C]       //  InMemoryOrderModuleList 使用模块初始化时的顺序遍历
4 0040483C  mov eax,dword ptr ds:[eax+0x8]        //  eax = 776C0000(LDR_DATA_TABLE_ENTRY.DllBase) 第一个DLL模块基址

7、从内存中获取Kernel32.dll基址后,我们就可以通过解析PE文件导出表来获取导出函数调用地址了,关于解析导出表获取导出函数的内容之前文章已经讲过,这里不再赘述。详情可以查阅这篇文章:https://www.cnblogs.com/SunsetR/p/11234093.html

 

posted @ 2019-07-25 16:09  SunsetR  阅读(1146)  评论(0编辑  收藏  举报