这篇是老调重弹了。看老罗之作,见原程序(asm.chm中)未完,用了很长时间补完,想想长期蒙受了各位大虾的赐教,特感激!!
.386
.model flat, stdcall
option casemap:none
include masm32includewindows.inc
GetKernelBase proto :DWORD
GetApiAddress proto :DWORD, :DWORD
.data
szMyMsg db "--= 暴力搜索内存空间获得 Api 的线性地址 =--", 13, 10, 13, 10,
"请注意:", 13, 10,
"* 本对话框的线性地址是通过暴力搜索得来 *", 13, 10, 13, 10,
"老罗的缤纷天地",13, 10, "http://www.luocong.com/", 0
szMyCaption db "老罗的病毒基础教程系列 by LC", 0
aKernel32Base dd 0
szUser32 db "user32.dll", 0
szExitProcess db "ExitProcess", 0
aExitProcess dd 0
szLoadLibraryA db "LoadLibraryA", 0
aLoadLibraryA dd 0
szGetProcAddress db "GetProcAddress", 0
aGetProcAddress dd 0
szMessageBoxA db "MessageBoxA", 0
aMessageBoxA dd 0
.code
main:
call delta
delta:
pop ebp
sub ebp, offset delta
;获得 Kernel32.dll 的基地址:
invoke GetKernelBase, [esp]
mov aKernel32Base, eax
;获得 Kernel32.dll 中的所需的 Api 的线性地址:
invoke GetApiAddress, aKernel32Base, addr szExitProcess
mov aExitProcess, eax
invoke GetApiAddress, aKernel32Base, addr szLoadLibraryA
mov aLoadLibraryA, eax
invoke GetApiAddress, aKernel32Base, addr szGetProcAddress
mov aGetProcAddress, eax
;载入 User32.dll :
push offset szUser32
call [ebp + aLoadLibraryA]
;获得 User32.dll 中的 MessageBoxA 的线性地址:
push offset szMessageBoxA
push eax
call [ebp + aGetProcAddress]
mov aMessageBoxA, eax
;呵呵,千呼万唤始出来,高兴了吧??
push MB_OK or MB_ICONINFORMATION
push offset szMyCaption
push offset szMyMsg
push NULL
call [ebp + aMessageBoxA]
;退出:
push 0
call [ebp + aExitProcess]
;**************************************************
;函数功能:查找 Kernel32.dll 的基地址
;**************************************************
GetKernelBase proc uses esi edi dwKernelRet:DWORD
LOCAL dwReturn: DWORD
mov edi, dwKernelRet ; edi = 堆栈顶
and edi, 0ffff0000h ; 用 AND 获得初始页
.while TRUE
.if word ptr [edi] == IMAGE_DOS_SIGNATURE ; 等于“MZ”吗?
mov esi, edi ; Yes, next...
add esi, [esi + IMAGE_DOS_HEADER.e_lfanew] ; 就是 esi + 3ch
.if word ptr [esi] == IMAGE_NT_SIGNATURE ; 等于“PE”吗?
mov dwReturn, edi ; Yes, we got it.
.break
.endif
.endif
;以下等同于sub edi, 010000h,即每次减少64k:
dec edi
xor di, di
.break .if edi < 070000000h ; 基地址一般不可能小于70000000h
.endw
mov eax, dwReturn
ret
GetKernelBase endp
;**********************************************************************
;函数功能:从内存中 Kernel32.dll 的导出表中获取某个 API 的入口地址
;**********************************************************************
GetApiAddress proc uses ecx ebx edx esi edi hModule:DWORD, szApiName:DWORD
LOCAL dwReturn: DWORD
LOCAL dwApiLength: DWORD
mov dwReturn, 0
;计算 API 字符串的长度(带尾部的0)
mov esi, szApiName
mov edx, esi
Continue_Searching_Null:
cmp byte ptr [esi], 0 ; 是否为 Null-terminated char ?
jz We_Got_The_Length ; Yeah, we got it. :)
inc esi ; No, continue searching.
jmp Continue_Searching_Null ; searching.......
We_Got_The_Length:
inc esi ; 呵呵, 别忘了还有最后一个“0”的长度。
sub esi, edx ; esi = API Name size
mov dwApiLength, esi ; dwApiLength = API Name size
;从 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 edi,szApiName
mov ecx,[esi].AddressOfNameOrdinals
add ecx,hModule;取得AddressOfNameOrdinals数组的指针
;invoke RVAToFileMap,hModule,[esi].AddressOfNames
mov ebx,[esi].AddressOfNames
add ebx,hModule;取得AddressOfNames数组的指针,特要注意AddressOfNames为指向指针的指针数组(个人之见),且都为RVA
Continue_look:
mov eax,[ebx]
add eax,hModule
push ebx
mov ebx,eax
push edi
push ecx
mov ecx,dwApiLength;字符串的长度
Continue:
mov al,[edi]
mov ah,[ebx]
xor al,ah;字符串的比较
jnz again
dec ecx
inc edi
inc ebx
jcxz found
jmp Continue
again:
pop ecx
pop edi
pop ebx
add ebx,4 ;ebx指向AddressOfNames名的数组,值为双字型,所以加4
add ecx,2;ecx指向序数数组,值是字类型,所以加2
jmp Continue_look
found:
pop ecx
pop edi
pop ebx
mov ebx,ecx
mov dx,[ebx] ;ebx指向序数数组,值是字类型
movzx edx,dx;因此将其转换成双字
mov ebx,[esi].AddressOfFunctions
add ebx,hModule;取得AddressOfFunctions的指针
shl edx, 2:索引乘以4 (AddressOfFunctions 数组中每个元素都是4字节大小)
add edx,ebx;然后加上数组首地址
mov eax,[edx];eax就是所要函数的RVA了
add eax,hModule
ret
GetApiAddress endp
end main