fs寄存器及kernel32.dll地址的获取

本文介绍了三种获取KERNEL32.DLL基址的方法:利用PEB结构查找、堆栈暴力搜索及使用SEH链表。这些方法分别通过访问特定的数据结构如TEB、PEB_LDR_DATA和LDR_MODULE链表实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FS寄存器指向当前活动线程的TEB结构(线程结构)
偏移 说明
000 指向SEH链指针
004 线程堆栈顶部
008 线程堆栈底部
00C SubSystemTib
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在内存中的镜像地址
020 进程PID
024 线程ID
02C 指向线程局部存储指针
030 PEB结构地址(进程结构)
034 上个错误号


得到KERNEL32.DLL基址的方法
assume fs:nothing             ;打开FS寄存器
mov eax,fs:[30h]            ;得到PEB结构地址
mov eax,[eax + 0ch]        ;得到PEB_LDR_DATA结构地址
mov esi,[eax + 1ch]        ;InInitializationOrderModuleList
lodsd                      ;得到KERNEL32.DLL所在LDR_MODULE结构的InInitializationOrderModuleList地址
mov edx,[eax + 8h]         ;得到BaseAddress,既Kernel32.dll基址

 

1)利用PEB结构来查找
原理:FS段寄存器作为选择子指向当前的TEB结构,在TEB偏移0x30处是PEB指针。而在PEB偏移的0x0c处是指向PEB_LDR_DATA结构的指针,位于         PEB_LDR_DATA结构偏移0x1c处,是一个叫InInitialzationOrderModuleList的成员,他是指向LDR_MODULE链表结构中,相应的双向链表头部        的指针(汗死,绕口令),该链表加载的DLL的顺序是ntdll.dll,kernel32.dll(偶找了N多资料,都只介绍这个DLL,如果你知道其他的请告诉偶)
        因此该成员所指的链表偏移0x08处为kernel32.dll地址
typedef struct _PEB { // Size: 0x1D8
/*000*/ UCHAR InheritedAddressSpace;
/*001*/ UCHAR ReadImageFileExecOptions;
/*002*/ UCHAR BeingDebugged;
/*003*/ UCHAR SpareBool; // Allocation size
/*004*/ HANDLE Mutant;
/*008*/ HINSTANCE ImageBaseAddress; // Instance
/*00C*/ VOID *DllList;
/*010*/ PPROCESS_PARAMETERS *ProcessParameters;
/*014*/ ULONG SubSystemData;
/*018*/ HANDLE DefaultHeap;
/*01C*/ KSPIN_LOCK FastPebLock;
/*020*/ ULONG FastPebLockRoutine;
/*024*/ ULONG FastPebUnlockRoutine;
/*028*/ ULONG EnvironmentUpdateCount;
/*02C*/ ULONG KernelCallbackTable;
/*030*/ LARGE_INTEGER SystemReserved;
/*038*/ ULONG FreeList;
/*03C*/ ULONG TlsExpansionCounter;
/*040*/ ULONG TlsBitmap;
/*044*/ LARGE_INTEGER TlsBitmapBits;
/*04C*/ ULONG ReadOnlySharedMemoryBase;
/*050*/ ULONG ReadOnlySharedMemoryHeap;
/*054*/ ULONG ReadOnlyStaticServerData;
/*058*/ ULONG AnsiCodePageData;
/*05C*/ ULONG OemCodePageData;
/*060*/ ULONG UnicodeCaseTableData;
/*064*/ ULONG NumberOfProcessors;
/*068*/ LARGE_INTEGER NtGlobalFlag; // Address of a local copy
/*070*/ LARGE_INTEGER CriticalSectionTimeout;
/*078*/ ULONG HeapSegmentReserve;
/*07C*/ ULONG HeapSegmentCommit;
/*080*/ ULONG HeapDeCommitTotalFreeThreshold;
/*084*/ ULONG HeapDeCommitFreeBlockThreshold;
/*088*/ ULONG NumberOfHeaps;
/*08C*/ ULONG MaximumNumberOfHeaps;
/*090*/ ULONG ProcessHeaps;
/*094*/ ULONG GdiSharedHandleTable;
/*098*/ ULONG ProcessStarterHelper;
/*09C*/ ULONG GdiDCAttributeList;
/*0A0*/ KSPIN_LOCK LoaderLock;
/*0A4*/ ULONG OSMajorVersion;
/*0A8*/ ULONG OSMinorVersion;
/*0AC*/ USHORT OSBuildNumber;
/*0AE*/ USHORT OSCSDVersion;
/*0B0*/ ULONG OSPlatformId;
/*0B4*/ ULONG ImageSubsystem;
/*0B8*/ ULONG ImageSubsystemMajorVersion;
/*0BC*/ ULONG ImageSubsystemMinorVersion;
/*0C0*/ ULONG ImageProcessAffinityMask;
/*0C4*/ ULONG GdiHandleBuffer[0x22];
/*14C*/ ULONG PostProcessInitRoutine;
/*150*/ ULONG TlsExpansionBitmap;
/*154*/ UCHAR TlsExpansionBitmapBits[0x80];
/*1D4*/ ULONG SessionId;
} PEB, *PPEB;

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

-typedef struct _LDR_MODULE

-{

-    LIST_ENTRY          InLoadOrderModuleList;   +0x00

-    LIST_ENTRY          InMemoryOrderModuleList; +0x08  

-    LIST_ENTRY          InInitializationOrderModuleList; +0x10

-    void*               BaseAddress;  +0x18

-    void*               EntryPoint;   +0x1c

-    ULONG               SizeOfImage;

-    UNICODE_STRING      FullDllName;

-    UNICODE_STRING      BaseDllName;

-    ULONG               Flags;

-    SHORT               LoadCount;

-    SHORT               TlsIndex;

-    HANDLE              SectionHandle;

-    ULONG               CheckSum;

-    ULONG               TimeDateStamp;

-} LDR_MODULE, *PLDR_MODULE;



实例代码:
               mov  eax,fs:[0x30]
               mov  eax,[eax+0x0c]
               mov  esi ,[eax+0x1c]
               lodsd
               mov eax,[eax+0x08]
               ret

2)利用堆栈暴力搜索

原理:  在程序开始时,栈顶所指向的值是kernel32.dll所在页的某个高地址,我们采用向下搜索的方法,来查找。同时,在PE中的            image_optional_header中在偏移0x34处有kernel32.dll的地址。以下是image_optional_header的结构    

   
     IMAGE_OPTIONAL_HEADER STRUC             ; Optional Header
     OH_Magic                              DW ?           ; Magic word
     OH_MajorLinkerVersion           DB ?           ; Major Linker version
     OH_MinorLinkerVersion           DB ?           ; Minor Linker version
     OH_SizeOfCode                      DD ?           ; Size of code section
     OH_SizeOfInitializedData         DD ?           ; Initialized Data
     OH_SizeOfUninitializedData     DD ?           ; Uninitialized Data
     OH_AddressOfEntryPoint         DD BYTE PTR ?  ; Initial EIP
     OH_BaseOfCode                     DD BYTE PTR ?  ; Code Virtual Address

     OH_BaseOfData                     DD BYTE PTR ?  ; Data Virtual Address

     OH_ImageBase                       DD BYTE PTR ?  ; Base of image-----------------------------------kernel32.dll地址
     OH_SectionAlignment              DD ?           ; Section Alignment
     OH_FileAlignment                   DD ?           ; File Alignment
     OH_MajorOperatingSystemVersion  DW ?           ; Major OS
     OH_MinorOperatingSystemVersion  DW ?           ; Minor OS
     OH_MajorImageVersion                 DW ?           ; Major Image version
     OH_MinorImageVersion                 DW ?           ; Minor Image version
     OH_MajorSubsystemVersion          DW ?           ; Major Subsys version
     OH_MinorSubsystemVersion          DW ?           ; Minor Subsys version
     OH_Win32VersionValue                 DD ?           ; win32 version
     OH_SizeOfImage                          DD ?           ; Size of image
     OH_SizeOfHeaders                       DD ?           ; Size of Header
     OH_CheckSum                             DD ?           ; unused
     OH_Subsystem                            DW ?           ; Subsystem
     OH_DllCharacteristics                   DW ?           ; DLL characteristic
     OH_SizeOfStackReserve               DD ?           ; Stack reserve
     OH_SizeOfStackCommit               DD ?           ; Stack commit
     OH_SizeOfHeapReserve               DD ?           ; Heap reserve
     OH_SizeOfHeapCommit               DD ?           ; Heap commit
     OH_LoaderFlags                         DD ?           ; Loader flags
     OH_NumberOfRvaAndSizes          DD ?           ; Number of directories
                                      UNION                         ;         directory entries
     OH_DataDirectory        IMAGE_DATA_DIRECTORY/
                                      IMAGE_NUMBEROF_DIRECTORY_ENTRIES DUP (?)
     OH_DirectoryEntries    IMAGE_DIRECTORY_ENTRIES ?
                                     ENDS                         ;
     ENDS                                                    ;

实例代码:
        start:   mov  edx,[esp]
                   dec   edx
                   mov  ebx,[edx+0x3c]
                   mov  ebx,[ebx+edx+0x34]
                   cmp  edx,ebx
                   jnz    start
                   ret                           ;此时在edx中的值为kernel32.dll地址

3)使用SEH的链表来查找
原理:在SEH中默认的unhandled exception hander 是利用kernel32.DLL中的一个函数设置的。因此可以历遍所有的exception    hander,          找到最后一个成员,该成员的前4个字节是0ffffffffh,后4个字节是kernel32.dll中的函数(看了很多paper,都没有说明为什么通过该函数可以得到kernel32.dll的地址,偶个人认为,通过这个函数定位到了输出表,然后向低地址搜索,就能找到ZM头,也就是kernel32的地址,如果有错误,请告诉偶,谢谢)

实例代码:
                   mov esi,fs[0]
        @@1:  lodsd
                   cmp [eax],0xffffffff
                   jne   @@1
                   mov  eax,[eax+0x04]
        @@2:  dec   eax
                   xor    ax,ax
                   cmp  eax word ptr[eax]  'MZ'
                   jne    @@2
                   ret
此时eax为kernel32.dll地址

参考:《恶意代码的亲密接触》
          《gloomy对内核的分析》
          《通过TEB/PEB枚举当前进程空间中用户模块列表》

<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、付费专栏及课程。

余额充值