【逆向】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 // 每一个被加载的模块都对应一个该结构体,结构体中保存了该模块的一些信息。

当通过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

 

转载于:https://www.cnblogs.com/SunsetR/p/11244932.html

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值