WindowsAPI函数地址的获取.docx
《WindowsAPI函数地址的获取.docx》由会员分享,可在线阅读,更多相关《WindowsAPI函数地址的获取.docx(12页珍藏版)》请在冰豆网上搜索。
WindowsAPI函数地址的获取
API函数地址的获取
byHume/
前言:
病毒没有什么恐怖的,也并非象想像中的复杂,病毒就象是双刃剑,歹意利用就会带来恶果,我本人
关于此类行为深恶痛绝!
咱们研究不是为了破坏而是为了知己知彼,另外病毒中确实也有很多精湛的
技术值得咱们学习,这才是咱们的目的所在,我绝没有指令人犯法的用意。
一点理论:
这是一个老题目了,若是咱们不用任何引入库,可否在程序中挪用api函数?
固然能够!
方式有很多,你
可能早就明白了,若是你已经了解了,就此打住,这是为还不了解这一技术而写.
另外这也是病毒必用的技术之一,若是你对病毒技术感爱好,接着看下去.
那个地址假设你了解PE的大体结构,若是还不懂,找点资料来看看,处处都是呦.
在几乎每一个病毒的开头都用下面的语句:
calldelta
delta:
popebp
subebp,offsetdelta
movdwordptr[ebp+offsetappBase],ebp
让咱们考虑一下程序的执行情形,若是下面的代码由编译器自动编译连接,那么程序执行的基址一
般是400000h,若是是在Nt下执行,那么基址可能不同,比如从100000h开始,不用担忧,操作系统的Loader
会自动为你重定位.可是那个地址停下来让咱们看一下,若是你想要把这段代码附加到其他程序的后面并想
让其正确执行的话,就不是那么简单了,因为你的代码可能要从555588h处开始执行,而在没有取得宿主
程序许可的情形下期望操作系统自动为你修正偏移错误是不可能的,既然有超级的目的,就得费点力气
自己弄定重定位.而上面的代码确实是第一取得eip指针,也是delta在程序执行时的实际偏移,然后减掉
代码头到delta的偏移从而取得你的代码的真正基址,后面关于偏移的操作都应以那个真正的偏移为准.
这确实是你上面看到的.若是不明白,就认真想一下,nothingdifficult!
下面的例子演示了这一点,并
没有全数重定位,因为这只是技术演示.
此刻回到本文的正式内容,要想取得api的地址,得第一取得诸如,的基址,
然后再找到真正的函数地址.如何取得基址和函数地址呢呢?
有几种方式
1)搜寻宿主的引入表取得GetModuleHandleA函数和GetProcAddress的地址,然后通过他返回系统
dll的基址.因为很多程序都要利用这两个函数,因此在某些情形下是可行的,若是宿主没有利用
GetProcAddress,那你就不能不搜寻Export表了.
2)直接取得的基址,然后再搜寻Export表取得GetProcAddress和LoadLibraryA的地
址,然后咱们就能够取得任何想挪用的函数地址.
3)硬编码挪用函数,比如在9X下GetModuleHandleA的地址一样是BFF7****.
第一种和第三种方式存在兼容性的问题,假设宿主没有挪用GetModuleHandleA,那么你就不能取得基
址,别的就更别想了...硬编码问题更大,操作系统不同那么不能运行了,比如9X下可能在有些运算机上正常,
但确信不能在Nt/2K下运行...
第二种方式兼容性比较好,因此作以介绍.
一点背景:
在PELoader装入咱们的程序启动后堆栈顶的地址是是程序的返回地址,确信在Kernel中!
因此咱们能够取得那个地址,然后向低地址缩减验证一直到找到模块的起始地址,验证条件为PE头不能
大于4096bytes,PEheader的ImageBase值应该和当前指针相等,嘿嘿,简单吧,而且兼容性还不错.
要取得Api的地址第一要取得GetModuleHandle,LoadLibraryA,GetProcAddress的地址,这是通
点注释,没有优化代码,是为了便于明白得.
好,这一部份终止了!
代码举例:
这是一个例子,没有效任何预引入函数,加了一条invokeInitCommonControls是为了在2K下也能正常
运行,不然不能在2K下不加载!
程序取得MessageBoxA的地址然后显示一个消息框,目的在于演示,重要部份加了注释,专门好明白.
注意连接时加入/section:
.text,RWE选项。
.586
.modelflat,stdcall
optioncasemap:
none;casesensitive
includec:
\hd\
includec:
\hd\
;;--------------
GetApiAproto:
DWORD,:
DWORD
;;--------------
.CODE
appBasedd?
k32Basedd?
lpApiAddrslabelnear
ddoffsetsGetModuleHandle
ddoffsetsGetProcAddress
ddoffsetsExitProcess
ddoffsetsLoadLibrary
dd0
sGetModuleHandledb"GetModuleHandleA",0
sGetProcAddressdb"GetProcAddress",0
sExitProcessdb"ExitProcess",0
sLoadLibrarydb"LoadLibraryA",0
sMessageBoxAdb"MessageBoxA",0
aGetModuleHandledd0
aGetProcAddressdd0
aExitProcessdd0
aLoadLibrarydd0
aMessageBoxAdd0
u32db"",0
k32db"",0
sztitdb"ByHume,2002",0
szMsg0db"Hey,HopeUenjoyit!
",0
;;-----------------------------------------
__Start:
invokeInitCommonControls
calldelta
delta:
popebp;取得delta地址
subebp,offsetdelta;因为在其他程序中基址可能不是默许的因此需要重定位
movdwordptr[ebp+offsetappBase],ebp;呵呵认真想一想
movecx,[esp];返回地址
xoredx,edx
getK32Base:
dececx;逐字节比较验证
movdx,wordptr[ecx+];确实是ecx+3ch
testdx,0f000h;DosHeader+stub不可能太大,超过4096byte
jnzgetK32Base;加速查验
cmpecx,dwordptr[ecx+edx+jnzgetK32Base;看Image_Base值是不是等于ecx即模块起始值,
mov[ebp+offsetk32Base],ecx;若是是,就以为找到kernel32的Base值
leaedi,[ebp+offsetaGetModuleHandle]
leaesi,[ebp+offsetlpApiAddrs]
lop_get:
lodsd
cmpeax,0
jzEnd_Get
pusheax
pushdwordptr[ebp+offsetk32Base]
callGetApiA;获取API地址
stosd
jmplop_get
End_Get:
pushoffsetu32
calldwordptr[ebp+offsetaLoadLibrary];在程序空间加载
leaEDX,[EBP+OFFSETsMessageBoxA]
pushedx
pusheax
moveax,dwordptr[ebp+aGetProcAddress];用GetProcAddress取得MessageBoxA的地址
calleax;挪用GetProcAddress
push40h+1000h;style
pushoffsetsztit;title
pushoffsetszMsg0;消息内容
push0
calleax;一个消息框产生了...嘿嘿
;有理由为此快乐吧,因为咱们没有预先引入
@@:
;这些函数
push0
call[ebp+aExitProcess]
;-----------------------------------------
K32_api_retrieveprocBase:
DWORD,sApi:
DWORD
pushedx;保留edx
xoreax,eax;现在esi=sApi
Next_Api:
;edi=AddressOfNames
movesi,sApi
xoredx,edx
decedx
Match_Api_name:
movbl,byteptr[esi]
incesi
cmpbl,0
jzfoundit
incedx
pusheax
moveax,[edi+eax*4];AddressOfNames的指针,递增
addeax,Base;注意是RVA,必然要加Base值
cmpbl,byteptr[eax+edx];逐字符比较
popeax
jzMatch_Api_name;继续搜寻
inceax;不匹配,下一个api
loopNext_Api
jmpno_exist;假设全数搜完,即未存在
foundit:
popedx;edx=AddressOfNameOrdinals
shleax,1;*2取得AddressOfNameOrdinals的指针
movzxeax,wordptr[edx+eax];eax返回指向AddressOfFunctions的指针
ret
no_exist:
popedx
xoreax,eax
ret
K32_api_retrieveendp
;-----------------------------------------
GetApiAprocBase:
DWORD,sApi:
DWORD
localADDRofFun:
DWORD
pushad
movedi,Base
addedi,
movedi,[edi];此刻edi=offPE_HEADER
addedi,Base;取得IMAGE_NT_HEADERS的偏移
movebx,edi
movedi,[edi+
addedi,Base;取得edi=IMAGE_EXPORT_DIRECTORY入口
moveax,[edi+1ch];AddressOfFunctions的地址
addeax,Base
movADDRofFun,eax
;ecx=NumberOfNames
movecx,[edi+18h]
movedx,[edi+24h]
addedx,Base;edx=AddressOfNameOrdinals
movedi,[edi+20h]
addedi,Base;edi=AddressOfNames
invokeK32_api_retrieve,Base,sApi
movebx,ADDRofFun
shleax,2;要*4才取得偏移
addeax,ebx
moveax,[eax]
addeax,Base;加上Base!
mov[esp+7*4],eax;eax返回api地址
popad
ret
GetApiAendp
;-----------------------------------------
END__Start
;------------------------------------------Endall
API函数地址的获取
(2)
byHume/
一点理论
API地址的获取方式很多,在API函数地址的获取
(1)中介绍的方式事实上是有专门大缺点的,要求
病毒定位的代码必需在WindowsPELoader加载程序后程序未改变esp的值之前执行,也确实是说
若是程序进行了堆栈操作那么咱们无法保证[esp]中的地址还位于Kernel32中了,因此若是为了
知足多态,EPO等对Vir代码入口的不同要求,必需寻觅其他良方。
事实上,若是你了解Windows的SEH机制,就会专门快找到一种简单的方式。
那确实是遍历SEH链,
在链中查找prev成员等于0xFFFFFFFF的EXCEPTION_REGISTER结构(具体参见杂志的SEH介绍),
该结构中handler值是所谓的系统异样处置例程,他老是位于中!
依照这一特性,
就能够够查找在内存中的基地址了。
利用遍历SEH链表取得API的步骤如下:
a)遍历SEH链,找到prev等于0xFFFFFFFF的EXCEPTION_REGISTER结构,获取handler值;
b)用类似方式1的方式查找的基地址;
c)搜索的IAT,获取GetProcAddress的地址;
d)以GetProceAddress获取其他任何Win32API函数地址。
soeasy。
代码实例
以下是该思路的实现的例子,敬请欣赏:
;walkingthroughthesehframetogetkernel32addr
;searchtogetGetProcAddressaddr
;thenusebaseofkernel32andGetProcAddressto
;getallneededAPIs
include'%fasinc%/'
include'%fasinc%/'
.data
bufrb256
fmtdb"Kernel32baseis:
%X",0
zTitdb"ByHume2K2+",0
hModulek32dd0
.codew
api_List:
api_ends:
StArT:
xoresi,esi
lodsdword[fs:
esi]
@@:
inceax
je@F
deceax
xchgesi,eax
LODSD;nextseh_frame
jmpnear@B
@@:
LODSD;kernel32func...
;compareifPE_hdr
xchgesi,eax
find_pe_header:
decesi
xorsi,si;kernel32is64kbalign
moveax,[esi]
addax,-'MZ';antiheuristic
jnefind_pe_header
movedi,[esi+];.e_lfanew
moveax,[esi+edi]
addeax,-'PE';antiheuristic
jnefind_pe_header
;mov[hModulek32],esi
pushesi
;esi=VA
;edi=RVA
movebp,esi
movedi,[ebp+edi+]
pushediesi
moveax,[ebp+edi+]
movedx,[ebp+edi+]
call@F
db"GetProcAddress",0
@@:
popedi
movecx,15
subeax,4
next_:
addeax,4
addedi,ecx
subedi,15
movesi,[ebp+eax]
addesi,ebp
movecx,15
repzcmpsb
jnznext_
popesiedi
subeax,[ebp+edi+]
shreax,1
addedx,ebp
movzxeax,word[edx+eax]
addesi,[ebp+edi+]
addebp,[esi+eax*4];ebp=;useGetProcAddressandhModuletogetotherfunc
popesi;esi=kernel32Base
;int3
invokeExitProcess,0
.endStArT