注册 登录
编程论坛 汇编论坛

在64位环境下,用 peb寻找 kernel32. Dll 基址时出现的问题(1)

tigerdown 发布于 2023-06-06 19:40, 5755 次点击
由于篇幅较长,分成二个帖子。
这寻找程序在巜 Windows PE权威指南》(戚利 著 )第11章 - Pebkernelbase:
--------------------------------------------------------------------------------
; 获取kernel32.dll的基址
; 从PEB结构中搜索kernel32.dll的基地址
; 戚利
; 2010.6.27
;------------------------
    .386
    .model flat,stdcall
    option casemap:none

include    windows.inc
include    user32.inc
includelib user32.lib
include    kernel32.inc
includelib kernel32.lib

;数据段
    .data

szText  db 'kernel32.dll的基地址为%08x',0
szOut   db '%08x',0dh,0ah,0
szBuffer db 256 dup(0)

;代码段
    .code

start:

   assume fs:nothing
   mov eax,fs:[30h] ;获取PEB所在地址
   mov eax,[eax+0ch] ;获取PEB_LDR_DATA 结构指针
   mov esi,[eax+1ch] ;获取InInitializationOrderModuleList 链表头
                     ;第一个LDR_MODULE节点InInitializationOrderModuleList成员的指针
   lodsd             ;获取双向链表当前节点后继的指针
   mov eax,[eax+8]   ;获取kernel32.dll的基地址

   ;输出模块基地址
   invoke wsprintf,addr szBuffer,addr szText,eax
   invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
   ret
   end start
----------------------------------------------------------------------------------------------
先在win xp 里运行,并用 od 核对其结果正确无误,然后在 Win8. 1里运行,用 od和 x32dbg进行核对,发现得到的基址是 kernelbase. Dll而不是 kernel32. Dll,  kernel32.dll基址在76D90000.
见如下:
只有本站会员才能查看附件,请 登录


查看了一下Teb和 peb结构,见如下:

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

只有本站会员才能查看附件,请 登录


只有本站会员才能查看附件,请 登录




但问题出在那儿?!

我把焦点放在了程序中的mov edx,[eax + 8h] 指令,并对其进行了分析和研究。

7 回复
#2
吹水佬2023-06-06 21:23
这本书上好像没有涉及到64位
64位的文件头和PE头与32位的不同
64位PE头主要区别有:
1、扩展头中基址大小是64位,初始栈和堆都是64位
2、导入表中导入地址表(IAT)和导入名称表(INT)指向的数组元素都是8个字节的函数地址(RVA)
3、TLS表中的VA地址都被换成64位

IMAGE_FILE_HEADER
Machine ................ 64位:0x8664  32位:0x014C
SizeOfOptionalHeader ... 64位:0x00F0  32位:0x00E0


64位扩展PE头 IMAGE_OPTIONAL_HEADER64,没有BaseOfData成员
Magic ................ 0x20B(64位)
ImageBase ............ 64位
SizeOfStackReserve ... 64位
SizeOfStackCommit .... 64位
SizeOfHeapReserve .... 64位
SizeOfHeapCommit ..... 64位

只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录
#3
tigerdown2023-06-07 09:04
在64位环境下运行32程序跟在64位环境下运行64位程序是二码事,这里的关键问题是:当 kernelbase. Dll 文件加入后,它的链表发生了变化,另外用 peb寻址不受环境影响,至少在这里如此。

[此贴子已经被作者于2023-6-7 09:22编辑过]

#4
tigerdown2023-06-07 09:09
在64位环境下,用 peb寻找 kernel32. Dll 基址时出现的问题(2)
首先,文件 kernelbase. Dll在 win xp是不存在的,所以在 Win8. 1里它的链表结构发生了变化,发生了什么变化,在微软网上的 API文档里找不到任何信息。从运行的结果来看,本来指向 kernel32. Dll的基址现在指向了 kernelbase. Dll基址,链表里面到底是什么情况?!我用 od 对 pebkernelbase进行了调试,用 f7 stepin执行每条指令,当执行到mov eax, [eax+08h]时,进入链表的内存查看一下:

只有本站会员才能查看附件,请 登录


从中发现指向kernel32. Dll基址的指针发生了50 h的偏移,把这条指令修改为 mov eax, [eax+50h], 运行结果指向了正确基址:

只有本站会员才能查看附件,请 登录


需要注意的是:载入链表的内存地址是变化的,每次运行得到的地址是不同的,但它的偏移8h和50h是不变的。

我好奇的是 od和 dbg是怎样得到它们基址的,用了 vc模块文件,还是有内部微软文档?估计前者更有可能。

但为什么是偏移50h呢? 有谁知道详情者,恳请细细道来,本人将不胜感激。

[此贴子已经被作者于2023-6-7 09:36编辑过]

#5
吹水佬2023-06-07 16:36
以下是引用tigerdown在2023-6-7 09:04:47的发言:

在64位环境下运行32程序跟在64位环境下运行64位程序是二码事,这里的关键问题是:当 kernelbase. Dll 文件加入后,它的链表发生了变化,另外用 peb寻址不受环境影响,至少在这里如此。

是的,我理解错了,不是32位或64位DLL的问题。
看似是获取动态加载DLL的基地址,是不是想得到动态DLL的API函数地址来调用API函数,需求是能调用API而没有导入表的EXE。


#6
tigerdown2023-06-09 07:52
补充一下,有一个程序用扫描文件名的方法来得到它们的基址,这程序在巜 Windows PE权威指南》(戚利 著 )第11章 - getTwoImportantFuns  见如下. 用这种方法就不受链表变化的影响,但这个程序比较复杂,对指令不熟悉的读者来说,解读起来挺费时间,但不管怎样,我还是推荐用这个程序,估什 od和 dbg都用了这个方法。

程序代码:
------------------------
    .386
    .model flat,stdcall
    option casemap:none

include    windows.inc
include    user32.inc
includelib user32.lib
include    kernel32.inc
includelib kernel32.lib

_QLGetProcAddress typedef proto :dword,:dword      ;声明函数
_ApiGetProcAddress  typedef ptr _QLGetProcAddress  ;声明函数引用

;数据段
    .data
szText         db  'kernel32.dll在本程序地址空间的基地址为:%08x',0dh,0ah,0
szText1        db  'GetProcAddress代码在本程序地址空间的首址为:%08x',0dh,0ah,0
szText2        db  'LoadLibraryA代码在本程序地址空间的首址为:%08x',0dh,0ah,0

szGetProcAddr  db  'GetProcAddress',0
szLoadLib      db  'LoadLibraryA',0

_getProcAddress _ApiGetProcAddress  ?              ;定义函数

kernel32Base   dd  ?
lpGetProcAddr  dd  ?
lpLoadLib      dd  ?  

szBuffer       db 256 dup(0)

;代码段
    .code
;------------------------------------
;
根据kernel32.dll中的一个地址获取它的基地址
;
------------------------------------
_getKernelBase  proc _dwKernelRetAddress
   local @dwRet

   pushad
   mov @dwRet,0
   
   mov edi,_dwKernelRetAddress
   and edi,0ffff0000h  ;查找指令所在页的边界,以1000h对齐

   .repeat
     .if word ptr [edi]==IMAGE_DOS_SIGNATURE  ;找到kernel32.dll的dos头
        mov esi,edi
        add esi,[esi+003ch]
        .if word ptr [esi]==IMAGE_NT_SIGNATURE ;找到kernel32.dll的PE头标识
          mov @dwRet,edi
          .break
        .endif
     .endif
     sub edi,010000h
     .break .if edi<070000000h
   .until FALSE
   popad
   mov eax,@dwRet
   ret
_getKernelBase  endp   

;-------------------------------
;
获取指定字符串的API函数的调用地址
;
入口参数:_hModule为动态链接库的基址,_lpApi为API函数名的首址
;
出口参数:eax为函数在虚拟地址空间中的真实地址
;
-------------------------------
_getApi proc _hModule,_lpApi
   local @ret
   local @dwLen

   pushad
   mov @ret,0
   ;计算API字符串的长度,含最后的零
   mov edi,_lpApi
   mov ecx,-1
   xor al,al
   cld
   repnz scasb
   mov ecx,edi
   sub ecx,_lpApi
   mov @dwLen,ecx

   ;从pe文件头的数据目录获取导出表地址
   mov esi,_hModule
   add esi,[esi+3ch]
   assume esi:ptr IMAGE_NT_HEADERS
   mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
   add esi,_hModule
   assume esi:ptr IMAGE_EXPORT_DIRECTORY

   ;查找符合名称的导出函数名
   mov ebx,[esi].AddressOfNames
   add ebx,_hModule
   xor edx,edx
   .repeat
     push esi
     mov edi,[ebx]
     add edi,_hModule
     mov esi,_lpApi
     mov ecx,@dwLen
     repz cmpsb
     .if ZERO?
       pop esi
       jmp @F
     .endif
     pop esi
     add ebx,4
     inc edx
   .until edx>=[esi].NumberOfNames
   jmp _ret
@@:
   ;通过API名称索引获取序号索引再获取地址索引
   sub ebx,[esi].AddressOfNames
   sub ebx,_hModule
   shr ebx,1
   add ebx,[esi].AddressOfNameOrdinals
   add ebx,_hModule
   movzx eax,word ptr [ebx]
   shl eax,2
   add eax,[esi].AddressOfFunctions
   add eax,_hModule
   
   ;从地址表得到导出函数的地址
   mov eax,[eax]
   add eax,_hModule
   mov @ret,eax

_ret:
   assume esi:nothing
   popad
   mov eax,@ret
   ret
_getApi endp

start:
    mov eax,dword ptr [esp]
    invoke _getKernelBase,eax
    mov kernel32Base,eax
    invoke wsprintf,addr szBuffer,addr szText,eax
    invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK

    invoke _getApi,kernel32Base,addr szGetProcAddr
    mov lpGetProcAddr,eax
    mov _getProcAddress,eax   ;为函数引用赋值 GetProcAddress
    invoke wsprintf,addr szBuffer,addr szText1,eax
    invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK


    invoke _getProcAddress,kernel32Base,addr szLoadLib
    mov lpLoadLib,eax
    invoke wsprintf,addr szBuffer,addr szText2,eax
    invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK

    ret
    end start
#7
tigerdown2023-06-16 07:48
结合上面的程序,谁能具体解释一下指令repnz scasb 的含义。
....

_getApi proc _hModule,_lpApi
   local @ret
   local @dwLen

   pushad
   mov @ret,0
   ;计算API字符串的长度,含最后的零
   mov edi,_lpApi
   mov ecx,-1
   xor al,al
   cld
   repnz scasb
   mov ecx,edi
   sub ecx,_lpApi
   mov @dwLen,ecx

.......


[此贴子已经被作者于2023-6-16 08:05编辑过]

#8
连海磊4 天前 15:06
asga  nsjurru  hiwe njd oai j,dvy .adghd ai . ,adgha,d,w haiad. uhadjaihdah  -dhavw
dgag .dbjah , uaioiubw ,f,hhu .hfiena  ,
fiu  -auyd .sfk we .ahdih ,abksb +vhdj . jbab a  bjabb ,ahiaaklsoie  ,abshaobeu  -adw
aeg bas.hfi . bdwe
ij
-svdh-d
badjs  .sjd ,sdba
djhsui -sjdirndsh-sdgj
dbja
bvbf,sdhhs .sakjoj njeoi  idkewoij k-eudba
计算sy,字符段数 ,
sh udak , hfhesoe /.-syag bsvhgewiopeouhewaalwhwa
ahs idsuh ,dbhhayej i.shaid ,sb -jhse -sakeo
dga , ahid
ahd iheiiafh
dshoroojr-aus bs hks,kdiwifewo/sidu l/shh aisr/di hbjsshbbdh
adweioopfo
duw n,d ls,jaoej ekpwoe-euebjwge -iewopqp -euj b
adhekn kuif  hihiwhr98ryr ewh wopi
he foy pheyr9wpqh
sau .eroj wueww ehewkh jebw-beahajbs,a../jdjewy -ebwj
sa .adknaj ,dlhdr-davhdjajb /dbuab ge
vsdabk b,dbjba kaoa jflaa-sayhiehdfe uiquijwo
dhih
\adnan
adbia lamoljiwe ,aoidwj
auihw i
ds  hjkajsihe /jaoij p-advab -wahj jaij
1