这里看不出任何理由,因此可以判断这个0x260毫无疑问是个硬编码而已
在我的XP SP3上,这个值是0x280,所以没有通用性
想要完美得到这个HashTable的地址,其实可以通过它的定义把它的地址“算”出来
以我的XP SP3为例,在windbg中观察到以下信息:
先找到PEB,这个不难,再找其中的Ldr,这几个偏移好像万年不变
0:000> dt _PEB 7ffdf000
ntdll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0x1 ''
+0x003 SpareBool : 0 ''
+0x004 Mutant : 0xffffffff Void
+0x008 ImageBaseAddress : 0x00400000 Void
+0x00c Ldr : 0x00251ea0 _PEB_LDR_DATA
0:000> dt _PEB_LDR_DATA 0x00251ea0
ntdll!_PEB_LDR_DATA
+0x000 Length : 0x28
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x251ee0 - 0x253990 ]
+0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x251ee8 - 0x253998 ]
+0x01c [COLOR="Red"][B]InInitializationOrderModuleList [/B][/COLOR]: _LIST_ENTRY [ 0x251f58 - 0x2538e0 ]
+0x024 EntryInProgress : (null)
Ldr+0xC开始,其实是个LDR_DATA_TABLE_ENTRY结构,这三个链表中的节点是一样的,但是顺序不一样,我们看第三个InInitializationOrderModuleList ,按初始化顺序,第一个被初始化的当然是ntdll.dll
0:000> dt nt!_LDR_DATA_TABLE_ENTRY 0x251f58-10
ntdll!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x252010 - 0x251ee0 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x252018 - 0x251ee8 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x252020 - 0x251ebc ]
+0x018 DllBase : 0x7c920000 Void
+0x01c EntryPoint : 0x7c9320f8 Void
+0x020 SizeOfImage : 0x96000
+0x024 FullDllName : _UNICODE_STRING "C:\windows\system32\ntdll.dll"
+0x02c BaseDllName : _UNICODE_STRING "ntdll.dll"
+0x034 Flags : 0x85004
+0x038 LoadCount : 0xffff
+0x03a TlsIndex : 0
[COLOR="Red"][B]+0x03c HashLinks : _LIST_ENTRY [ 0x25384c - 0x7c99e2e8 ] //就是这个Link[/B][/COLOR]
+0x03c SectionPointer : 0x0025384c Void
+0x040 CheckSum : 0x7c99e2e8
+0x044 TimeDateStamp : 0x4d00f280
+0x044 LoadedImports : 0x4d00f280 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
上面加红的就是这个Link了,考虑到LdrpHashTable的定义如下:
#define LDRP_HASH_TABLE_SIZE 32
#define LDRP_HASH_MASK (LDRP_HASH_TABLE_SIZE-1)
#define LDRP_COMPUTE_HASH_INDEX(wch) ( (RtlUpcaseUnicodeChar((wch)) - (WCHAR)'A') & LDRP_HASH_MASK )
LIST_ENTRY LdrpHashTable[LDRP_HASH_TABLE_SIZE];
可以看到Hash算法其实就是模块名称的首字母大写之后减去'A',然后与32取余
然后挂入一个双链表数组中(没学过数据结构的请自行补课, 经典结构)
那么现在的情况是,已经知道了模块ntdll.dll的首节点,来反推数组的首地址,我们按算法算一下:
'N '- ' A' = 13
13 * sizeof(LIST_ENTRY)= 13 * 8 = 104 = 0x68
0x7c99e2e8 - 0x68 = 0x7c99e280
那么0x7c99e280 就是LdrpHashTable的地址了
来验证一下:
0:000> x ntdll!*hashtable*
7c99e280 ntdll!LdrpHashTable = <no type information>
至于编码实现,楼主就自己去实现吧。
虽然这里面也用到了几个硬编码,不过这几个是微软万年不变的,放心用好了