| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 5361 人关注过本帖
标题:在64位环境下,用 peb寻找 kernel32. Dll 基址时出现的问题(1)
只看楼主 加入收藏
tigerdown
Rank: 1
等 级:新手上路
帖 子:72
专家分:8
注 册:2017-8-21
结帖率:64.29%
收藏
已结贴  问题点数:20 回复次数:6 
在64位环境下,用 peb寻找 kernel32. Dll 基址时出现的问题(1)
由于篇幅较长,分成二个帖子。
这寻找程序在巜 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] 指令,并对其进行了分析和研究。

搜索更多相关主题的帖子: 获取 结构 kernel32 Dll 地址 
2023-06-06 19:40
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10551
专家分:42996
注 册:2014-5-20
收藏
得分:20 
这本书上好像没有涉及到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位

图片附件: 游客没有浏览图片的权限,请 登录注册

图片附件: 游客没有浏览图片的权限,请 登录注册

图片附件: 游客没有浏览图片的权限,请 登录注册

图片附件: 游客没有浏览图片的权限,请 登录注册
2023-06-06 21:23
tigerdown
Rank: 1
等 级:新手上路
帖 子:72
专家分:8
注 册:2017-8-21
收藏
得分:0 
在64位环境下运行32程序跟在64位环境下运行64位程序是二码事,这里的关键问题是:当 kernelbase. Dll 文件加入后,它的链表发生了变化,另外用 peb寻址不受环境影响,至少在这里如此。

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

2023-06-07 09:04
tigerdown
Rank: 1
等 级:新手上路
帖 子:72
专家分:8
注 册:2017-8-21
收藏
得分:0 
在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编辑过]

2023-06-07 09:09
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10551
专家分:42996
注 册:2014-5-20
收藏
得分:0 
以下是引用tigerdown在2023-6-7 09:04:47的发言:

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

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


2023-06-07 16:36
tigerdown
Rank: 1
等 级:新手上路
帖 子:72
专家分:8
注 册:2017-8-21
收藏
得分:0 
补充一下,有一个程序用扫描文件名的方法来得到它们的基址,这程序在巜 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
2023-06-09 07:52
tigerdown
Rank: 1
等 级:新手上路
帖 子:72
专家分:8
注 册:2017-8-21
收藏
得分:0 
结合上面的程序,谁能具体解释一下指令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编辑过]

2023-06-16 07:48
快速回复:在64位环境下,用 peb寻找 kernel32. Dll 基址时出现的问题(1)
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018713 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved