首页
社区
课程
招聘
[分享]PE病毒学习笔记——搜索API
发表于: 2007-10-1 10:13 29672

[分享]PE病毒学习笔记——搜索API

2007-10-1 10:13
29672

这几天学习了一下古老的PE病毒原理,不过刚开始没多久,就打算一心一意复习考研,不再搞别的事情分心了。本来准备发不止一个学习笔记,其中包括感染技术,传播技术等等,不过都没时间了,只好先把这个发出来再说吧。

对于Ring3的PE病毒来说,我想最重要的莫过于搜索API地址了,因为所有的所有别的事情,包括感染、注入、获取机器信息、升级到Ring0、传播甚至破坏,没有哪一个不会用到API的,只要找到了API地址,那么任何一个普通程序能做的事,病毒都能做到,甚至包括WinFlash干的事。

kernel32.dll里有一个函数:GetProcAddress,只要给定模块handle和API的名字,就可以得到API的地址,很不错的搜索函数。当然,GetProcAddress也是一个API,所以我们自己的搜索代码不是没有用武之地的,不过简单的来说,我们只用搜索出GetProcAddress就可以了。

在搜索API的方法上,病毒和蠕虫有点点区别。我现在还没有听说过可以攻击各种各样完全不同漏洞的通用蠕虫,当然,每个漏洞一般对应一个特定的程序的特定版本;我也没听说过只感染一个特定程序的病毒。针对特定漏洞的蠕虫所处的是一个特定的环境,这个环境是可预知的,所以,蠕虫编写者可以直接调试漏洞程序,分析漏洞程序导入表中GetProcAddress地址的位置,然后直接hardcode。那么如果漏洞程序升级了,导入表变了呢?放心,也别指望新版本里漏洞还给你留着。

病毒所面对的就是比较极端的环境了,完全可变的通用的环境,唯一知道就是——这是一个PE程序(这是感染部分选择感染对象的职责,如果你硬要感染word文档我也没话说)。还有呢?有!这个程序至少加载了kernel32模块——这是无条件的!否则这个进程怎么被创建的呢?主线程怎么创建的呢?

“正如你所知道的,当我们在执行一个应用程序的时候,代码是从KERNEL32 "call"一部分代
码的(也就像KERNEL 调用我们的代码一样)。而且,如果你还记得的话,当一个call 调用之
后,返回的地址是在堆栈里(即,在由ESP 所指定的内存地址里的)的。”
——被引用过无数次的一个常识

其实这个call就是CreateProcess发出的,这很好理解。那么,有了这个处于kernel32内部的地址,寻找kernel32的基址应该不成问题,kernel32肯定是按页加载和对齐的,kernel32的基址肯定是一个页首。我们可以一页一页向前搜索kernel32的MZ头。

GetK32.asm
;---------------------------
; input:
; ESI = near page addr
; EAX = limit checkpagenum
;
; output:
; EAX = ImageBase of kernel32
;
; used reg:
; EAX,ESI,EDI
;---------------------------

GetK32 proc
    and eax,eax
    jz _GetK32_Fail

_GetK32_Start:
    cmp word ptr [esi],"ZM"
    jnz _GetK32_Loop
    mov edi,[esi+3Ch]
    add edi,esi
    cmp dword ptr [edi],"EP"
    jz _GetK32_Done

_GetK32_Loop:
    sub esi,10000h
    dec eax
    jnz _GetK32_Start

_GetK32_Fail:
    mov esi,7c800000h

_GetK32_Done:
    xchg eax,esi
    ret
GetK32 endp

仅仅判断"MZ"还不够,有可能会是碰上的,所以我们还要再追踪下"PE",当然像上面那样直接找可能会访问到非法区域。当然,可以添加SEH来避免错误被显示出来。我们还需要一个搜索页数的上限,避免无限制搜索下去。其实这个搜索是很快的,因为这个被调用函数是CreateProcess,具体原因后面我会提到。

好,现在找到了kernel32的基址,我们把它存在kernel这个变量里。

kernel dd 0

mov esi,[esp]
and esi,0ffff0000h
mov eax,100
call GetK32
mov kernel,eax

下面我们就要搜索kernel32的导出表了,熟悉PE文件结构的人肯定不会陌生,在导出表的名字表里一个一个比较,就这么简单。不过我从来都不喜欢字符串比较函数,包括C标准库里面的字符串函数我都不喜欢,我总想要更直观的函数,因为我们只需要看看两个字符串是否相等,当然,比较下他们的CRC32就可以了,刚好是一个双字的比较,一条指令。

首先,我们需要一个求CRC32的代码,我在Billy Belceb 病毒编写教程---Win32 篇看到了一个
;----------------------------------------------
; input:
; ESI = Offset where code to calculate begins........volatile
; EDI = Size of that code............................volatile
;
; output:
; EAX = CRC32 of given code
;
; used reg
; EAX,EBX,ECX,EDX,ESI,EDI
;----------------------------------------------

CRC32 proc
    cld
    xor ecx,ecx   
    dec ecx         
    mov edx,ecx     
   
NextByteCRC:
    xor eax,eax     
    xor ebx,ebx     
    lodsb           
    xor al,cl      
    mov cl,ch      
    mov ch,dl      
    mov dl,dh      
    mov dh,8        
   
NextBitCRC:
    shr bx,1        
    rcr ax,1      
    jnc NoCRC
    xor ax,08320h
    xor bx,0EDB8h
   
NoCRC:
    dec dh
    jnz NextBitCRC
    xor ecx,eax
    xor edx,ebx
    dec edi         ;---------------- 1 byte less
    jnz NextByteCRC
    not edx
    not ecx
    mov eax,edx
    rol eax,16
    mov ax,cx
    ret
CRC32 endp

我想,有了kernel32的基址,得到kernel32导出表的名字表和地址表还有序号表,不是问题吧。
;--------------------------------------
; input:
; kernel = ImageBase of kernel32.dll
;
; output:
; ESI = OrdinalTableVA----VA
; EBX = NameTableVA-------VA
; EDI = AddressTableVA----VA
;
; used reg:
; EAX,EBX,ESI,EDI
;--------------------------------------

GetAPI_Init proc
    mov esi,3ch
    add esi,kernel                        ;------------ Get PE header of KERNEL32
    lodsd
    add eax,kernel
    mov esi,[eax+78h]                   ;------------ Get a RAV pointer to its Export Table
    add esi,1ch                         ;------------ RAV pointer to address table
    add esi,kernel                        ;------------ Turn RVA to VA

    mov edi,[esi]                        ;------------ Pointer to the address table
    add edi,kernel
    mov ebx,[esi+4]
    add ebx,kernel
    mov eax,[esi+8]
    add eax,kernel
    mov esi,eax
    ret
GetAPI_Init endp

上面这个部分只运行一次,我不喜欢读写内存,在我印象里,读写内存是很慢的事,多用寄存器才是我喜欢的事。

;--------------------------------------
; input:
; EDX = CRC32 of the API ASCIIz name........volatile
; EBP = Length of API name
; ESI = OrdinalTableVA----VA
; EBX = NameTableVA-------VA
; EDI = AddressTableVA----VA
; kernel = ImageBase of kernel32.dll
;
; output:
; EAX = API address
;
; used reg
; EAX,EBX,ECX,EDX,ESI,EDI,EBP
;--------------------------------------

GetAPI_ET_CRC32 proc
    push edi
    push esi
    xor ecx,ecx
p1: mov edi,[ebx+ecx*4]
    add edi,kernel
    mov esi,edi
    xor al,al
lp: scasb
    jnz lp
    sub edi,esi
    inc ecx
    cmp ebp,edi
    jnz p1
    push ecx
    push edx
    push ebx
    call CRC32
    pop ebx
    pop edx
    pop ecx
    cmp edx,eax
    jnz p1
    dec ecx
    pop esi
    pop edi
    xor edx,edx
    mov dx,word ptr [ecx*2+esi]
    mov eax,[edx*4+edi]
    add eax,kernel
    ret
GetAPI_ET_CRC32 endp

把你要找的API的CRC32值放在edx里,名字长度(包括结尾的0)放在ebp里,eax输出的就是它的地址,不错吧。而且这个过程不破坏用于存放3个表地址的3个寄存器EBX,ESI,EDI,所以一次Init以后可以反复调用,直到把你自己构造的“导入表”(我习惯这么叫,叫跳转表也不错)添满为止。

不过我只要一个函数的地址,就是GetProcAddress,15字节,CRC32:0FFC97C1Fh
如果要再加一个的话,那就是LoadLibraryA,13字节,CRC32:04134D1ADh

写个实际的程序试试
test.asm

.386
.model flat,stdcall
option casemap:none

.data
kernel dd 0   
GETPROCCRC equ 0FFC97C1Fh
LORDDLLCRC equ 04134D1ADh
user db "user32.dll",0
mess db "MessageBoxA",0
userhd dd 0
getprochd dd 0

.code

GetAPI_Init proc
......
GetAPI_Init endp

CRC32 proc
......
CRC32 endp

GetAPI_ET_CRC32 proc
......
GetAPI_ET_CRC32 endp

GetK32 proc
......
GetK32 endp

start:
        mov esi,[esp]
        and esi,0ffff0000h
        mov eax,100
        call GetK32
        mov kernel,eax

        call GetAPI_Init

        mov edx,LORDDLLCRC
        mov ebp,13
        call GetAPI_ET_CRC32

        push offset user
        call eax
        mov userhd,eax

        mov edx,GETPROCCRC
        mov ebp,15
        call GetAPI_ET_CRC32

        mov getprochd,eax

        push offset mess
        mov eax,userhd
        push eax
        mov eax,getprochd
        call eax
        push 0
        push offset mess
        push offset mess
        push 0
        call eax

over:        mov edx,040F57181h
        mov ebp,12
        call GetAPI_ET_CRC32
        push 0
        call eax
end start

用MASM32编译连接
ml /c /coff test.asm
link /SUBSYSTEM:WINDOWS test.obj

我没有导入任何头文件和库文件,不过这个程序可以跑,这也就验证了我们的代码的功能。

最后再提几点:

1,我这程序显式地用到什么函数?从字符串来看,只能看到"MessageBoxA"!这就是CRC32搜索法的好处,GetProcAddress,LoadLibraryA,还有一个:ExitProcess,CRC32:040F57181h,直接在代码里看不出来的。

2,这种方法可以用在蠕虫里么?严格的来说不可以,因为看看我们搜索kernel32基址的函数,我们利用kernel32内的CreateProcess函数call我们程序本身的代码并将返回到CreateProcess内部的地址留在[esp]的特点来搜索kernel32头,因为病毒代码总是在宿主程序运行前执行,也就是处在一个进程刚刚创建的状态。而蠕虫所利用的漏洞一般在很复杂的、不知道有多少层的子函数里,这个时候的esp早不知道指向哪里去了。

3,为什么我说从CreateProcess(准确来说CreateProcessA或CreateProcessW)搜索kernel32头几步就完成了呢?因为我在调试时发现,kernel32导出表里名字表内的函数名排列是按照字典序排列的,而CreateProcess开头是C,是不是很快呢?这个特点也可以让我们写出更高效的kernel32的API搜索法,比如二分排序等等……(学计算机的,就总免不了在算法上较劲),当然实用性不高,呵呵

这篇文章不敢妄称原创,虽然除了算CRC32的代码外其他代码都是我自己写的,但是思想是别人的,我只是一个学习者,所以我这叫学习笔记,而且是[分享]而不是[原创]。这些实践大大加深了我对PE文件格式的理解,看来动手才是提高的唯一办法。

每次来看雪都有一种很亲切的感觉,大家都很热情,人气很高,讨论的内容也很有技术含量,不像别的论坛,一旦问题深入一点,就卖关子故弄玄虚甚至开口要价,好像不这样就显不出自己是高手一样。我想管理者与主持者的对技术的热情和无私,对培养后继者,端正论坛风气有很大益处,只有这样看雪才会继续繁荣下去。

参考文献:《Billy Belceb 病毒编写教程---Win32 篇》


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (43)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
;
; xlib v1.30                                           (x) 2007
; ~~~~~~~~~~
; coded by forgot & heXer
;
; example
; ~~~~~~~
; include               xlib.inc
;
;                       initX   XFL_MAX, "user32", "ntdll"
;
;                       getX    KiUserExceptionDispatcher
;
;                       push    eax
;                       push    ofs buf
;                       push    ofs fmt
;                       callX   wsprintfA
;                       add     esp, 3*4
;
;                       push    0
;                       push    0
;                       push    ofs buf
;                       push    0
;                       callX   MessageBoxA
;
;                       callX   ExitProcess

XFL_GET                 equ     1                               ; use getX macro
XFL_DBG                 equ     2                               ; use push calls
XFL_ILN                 equ     4                               ; initX can be run through
XFL_MIN                 equ     0                               ; use minimum setting
XFL_MAX                 equ     7                               ; use maximum setting

initX                   macro   fl:req, dll:vararg
                        local   hash
                        local   stub, ntport, getkrnl
                        local   getdll, restart, chknext
                        local   gethash, reached, alone

  hash                  macro   s
                        h = 0
                        forc c, <s>
                          h = ((h shl 7) and 0ffffffffh) or (h shr (32-7))
                          h = h xor "&c"
                        endm
                        exitm <h>
                        endm

  callX                 macro   x
                        if (fl and XFL_DBG)
                          push  hash(x)
                          call  stub
                        else
                          call  stub
                          dd    hash(x)
                        endif
                        endm

  getX                  macro   x
                        if (fl and XFL_GET)
                          if (fl and XFL_DBG)
                            push  hash(x)
                            call  stub+1
                          else
                            call  stub+1
                            dd    hash(x)
                          endif
                        else
                          error
                        endif
                        endm

                        if (fl and XFL_ILN)
                          jmp   alone
                        endif
  stub:
                        if (fl and XFL_GET)
                          test  al, 0f9h
                        endif

                        if (fl and XFL_DBG)
                          pop   edx
                          pop   eax
                          push  edx
                        else
                          xchg  esi, [esp]
                          lodsd
                          xchg  esi, [esp]
                        endif

                        if (fl and XFL_GET)
                          pushfd
                          pushad
                        else
                          pushad
                        endif

                        push    34h
                        pop     edx
                        db      64h
                        mov     eax, [edx-04h]
                        test    eax, eax
                        jns     ntport
                        add     edx, [eax+edx]
                        lea     eax, [eax+7ch]
                        jmp     getkrnl
  ntport:               mov     eax, [eax+0ch]
                        mov     esi, [eax+1ch]
                        lodsd
  getkrnl:              mov     eax, [eax+08h]

                        call    getdll
                        for     c, <dll>
                          db    c, 0
                        endm
  getdll:               pop     edi

  restart:              xchg    ebx, eax
                        mov     eax, [ebx+3ch]
                        mov     ebp, [ebx+eax+78h]
                        add     ebp, ebx

                        mov     ecx, [ebp+18h]
  chknext:              lea     esi, [ebx+ecx*4]
                        add     esi, [ebp+20h]
                        lodsd

                        cdq
  gethash:              rol     edx, 7
                        xor     dl, [ebx+eax]
                        inc     eax
                        cmp     bl, [ebx+eax]
                        jne     gethash

                        lea     esi, [esp+7*4]
                        sub     edx, [esi]
                        jz      reached
                        dec     ecx
                        jns     chknext

                        push    edi
                        callX   LoadLibraryA

                        repnz   scasb
                        jmp     restart

  reached:              mov     edx, [ebp+24h]
                        add     edx, ebx
                        movzx   edx, word ptr [edx+ecx*2]
                        mov     eax, [ebp+1ch]
                        add     eax, ebx
                        add     ebx, [eax+edx*4]
                        mov     [esi], ebx

                        if (fl and XFL_GET)
                          popad
                          popfd
                          push  eax
                          jnc   $+3
                          pop   eax
                          retn
                        else
                          popad
                          jmp   eax
                        endif
  alone:
                        endm

2007-10-1 10:22
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
完全看不懂
留贴  
以后在来学习
2007-10-1 11:23
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
用crc來判斷字符串,不知道你是否實際用過?大小寫怎麼區分?????
2007-10-1 11:46
0
雪    币: 8
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我什么都不懂,不过API的大小写可不要乱改!!所以Hash值一定是一定的,呵呵。
2007-10-1 13:47
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
国际标准已经定义了8、12、16和32比特生成多项式。32比特的标准CRC32被大量的链路级IEEE协议采用。每个CRC标准都能检测小于r+1比特的突发错误(对于CRC32来说就是33比特),而且在合适的假设下,长度大于r+1比特的突发差错被检测到的概率为1-0.5^r。每个CRC标准都能检测任何奇数个比特差错。

比如以太网帧使用的就是4个字节CRC32检测字段判断帧是否出错,这个运算过程还有整个协议栈集成在了我们的以太网卡里。

以太网帧最长可以有1500字节,如果你相信你上网的网卡不会给你传送错误的数据的话,你也就应该相信CRC32肯定可以检测名字长十几二十个字节的API的不同,至少,没有1500字节长的API名字吧!

当然,用任何Hash函数来计算都是可以的,只是CRC32非常简单,很多人都会计算,所以这里用CRC32。而且我不懂这和ASCII字符字母大小写有什么关系,比如'A'和'a'的ASCII码不是有一个比特不同吗,凭什么CRC32检测不出来呢?
2007-10-1 14:55
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
启发很大,谢谢你的慷慨大方。
2007-10-1 18:23
0
雪    币: 214
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
什么东西........................?
2007-10-1 20:17
0
雪    币: 257
活跃值: (56)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
已经搜藏,xiexei1!
2007-10-1 20:31
0
雪    币: 452
活跃值: (72)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
10
iczelion的PE教程里面对export table有很详细的描述啊,如果自己写一个GetProcAddress的话,这样不仅速度提高了,而且,如果菜用比较函数名hash的方法,可以避免在程序当中出现明文,增加跟踪的难度。

还有,搜索dll的基址,不用暴力搜索的话也可以,在PEB结构里面可以找到的。
2007-10-1 21:05
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
11
你说得很对,我的代码里的GetAPI_Init和GetAPI_ET_CRC32就类似于一个GetProcAddress,唯一就是把那个kernel变量换掉。提高搜索速度可以有很多种方法,但是无一例外都是要访问导出表里名字表的,这个是关键。我当初写GetAPI_Init和GetAPI_ET_CRC32的时候就是对照PE格式那张图写的(对PE结构不够烂熟,羞愧)。

比较函数名hash和比较函数名CRC32其实是一个意思,我的想法是哪个代码越少,哪个越高效选哪个。我作为新手,写自己熟悉的CRC还是比较容易理解和上手。

你提到PEB,这个想法很不错,这个板块里有篇离我的帖子位移很近的描述PEB的帖子,我也看了,很详细很不错,如果有个图就更好了,毕竟指针看多了会头晕的。但是我想提到的是,暴力搜索其实在这里,经过我自己调试(WinXP sp2),也就只是向前搜索了页。原因其实很简单,kernel32导出表名字表里函数名是按照字典序排的,CreateProcess开头是C,离kernel32头很近。蠕虫里也可以用PEB的办法,不过,似乎一般都使用漏洞程序自己的导入表,因为漏洞程序本身是一个已知的可调试的特定环境。

很高兴大家能有广阔的思维,我想这就是看雪和别的一些论坛不同的地方,别的地方可能大家就只是想拿了现成的东西就走人,而看雪这里大家都积极思考——可不可以更好,这是一种进步的力量,也是让看雪充满生命力的原因。我的代码说实话很初级,不过关键不在于我的实现代码(只是说明问题的一种方法),而在于我想解决的问题。如果这点初级的内容,能够引发大家更广阔的思考和实践,甚至都不再局限于我原本打算解决的问题本身了,那我的帖子就算是物超所值,抛砖引玉了。

欢迎大家继续积极思考!!
2007-10-1 22:07
0
雪    币: 452
活跃值: (72)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
12
补充一个小技巧
我用的HASH函数比CRC简单,所以我编程的时候是写了一个HASH的宏,用的时候直接写:
HASH("MessageBoxA"),就能返回一个常量,编程的时候就不用自己算hash了
如果方便的话,你可以写一个CRC的宏,然后CRC("MessageBoxA")
2007-10-1 22:16
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
13
讨论了这么多,难道没有人看到2楼。
2007-10-1 22:34
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
14
当然看到了,所以我在6楼就提到,用任何hash和用CRC是一样的道理。

没错,定义成宏让编译器来算——好办法,学了一招。
2007-10-1 22:42
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
15
看到PEB了吗,扔掉那个蹩脚的搜索吧
2007-10-1 23:39
0
雪    币: 1829
活跃值: (1372)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
16
鼓励好文,感谢2楼提供的可重用宏
2007-10-2 03:16
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
17
那确实,PEB确实比从CreateProcess向前搜索好用,给人感觉舒畅自然。
2007-10-2 10:33
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
已经搜藏,xiexei1!
2007-10-2 13:04
0
雪    币: 10729
活跃值: (3257)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
19
GetAPI_ET_CRC32 proc
    push edi
    push esi
    xor ecx,ecx
p1: mov edi,[ebx+ecx*4]
    add edi,kernel
    mov esi,edi
    xor al,al
lp: scasb
    jnz lp
    sub edi,esi
    inc ecx
    cmp ebp,edi
    jnz p1
    push ecx
    push edx
    push ebx
    call CRC32
    pop ebx
    pop edx
    pop ecx
    cmp edx,eax
    jnz p1
    dec ecx
    pop esi
    pop edi
    xor edx,edx
    mov dx,word ptr [ecx*2+esi]
    mov eax,[edx*4+edi]
    add eax,kernel
    ret
GetAPI_ET_CRC32 endp

这里:
xor edx,edx
    mov dx,word ptr [ecx*2+esi]
    mov eax,[edx*4+edi]

此时 ecx就是rva表里 要找的api的rva的个数偏移 edi指向rva表 开始位置
是不是可以将此三句改为:
mov eax,[ecx*4+edi]

?

另外

mov dx,word ptr [ecx*2+esi]
    mov eax,[edx*4+edi]
这两句不理解  esi :OrdinalTableVA 和ecx*2 ?
对pe格式学得不熟
2007-10-2 17:30
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
20
[QUOTE=netwind;366582]GetAPI_ET_CRC32 proc
    push edi
    push esi
    xor ecx,ecx
p1: mov edi,[ebx+ecx*4]
    add edi,kernel
    mov esi,edi
    xor al,al
lp: sc...[/QUOTE]

你说得没错,其实我第一个版本的代码就像你说的那样,直接通过名字表里的序数在地址表索引。所以我一开始也就没有保存序号表的地址。这在kernel32里是没有一点问题的!在kernel32里名字的序号实际就是和地址的序号相对应的,没必要通过序号表,我想是因为kernel32里没有一个函数多个名字的原因吧。可是严格来说,名字表和地址表不是一一对应的,因为存在一个函数被导出多个不同名字的情况,或者可能还有别的原因。所以PE结构的情况是:

名字表的序数对应序号表相应序数的相应项,序号表的项里面的值就是地址表相应项的序数。

序号表每项是2个字节,也就是16位,这与名字表和地址表每项4个字节32位不同。

比如在名字表第X项所指的字符串就是你想要的函数,那么名字表第x项距离名字表头的距离应该是X*4字节。而序号表的第X项(距离序号表头为X*2字节)存的就是这个函数的序号,也就是[X*2+序号表首地址]。把这个序号提取出来,假设为X',那么,地址表的第X'项(距离地址表头为X*4字节)存的就是你要的函数的地址。也就是[X'*4+地址表首地址]

在kernel32里这个X=X',也就是序号表里第X项的值就是X本身。可是在别的地方这就不一定了。

如果对PE不熟的话,记住一点:任何从文件内部提取的地址都是RVA,必须加上文件加载的基地址ImageBase后才能使用。
2007-10-2 19:35
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
21
我又稍微了解了下PEB的资料,我发现PEB搜索模块基址这个方法只能在Win NT/2000/XP这个系列里跑,似乎在Win9x/Me里面不行。当然,也许从实际说起来在中国根本不用考虑跑Win9x/Me系列的机子的问题,没错,一个连CreateRemoteThread函数都没有的系统,在中国已经濒临绝迹。

但是,如果硬要兼容Win9x/Me系列的话,似乎可以用展开SEH链的办法,遍历到SEH链尾的KERNEL32!_except_handler3函数,因为这个函数在kernel32.dll内部,然后用我那个从CreateProcess向前搜索kernel32基址的办法,从这个Windows默认分配的SEH程序向前搜索kernel32基址。

这个办法也可以适用于蠕虫。很麻烦的方法,不过相比PEB,似乎唯一的好处就是可以肯定(根据MS公开发布的详细的文档)能在Win9x/Me下跑……
2007-10-3 15:47
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
大牛崇拜一下
2007-10-3 19:45
0
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
这个要看看的,不要沉了
2007-10-4 17:38
0
雪    币: 235
活跃值: (17)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
24
是CVC的shenck吗
2007-10-7 09:44
0
雪    币: 424
活跃值: (10)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
25
.............................嘿嘿嘿
call nStart

nStart:
  pop ebp
  sub ebp, offset nStart
2007-10-7 20:44
0
游客
登录 | 注册 方可回帖
返回
//