首页
论坛
专栏
课程

[Anti Virus专题]1.2 - 2.kernel32基地址获得

2009-4-10 15:09 64782

[Anti Virus专题]1.2 - 2.kernel32基地址获得

2009-4-10 15:09
64782
1.2 2.kernel32基地址获得

    

    kernel32基地址的获得也是病毒中必不可少的技术,因为在win32环境中一般初始化PE文件的时候,我们的ntdll.dll, kernel32.dll是随进程以及线程初始化时候加载的。所以即使程

序中我们不引入任何输入表结构,这两个DLL在我们程序进程以及线程初始化时也必须加载。所以我们今天这篇文章的目的就是在我们的进程内存空间中来获得加载kernel32.dll的基地址。因为我们获得

kernel32.dll的基地址后,就可以获得LoadLibray函数来继续加载其他的动态链接库,然后再通过我们的GetProcAdress函数来获得相应需要的api函数地址,这样也就做到了可移植性的要求,所有的函数

均是自己动态获取并填充。

    

    今天我给大家介绍三种获取kernel32.dll基地址的技术,其实还有部分方法也可以获得,只是这3种比较通用,也是病毒中经常用到的。它们分别是

    1. 通过线程初始化时, 获得esp堆栈指针中的ExitThread函数的地址,然后通过搜索获得kernel32.dll的基地址。

    2. 遍历seh异常链,然后获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll,通过它搜索得到kernel32.dll的基地址。

    3. 通过TEB获得PEB结构地址,然后再获得PEB_LDR_DATA结构地址,然后遍历模块列表,查找kernel32.dll模块的基地址。
    
  1.

第一种方法:

  我们上面介绍了线程在被初始化的时,其堆栈指针指向ExitThread函数的地址,windows这样做是为了通过ret返回时来调用ExitThread地址。所以一般我们可以在我们主线程的起始位置(也就是

程序入口点处)通过获得堆栈指针中ExitThread函数(当然你要想创建一个线程时候获得也可以o(∩_∩)o...哈哈)。

  我们直接通过
  mov  edx, [esp] ;获得堆栈指针中ExitThread地址到edx寄存器。因为ExitThread地址在kernel32.dll空间中,所以我们可以通过它往上搜索来获得基地址。

  分析过kernel32.dll的朋友应该都知道kernel32.dll的块对齐值是00001000h, 并且一般DLL以1M为边界,所以我们可以通过10000h (64k) 作为跨度,这样可以增加搜索速度。我们如何确定这个

地址是基地址,我们都知道我们判断这个地址的前两个字节是否是"MZ",然后定位到PE头结构,然后判断是否是"PE",如果这两个都符合的话则表示我们的地址则是基地址了。。

  有了以上的了解我们就可以来写代码了。

  mov  edx, [esp] 
 .Next:  
  dec  edx  ;
  xor  dx, dx  ; 减去跨度
  cmp  word [edx], "MZ"
  jz  .IsPe
  jmp  .Next
  
 .IsPe:
  mov  eax, [edx+3ch]
  cmp  word [eax+edx], 'PE'
  jnz  .Next
  xchg  eax, edx   ; eax = kernel32 基地址

这里贴一个封装过的过程。

 ;++
 ;
 ; int
 ;   GetKrnlBase(
 ;      int KrnlApiAddress
 ;   )
 ;
 ; Routine Description:
 ;
 ;    获得kernel32基地址
 ;
 ; Arguments:
 ;
 ;    (esp)          - return address
 ;
 ;    Data   (esp+4) - KrnlApiAddress, ptr ApiAddress
 ;
 ;
 ; Return Value:
 ;
 ;    eax =  krnl32 base
 ;
 ;--

 GetKrnlBase:
   pop  eax
   pop  edx
   push  eax
   
 .Next:
   cmp  word [edx], 'MZ' 
   jz  .IsPe
  dec  edx
  xor  dx, dx
   jmp  .Next
 
 .IsPe:  
   mov  eax, [edx+3ch]
   cmp  word [eax+edx], 'PE'
   jnz  .Next
   xchg  eax, edx
   ret



  
2. 
  第二种方法:
  
  这个方法是遍历遍历seh异常链,然后获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll,通过它搜索得到kernel32.dll的基地址。
搜索的方法在上面我已经说了,通过减去跨度,然后判断地址前两字节是否是"MZ",是的话,继续定位到PE头结构,然后判断前两个字节是否是"PE",不是的话继续减去跨度搜索。直到是为止。

  struct EXCEPTION_REGISTRATION 
        prev dd ?                       
        handler dd ?                   
  ends 

  遍历的方法也很简单,我们都知道[fs:0]的ExceptionList 指向EXCEPTION_REGISTRATION结构,所以通过[fs:0]获得EXCEPTION_REGISTRATION结构后,判断prev成员是否是-1,如果是的话则取

异常处理过程地址,然后进行搜索。
  
  
  有了思路,我们开始写代码。

  mov  edx, [fs:0]  ;获得EXCEPTION_REGISTRATION结构地址
 .Next:
  inc  dword [edx]  ;将prev成员 + 1
  jz  .Krnl    ;如果ZF = 1, 则跳转.Krnl
  dec  dword [edx]
  mov  edx,  [edx]
  jmp  .Next
 .Krnl:
  dec  dword [edx]  ;恢复 -1
  mov  edx, [edx+4]  ;获得handler, 然后下面进行搜索..
 .Loop:
  cmp  word [edx], 'MZ' 
   jz  .IsPe
  dec  edx
  xor  dx, dx
  jmp  .Loop

 .IsPe:  
   mov  eax, [edx+3ch]
   cmp  word [eax+edx], 'PE'
   jnz  .Next
   xchg  eax, edx  ; eax = kernel32 基地址

一个封装过的过程:
 ;++
 ;
 ; int
 ;   GetKrnlBase2(
 ;    void
 ;   )
 ;
 ; Routine Description:
 ;
 ;    获得kernel32基地址
 ;
 ; Arguments:
 ;
 ;    (esp)          - return address
 ;
 ;
 ; Return Value:
 ;
 ;    eax =  krnl32 base
 ;
 ;--
 
 GetKrnlBase2:
   mov  edx, [fs:0]
 .Next:
  inc  dword [edx]
  jz  .Krnl
  dec  dword [edx]
  mov  edx,  [edx]
  jmp  .Next
 .Krnl:
  dec  dword [edx]
  mov  edx, [edx+4]
 .Loop:
  cmp  word [edx], 'MZ' 
   jz  .IsPe
  dec  edx
  xor  dx, dx
  jmp  .Loop

 .IsPe:  
   mov  eax, [edx+3ch]
   cmp  word [eax+edx], 'PE'
   jnz  .Next
   xchg  eax, edx
   ret


3. 

第三种方法:

  此方法是通过TEB获得PEB结构地址,然后再获得PEB_LDR_DATA结构地址,然后遍历模块列表,查找kernel32.dll模块的基地址。

  TEB是线程环境块(Thread Environment Block)结构, 我们的fs段选择子所对应的段指向TEB,也就是fs:0(注意这里可不是“[fs:0]”)指向TEB.那么TEB的ProcessEnvironmentBlock结构成员指

向我们的PEB进程环境块结构(Process Environment Block),然后通过PEB结构来获得PEB_LDR_DATA。 接下来我们通过windbg来查看下相关结构。

我们首先来看下TEB结构,通过windbg的dt命令。

lkd> dt _TEB
nt!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x01c EnvironmentPointer : Ptr32 Void
   +0x020 ClientId         : _CLIENT_ID
   +0x028 ActiveRpcHandle  : Ptr32 Void
   +0x02c ThreadLocalStoragePointer : Ptr32 Void
   +0x030 ProcessEnvironmentBlock : Ptr32 _PEB  ;;;;;;;;;;

   ......省略

我们可以看到TEB结构的0x30偏移处存储的我们的PEB结构的地址。。

   然后接下来我们来看PEB结构。

lkd> dt _PEB
nt!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 SpareBool        : UChar
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   ..........省略

我们可以看到PEB结构的0x0c偏移处存储的我们的_PEB_LDR_DATA结构地址。

    好到这里我们可以先把获得_PEB_LDR_DATA结构地址的代码写出来。

    mov eax, [fs:30h] ;Get Peb
    mov eax, [eax+0ch] ;Get _PEB_LDR_DATA

然后我们再来查看 _PEB_LDR_DATA结构

lkd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr32 Void
   +0x00c InLoadOrderModuleList : _LIST_ENTRY
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY
   +0x024 EntryInProgress  : Ptr32 Void

我们看到这个结构中的模块立标有3个_LIST_ENTRY结构,它们分别是
InLoadOrderModuleList (加载顺序模块列表)
InMemoryOrderModuleList(内存顺序模块排列)
InInitializationOrderModuleList(初始化顺序模块列表)

然后我们继续查看这个结构。

nt!_LIST_ENTRY
   +0x000 Flink            : Ptr32 _LIST_ENTRY
   +0x004 Blink            : Ptr32 _LIST_ENTRY
  
这个结构我们可以看到它是一个双向链表,Flink表示从前往后, Blink表示从后往前。

并且这三个链表的结点是均是指向此结构

typedef struct _LDR_MODULE
{
    LIST_ENTRY        InLoadOrderModuleList;            // +0x00
    LIST_ENTRY        InMemoryOrderModuleList;          // +0x08
    LIST_ENTRY        InInitializationOrderModuleList;  // +0x10
    PVOID             BaseAddress;                      // +0x18
    PVOID             EntryPoint;                       // +0x1c
    ULONG             SizeOfImage;                      // +0x20
    UNICODE_STRING    FullDllName;                      // +0x24
    UNICODE_STRING    BaseDllName;                      // +0x2c
    ULONG             Flags;                            // +0x34
    SHORT             LoadCount;                        // +0x38
    SHORT             TlsIndex;                         // +0x3a
    LIST_ENTRY        HashTableEntry;                   // +0x3c
    ULONG             TimeDateStamp;                    // +0x44
                                                        // +0x48
} LDR_MODULE, *PLDR_MODULE;

我们一般取它的初始化顺序结构(InInitializationOrderModuleList)的Flink成员指向的_LDR_MODULE结构的BaseAddress成员则为我们需要的基地址,当然由于第一个是
ntdll,所以取第二个则为我们的Kernel32.dll。

OK我们开始写代码,上面我们已经写了取得_PEB_LDR_DATA结构地址的代码了。我们把它copy下来。

    mov eax, [fs:30h] ;Get Peb
    mov eax, [eax+0ch] ;Get _PEB_LDR_DATA
    mov eax, [eax+1ch];Get InInitializationOrderModuleList.Flink, 此时eax指向的是ntdll模块的InInitializationOrderModuleList线性地址。所以我们获得它的下一个则是kernel32.dll
    mov eax, [eax]
    mov eax, [eax+8] ; 8 = sizeof.LIST_ENTRY
    ret

同样我们贴一个封装过的过程。
 ;++
 ;
 ; int
 ;   GetKrnlBase3(
 ;    void
 ;   )
 ;
 ; Routine Description:
 ;
 ;    获得kernel32基地址
 ;
 ; Arguments:
 ;
 ;    (esp)          - return address
 ;
 ;
 ; Return Value:
 ;
 ;    eax =  krnl32 base
 ;
 ;--
 
 GetKrnlBase3:
   mov  eax, [fs:30h]
   mov  eax, [eax+0ch]
   mov  eax, [eax+1ch]
   mov  eax, [eax]
  mov  eax, [eax+8h]
  ret   
 


附件为此三种方法的代码和bin。

[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
最新回复 (86)
dsjHZAHfaf 2009-4-10 15:13
2
0
请问这是什么fasm?

注:我才是第一个顶的.别的都是假的.
zapline 2009-4-10 15:14
3
0
支持一下。。
moonife 8 2009-4-10 15:15
4
0
这么好的系列文章,我一定要顶啊
xfish 5 2009-4-10 15:27
5
0
传统的fasm啊,写这个专题我基本上没有用宏。所以大家可以直接编译的。

这里下载。
http://flatassembler.net/download.php
zapline 2009-4-10 15:29
6
0
#include "stdio.h"
void main()
{
        long addr;
       
        __asm
        {
                mov  eax, fs:[30h]
                mov  eax, [eax+0ch]
                mov  eax, [eax+1ch]
                mov  eax, [eax]
                mov  eax, [eax+8h]
                mov  addr,eax
        }
        printf("%lx\n",addr);
}

只用汇编还是不会写啥东西,学习
再支持一下
xfish 5 2009-4-10 15:37
7
0
[QUOTE=zapline;604624]#include "stdio.h"
void main()
{
        long addr;
       
        __asm
        {
                mov  eax, fs:[30h]
                mov  eax, [eax+0ch]
                mov  eax, [eax+1ch]
                mov  eax, [eax]
...[/QUOTE]

代码最重要还是要理解。
如果仅写不理解的话就没什么意思了。
希望这个专题能让大家学到的是思路而并非是如何写。
udknight 2009-4-10 15:38
8
0
lz的文章很不错啊,希望能多看到这个系列的作品
zhoro 2009-4-10 16:12
9
0
是不是从29a搞过来的。。。。老东西了
xfish 5 2009-4-10 16:24
10
0
哎。
现在的人可悲的是连看都没有看就在那乱评论。
麻烦你去从29a一期杂志到八期杂志找找。
文章我力求从原理开始讲解,从文章也应该可以看到讲解更多的是原理以及思路。而且代码也更加短小精炼。每篇文章我花费不少心心思来想如何更全面更易懂的方式的来讲解,这年头鸟人就是多。
dsjHZAHfaf 2009-4-10 16:29
11
0
好强大.顶!
轩辕小聪 7 2009-4-10 17:30
12
0
楼主给个系统的总结是很好的,而且写得比较详细。虽然这三种方法是很早就有人总结过了。
另外第一种方法因为依赖于主线程初始化时的堆栈地址,因此一般在PE感染型病毒里使用,基本不用在shellcode里。
dsjHZAHfaf 2009-4-10 17:34
13
0
好多计算机语言都消亡了.
能读懂这些濒危语言的人都是国宝.
kanxue 8 2009-4-10 17:48
14
0
感谢xfish的系列教学。
愿意将自己所掌握的知识拿出来与大家分享,就值得肯定。
有些知识点论坛也有,但整理梳理一下,对想学的人还是很有帮助的。
不足的地方,可以一起讨论,xfish再改进。
xfish 5 2009-4-10 17:54
15
0
不仅仅是主线程,只要shellcode中能取得CreateThread函数地址,那么创建工作线程在工作线程中取得然后写入回来也可以。

而且其实还有很多其他的思路,例如通过异常然后通过异常环境此时的线程环境来取得Kernel32.dll的地址,然后进行搜索等。。
nba2005 5 2009-4-10 18:41
16
0
认真学习,好好领会。。。

支持楼主的系列。。。
combojiang 26 2009-4-10 18:49
17
0
文章写的不错,比较喜欢。但有两处笔误,都是以下这句。

mov eax, [fs:0ch] ;Get _PEB_LDR_DATA
快雪时晴 4 2009-4-10 19:50
18
0
能写汇编的都是高手
xfish 5 2009-4-10 20:25
19
0
[QUOTE=combojiang;604742]文章写的不错,比较喜欢。但有两处笔误,都是以下这句。

mov eax, [fs:0ch] ;Get _PEB_LDR_DATA[/QUOTE]

感谢指出,的确笔误,已经修正。。
flowons 2 2009-4-10 22:24
20
0
支持楼主,顶!
twoseconds 2009-4-10 23:31
21
0
顶顶顶,,,,
御剑圣者 2009-4-11 00:14
22
0
学习了,明天继续
轩辕小聪 7 2009-4-11 03:54
23
0
这就变成“先有蛋还是先有鸡”的问题了,你要取得CreateThread函数地址,用啥方法?还是先取kernel32.dll再得到CreateThread地址的。或者说,如果都有了CreateThread函数地址了,那直接用这函数地址去搜索kernel32.dll就行了。
xfish 5 2009-4-11 14:51
24
0
刚起来..

  我在开头已经说了,线程在被初始化的时,其堆栈指针指向ExitThread函数的地址,一般我们在主线程初始化时来进行取得。这个方法的原理就是这样,而你在下面又叙述了遍而且说只能依赖主线程初始化时取得,这句话本来就是错的,任何线程都可以,我在文章早也已经说了原理。

  所以我才要反驳你,这的确是先有蛋还是先有鸡,其实这个本就没什么讨论的,我在文章中早已经说过了,而你非要提下只能依赖主线程初始化时来进行取得。这个本身在shellcode中,只要先拿到鸡,然后在去拿蛋也可以,难道不是吗。。获取kernel32基地址方法本就很多,看你如何运用思路了,如我前面所提的思路等。。

下了,周末出去放松去了。。周一见吧。。。
wowzjj 2009-4-11 17:36
25
0
支持下,希望楼主坚持把教学搞完,千万别夭折了.
轩辕小聪 7 2009-4-11 20:01
26
0
其实我主要并不是在强调这个“主线程”的严密概念,也许你误以为我是故意强调这个“主”字,是我表述不严密。
我的意思是一般进入shellcode的一开始不是在线程初始化的环境,这和修改入口点的感染型病毒的环境不一样,shellcode的环境通常更具有随机性,因此一般不用这个方法。这才是我的回帖想强调的内容,即这个环境的差异。这里我表述的“主线程”,我其实想指的是进入shellcode时的那个线程环境,这当然不一定是进程的主线程,只是对于shellcode来说(这里可能有点“拟人”了,即shellcode它“置身”的那个环境),即是它原始“生命力”所依赖的环境,这与对于病毒而言的主线程有相似的地位,因此我随口用了“主线程”这个词,其实是shellcode自身意义上的“主线程”,这样的借用,应该比罗罗嗦嗦说上一堆话来解释简明得多吧,当然容易引起误解。我相信这个应该可以意会的,没想到还是出现彼此误解,是我表述的问题。
好了,在这种情况下,这时shellcode它身处的这个环境一般不是处于线程刚刚初始化的状态。
这里我想说的其实是,就是在这种环境下,我假设它不用其他方法取得CreaeThread的地址的情况下,它在这个它所初始依赖的线程环境中,就难以“首先”用这个方法来获取kernel32.dll的基址。
所以,实际上就我想表达的意思来讲,你根本没有“反驳”我,也不必“反驳”我。

当然严格来说,的确如你所说,在shellcode中先通过其他方法获取CreateThread地址,然后创建一个新线程,这个新线程的确就可以使用这种方法再去获取kernel32.dll的地址。
因此我提“先有蛋还是先有鸡”并不是说“技术上不可行”,而是因为已经有了CreateThread地址就可以不用这样的方法,所以在shellcode实际中使用这种方法的可能性比较小,所以我说“一般不用”并不是“技术上不能用”,而是有所取舍的情况下,相比之下感染型病毒就比较常用这个方法,而shellcode中就比较少用。
shellcode中现在普遍还是使用PEB的方法比较多。

我提这个的原因还有一点,就是希望楼主这篇文章能够再加入比较一下这三个方法的讨论内容。
比如我以上说的话题就是一个讨论内容,不局限于病毒,对于相像的shellcode来说,获取kernel32.dll基址也是具有同等甚至更高的重要性。
为什么说“重要性可能更高”,是因为更“别无选择”。病毒其实也可以在感染时把原程序中指向LoadLibraryA和GetProcAddress的IAT位置保存下来,或者自己在输入表中加上相应项目,感染后的程序一旦运行,这个IAT已经被系统填上了地址,病毒直接用就行了,现实中有些病毒就是这么做的,这就跟壳差不多了。病毒一般就是依靠修改原PE程序而获得其代码执行机会,而shellcode没有这样的条件,包括一般不能像病毒这样用原始IAT来获得API地址,所以对于shellcode来说,动态获取kernel32.dll的地址就显得更为重要。
上面又提到了壳,壳又怎么样呢?病毒和壳也是比较像的,有的病毒可以说是用壳的方式感染文件。
因此以上这些内容,其实不是完全无关的,楼主在讲的虽然是AntiVirus,但其实本质上涉及到PEDIY的方方面面,因此有必要拓宽了讲,让大家有个整体全局上的了解。
加百力 12 2009-4-12 08:44
27
0
支持一下xfish的病毒分析系列教程。

病毒分析与防治也是逆向工程的重要应用,是一个很好的发展方向!
feifeixiao 2009-4-12 18:42
28
0
谢谢xfish, 看完后学到好多

我以前都是用这样的方法:以10000h为增量暴力搜索MZ标志  再定位到输出表的NAME字段
                        定位指定模块的基址
imaker 2009-4-13 21:00
29
0
楼主讲的真不错,很详细,赞一个.      希望代码的注释再详细一点,虽然已经很简单了.
;///////////////////////////////////////
;++                                                   问题:这里的"++"和后面的"--",代表什么?
;
; int                                                ;调用方式
;   GetKrnlBase2(
;    void
;   )
;
; Routine Description:                               ;功能描述
;
;    获得kernel32基地址
;
; Arguments:                                        ;传入参数
;
;    (esp)          - return address
;
;
; Return Value:                                     ;返回值
;
;    eax =  krnl32 base
;--
;///////////////////////////////////////
zapline 2009-4-13 21:15
30
0
是作者都注释风格吧
xfish 5 2009-4-14 00:19
31
0
恩,我的注释风格,

;++ 代表开始位置

;-- 代表结束
cham 2009-4-14 08:05
32
0
这些都是进入kernel32.dll内部然后向上找到基址的,可以不进入其内部找到其基址么?
xfish 5 2009-4-14 10:22
33
0
第三种方法不就是吗。
cham 2009-4-14 11:08
34
0
搞错了,第三种确实是的!
nkspark 3 2009-4-14 11:14
35
0
先顶后看~~~~~
feierin 2009-4-15 13:53
36
0

楼主好强哦
学习。。。
zyfnhct 2009-4-17 13:14
37
0
学习ing~~~
xiaodai 2009-4-18 20:32
38
0
分析过kernel32.dll的朋友应该都知道kernel32.dll的块对齐值是00001000h, 并且一般DLL以1M为边界,所以我们可以通过10000h (64k) 作为跨度,这样可以增加搜索速度。

      只知道kernel32.dll的块对齐值是00001000h,一般DLL以1M为边界是什么意思,不明白,希望老大指点,然后又怎么得到64K为跨度呢?
hehuolong 2009-4-19 01:47
39
0
支持,写的很好啊
StarWing 2009-4-19 03:27
40
0
好文,就是最后一个看了一会儿才懂。原来_LIST_ENTRY是嵌入到结构体里面的,开始看了一会儿没懂~呵呵
XSJS 2009-4-19 10:57
41
0
1.加壳后呢?以及你并不知道病毒代码什么时候被执行到,那时的[ESP]不一定指向ExitThread()
2.擦除'MZ'和'PE'就能给你带来很大的麻烦,程序自己VirtualProtect()+WriteProcessMemory()一下就行,很简单。
3.或许这个是最好的方法了。。。。但是MS也没个准,说不定下次SP就给你改了。
刘国华 2009-4-26 19:40
42
0
真不错,学习了!
一转身 2009-4-29 21:53
43
0
膜拜.......
一转身 2009-5-14 14:00
44
0
mov  edx, [esp]
.Next:  
  dec  edx  ;
  xor  dx, dx  ; 减去跨度
  cmp  word [edx], "MZ"
  jz  .IsPe
  jmp  .Next
  
.IsPe:
  mov  eax, [edx+3ch]
  cmp  word [eax+edx], 'PE'
  jnz  .Next
  xchg  eax, edx   ; eax = kernel32 基地址

这段代码为什么要这样算

  dec  edx  ;
  xor  dx, dx  ; 减去跨度

为什么要 EDX 先自减一,然后再将 DX 清零

mov  eax, [edx+3ch]
为什么要 EDX+3CH呢?
hackerlx 2009-5-22 22:38
45
0
mov  eax, [edx+3ch]
为什么要 EDX+3CH呢?

edx这时候就指向文件偏移0的地址,在3ch存放的就是PE文件头的地址.

dec  edx  ;假如edx=00030000h,dec edx 后就是0002FFFFh
  xor  dx, dx  ; 减去跨度,xor dx,dx就是低四位清零,edx=00020000h
这两句代码就刚好减去00010000h.
hackerlx 2009-5-23 18:34
46
0
用RADASM编译楼主的第 一种方法的代码,不行啊,,

会产生异常,,,,,,这是什么原因
zousf 2009-5-25 14:29
47
0
虽然不太看的懂,但我还是要看,学习,把它弄懂。
hackerlx 2009-5-25 18:25
48
0
.386
                .model flat, stdcall
                option casemap :none   
                include        windows.inc
               
               

                .code
start:        mov  edx, [esp]
Next:          dec  edx  ;
         xor dx,dx;
         cmp  word ptr [edx], 5A4Dh;
        jnz Next;
  
IsPe:
         mov  eax, [edx+3ch];
         cmp  word ptr [eax+edx], 4550h;
          jnz  Next;
          xchg        eax,edx;
         
          end start

我觉得楼主第一种方法的代码写错了,,,MZ十六制代码是4D5A,而内存读写会读写成5A4D,所以不能用"MZ"判断
刘国华 2009-5-26 08:14
49
0
[QUOTE=hackerlx;630734].386
                .model flat, stdcall
                option casemap :none   
                include        windows.inc
               
               

                .code
start:        mov  edx, [esp]
Next:          dec  edx  ;
         xo...[/QUOTE]

楼上的,是你搞错了。再仔细想想在内存中的排布
hackerlx 2009-5-26 14:58
50
0
可我用OD跟过了,,,,

386
    .model flat, stdcall
    option casemap :none   
    include  windows.inc
   
   

    .code
start:  mov  edx, [esp]
Next:    dec  edx  ;
    xor dx,dx;
    cmp  word ptr [edx], 5A4Dh;
   jnz Next;
  
IsPe:
    mov  eax, [edx+3ch];
    cmp  word ptr [eax+edx], 4550h;
    jnz  Next;
    xchg  eax,edx;
   
    end start
这段代码是通过,,能得到基址,,用楼主的就不行,
游客
登录 | 注册 方可回帖
返回