原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题
第一种方法
通过线程初始化时, 获得esp堆栈指针中的ExitThread函数的地址,然后通过搜索获得kernel32.dll的基地址。
线程在被初始化的时,其堆栈指针指向ExitThread函数的地址,windows这样做是为了通过ret返回时来调用ExitThread地址。所以一般我们可以在我们主线程的起始位置(也就是
程序入口点处)通过获得堆栈指针中ExitThread函数(当然你要想创建一个线程时候获得也可以)
我们直接通过
mov edx, [esp] ;获得堆栈指针中ExitThread地址到edx寄存器。因为ExitThread地址在kernel32.dll空间中,所以我们可以通过它往上搜索来获得基地址。
分析过kernel32.dll的朋友应该都知道kernel32.dll的块对齐值是00001000h, 并且一般DLL以1M为边界,所以我们可以通过10000h (64k) 作为跨度,这样可以增加搜索速度。我们如何确定这个地址是基地址,我们都知道我们判断这个地址的前两个字节是否是"MZ",然后定位到PE头结构,然后判断是否是"PE",如果这两个都符合的话则表示我们的地址则是基地址了.
注意,程序要用汇编编写,至少我不会用VC来内联编写,应为这个方法需要在程序的一开始就获取,而用VC编写的会有启动函数,也就是mainCRTStartup这些
代码如下:
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
includelib user32.lib
includelib kernel32.lib
.const
szFormat db 'kernel32.dll address is: %x', 0dh, 0ah, 0
szFormat1 db 'LoadLibrary kernel32.dll address is: %x', 0dh, 0ah, 0
szKrnl32 db 'kernel32.dll', 0
.data?
hStdOut dd ?
dwWriteBytes dd ?
szBuffer db 1024 (?)
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取字符串长度
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
GetStrLen proc _lpString
mov edi, _lpString
or ecx, 0ffffffffh ; ecx初值等于-1
xor eax, eax
repnz scasb ; 循环扫描,每循环执行一次,ecx-1,直到出现0为止,0也会减1
; 一个公式(摘自《C++反汇编与逆向分析技术揭秘》P183):
; ecx(终值) = ecx(初值) - (Len + 1)
; ecx(终值) = -1 - (Len + 1) = -(Len + 2) : 将-1代入公式
; neg(ecx(终值)) = Len + 2 : neg为求补,ecx(终值) = -(Len + 2),求补后为正Len + 2
; not(ecx(终值)) + 1 = Len + 2
; not(ecx(终值)) = Len + 1
; Len = not(ecx(终值)) - 1
not ecx
dec ecx
mov eax, ecx
ret
GetStrLen endp
start:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 获取kernel32的基址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
mov edx, [esp]
Next:
cmp word ptr [edx], 'ZM' ; 因为读取后edx的内容