码迷,mamicode.com
首页 > Windows程序 > 详细

hash扫描获得api函数地址学习笔记

时间:2015-02-03 12:32:56      阅读:309      评论:0      收藏:0      [点我收藏+]

标签:

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题

搜索获得api函数地址的实现

我们的程序能正常的调用函数。那么这个动态链接库是如何输出函数来供我们的用户程序调用呢?它实际上是采用输出表结构来描述本dll需要导出哪些函数来供其他的程序调用,这样其他的用户程序才能正常的调用此动态链接库的输出函数.

导出表结构:

IMAGE_EXPORT_DIRECTORY struct
Characteristics		DWORD	?	; 未使用,总是为0
TimeDateStamp		DWORD 	?	; 文件的产生时刻
MajorVersion		WORD	?	; 未使用,总是为0
MinorVersion		WORD	?	; 未使用,总是为0
nName			DWORD	?	; 指向文件名的RVA
nBase			DWORD 	? 	; 导出函数的起始序号
NumberOfFunctions	DWORD	?	; 导出函数的总数
NumberOfNames		DWORD 	?	; 以名称导出的函数总数
AddressOfFunctions	DWORD	?	; 指向导出函数地址表的RVA
AddressOfNames		DWORD 	?	; 指向函数名地址表的RVA
AddressOfNameOrdinals	DWORD	?	; 指向函数名序号表的RVA
IMAGE_EXPORT_DIRECTORY ends

在这里说下AddressOfFunctions、AddressOfNames和AddressOfNameOrdinals这三个成员的对应关系,比如说我们通过名称搜索MessageBoxA函数的地址, AddressOfNames的值是一个RVA数组,每个RVA加上模块基址就是实际的函数名称字符串地址,如果说MessageBoxA这个字符串在这个数组的索引位置0处, 而AddressOfNames和AddressOfNameOrdinals是对应的,用”MessageBoxA“字符串在AddressOfNames中的索引在AddressOfNameOrdinals指向的函数名序号表中取值,取出的序号值再用在AddressOfFunctions中用来取值,取得的RVA加上模块基址就是MessageBoxA函数的地址了。

 

技术分享

 

下面的代码用来测试获取MessageBoxA函数的地址并调用

技术分享
.386    
02.    .model flat, stdcall    
03.    option casemap:none    
04.    
05.include windows.inc    
06.include user32.inc    
07.include kernel32.inc    
08.includelib user32.lib    
09.includelib kernel32.lib    
10.    
11.    .const    
12.szDllName   db user32.dll, 0    
13.szApiString db MessageBoxA, 0    
14.szText      db Get API Address Success!, 0    
15.szCaption   db Success, 0    
16.    
17.    .code    
18._GetApiAddress proc _hDllHandle, _lpApiString    
19.        
20.    push ebp    
21.    mov eax, _hDllHandle        ; hModule    
22.    mov ecx, _lpApiString       ; lpApiString    
23.    mov ebx, eax            ; ebx = hModule    
24.    mov edi, ecx            ; edi = lpApiString    
25.    xor al, al    
26.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
27.; 获得_lpApiString长度    
28.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
29._Scasb:    
30.    scasb    
31.    jnz _Scasb          ; 单字节扫描edi指向的字符串,不和al相等跳转    
32.    dec edi             ; 减去字符串结尾0    
33.    sub edi, ecx            ; 字符串结尾减去起始获得长度    
34.    xchg edi, ecx           ; 将长度放到ecx, 字符串放到edi    
35.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
36.; 读取导出表的一些数据    
37.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
38.    mov eax, [ebx + 3ch]        ; 获得PE头的位置    
39.    mov esi, [ebx + eax + 78h]  ; 获得IMAGE_NT_HEADERS.IMAGE_OPTIONAL_HEADER32.IMAGE_DATA_DIRECTORY    
40.    lea esi, [esi + ebx + IMAGE_EXPORT_DIRECTORY.NumberOfNames] ; esi指向导出表结构的NumberOfNames    
41.    lodsd               ; 将NumberOfNames读取到eax    
42.    xchg eax, edx           ; edx = NumberOfNames    
43.    lodsd               ; 将AddressOfFunctions读取到eax    
44.    push eax            ; [esp] = AddressOfFunctions    
45.    lodsd               ; eax = AddressOfNames    
46.    xchg eax, ebp           ; ebp = AddressOfNames    
47.    lodsd               ; eax = AddressOfNameOrdinals    
48.    xchg eax, ebp           ; eax = AddressOfNames, ebp = AddressOfNameOrdinals    
49.    add eax, ebx            ; eax = 函数名称RVA数组    
50.        
51.    mov [esp - 4 * 1], edi      ; 临时存储    
52.    mov [esp - 4 * 2], ecx      ; 临时存储    
53.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
54.; 扫描指定api是否存在dll的导出表中    
55.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
56._LoopScas:    
57.    dec edx                 ; edx = NumberOfNames    
58.    js _Ret             ; 为负转移,即为-1转移  
59.    mov esi, [eax + edx * 4]    ; 取得函数名称RVA, 乘4是因为函数名称RVA数组是dword类型的    
60.    add esi, ebx                ; esi = 函数名称的实际地址    
61.    repz cmpsb              ; esi和edi循环单字节比较,相等循环    
62.    jz _GetAddr    
63.    mov edi, [esp - 4 * 1]      ; 还原edi    
64.    mov ecx, [esp - 4 * 2]      ; 还原ecx    
65.    jmp _LoopScas    
66.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
67.; 获取指定api的地址    
68.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>       
69._GetAddr:    
70.    shl edx, 1              ; edx = 函数名地址表索引, 乘以2是因为AddressOfNameOrdinals是word数组    
71.    add ebp, edx                ; ebp = 函数序号表的RVA, 加edx为取得指定索引的序号    
72.    movzx eax, word ptr [ebp + ebx] ; 取得指定序号值    
73.    shl eax, 2              ; 乘以4是因为AddressOfFunctions为dowrd数组    
74.    add eax, [esp]    
75.    mov eax, [ebx + eax]        ; 取得指定序号函数的地址RVA    
76.    add eax, ebx                ; 获得函数实际地址    
77.        
78._Ret:    
79.    pop ecx                 ; 将AddressOfFunctions弹出堆栈    
80.    pop ebp    
81.    ret                 
82.        
83._GetApiAddress endp     
84.    
85._WinMain proc    
86.    
87.    invoke LoadLibrary, addr szDllName    
88.    invoke _GetApiAddress, eax, addr szApiString    
89.    push MB_OK    
90.    lea ebx, szCaption    
91.    push ebx    
92.    lea ebx, szText    
93.    push ebx    
94.    push 0    
95.    call eax    
96.    ret    
97.        
98._WinMain endp    
99.    
100.start:    
101.    
102.    call _WinMain    
103.    invoke ExitProcess, 0    
104.        
105.    end start   
View Code

hash算法搜索获得api函数地址的实现

我们一般要获得一个函数的地址,通常采用的是明文,例如定义一个api函数字符串"MessageBoxA",然后在GetProcAddress函数中一个字节一个字节进行比较。这样弊端很多,例如如果我们定义一个杀毒软件比较敏感的api函数字符串,那么可能就会增加杀毒软件对我们的程序的判定值,而且定义这些字符串还有一个弊端是占用的字节数较大。我们想想如何我们的api函数字符串通过算法将它定义成一个4字节的值,然后在GetProcAddress中把AddressOfNames表中的每个地址指向的api字符串通过我们的算法压缩成4字节值后,与我们之前定义的4字节值进行判断,如果匹配成功则读取函数地址。

我们来看一种rol 3移位算法,这个算法是每次将目的地址循环向左移动3位,然后将低字节与源字符串的每个字节进行异或。算法很精巧方便。还有很多方便小巧的算法,例如ROR 13等算法,其实它和我们上面的基本一样,只不过它将函数名表的字符串通过我们的算法过程获得hash值后与我们之前定义的hash值进行匹配,匹配成功则获得对应函数的地址。

下面的代码通过hash搜索WinExec函数来运行Clac:

技术分享
.386    
02.    .model flat, stdcall    
03.    option casemap:none    
04.        
05.include windows.inc    
06.include user32.inc    
07.include kernel32.inc    
08.includelib user32.lib    
09.includelib kernel32.lib    
10.    
11.    .const    
12.szCalc      db calc.exe, 0    
13.    
14.    .code    
15.        
16._GetKrnl32 proc    
17.  
18.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
19.; 获取kernel32.dll的地址,因为xp和win7不一样  
20.; 故采用比较名称的方式获取地址,具体见Kernel32基地址获得学习  
21.; http://blog.csdn.net/programmingring/article/details/11357393  
22.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
23.    assume fs:nothing    
24.    mov eax, fs:[30h]    
25.    mov eax, [eax + 0ch]    
26.    mov eax, [eax + 1ch]        ; 第一个LDR_MODULE  
27.      
28.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
29.; 压入kernel32.dll的unicode字符串  
30.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>    
31.    push dword ptr 006ch    
32.    push dword ptr 6c0064h    
33.    push dword ptr 2e0032h    
34.    push dword ptr 33006ch    
35.    push dword ptr 65006eh    
36.    push dword ptr 720065h    
37.    push word ptr 006bh    
38.    mov ebx, esp            ; ebx指向字符串  
39.      
40.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
41.; 开始比较  
42.;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>        
43._Search:    
44.    cmp eax, 0    
45.    jz _NotFound    
46.    cmp eax, 0ffffffffh    
47.    jz _NotFound    
48.    lea esi, [eax + 1ch]        ; esi指向UNICODE_STRING结构  
49.    xor ecx, ecx              
50.    mov cx, 13              ; 比较的长度  
51.    mov esi, [esi + 4]          ; 获得UNICODE_STRING结构的Buffer成员  
52.    mov edi, ebx            ; edi指向kernel32unicode字符串  
53.    repz cmpsw    
54.    or ecx, ecx             ; ecx减完说明相等  
55.    jz _Found    
56.    mov eax, [eax]              ; 获取下一个LDR_MODULE  
57.    jmp _Search    
58.        
59._NotFound:    
60.    mov eax, 0ffffffffh    
61.    jmp _Over    
62.        
63._Found:    
64.    mov eax, [eax + 08h]        ; 获得地址  
65.        
66._Over:    
67.    add esp, 26    
68.    ret    
69.        
70._GetKrnl32 endp    
71.    
72._GetRolHash proc _lpApiString    
73.        
74.    mov eax, _lpApiString   
75.    push esi    
76.    xor edx, edx    
77.    xchg eax, esi           ; esi = _lpApiString  
78.    cld                 ; 递增  
79.        
80._Next:        
81.    lodsb               ; 从esi指向的地址中取一个字符  
82.    test al, al             ; 比较是否为0  
83.    jz _Ret    
84.    rol edx, 3              ; 左循环移位3位  
85.    xor dl, al              ; 低字节和字符异或  
86.    jmp _Next    
87.        
88._Ret:    
89.    xchg eax, edx           ; eax = 处理后的_lpApiString的hash  
90.    pop esi    
91.    ret    
92.        
93._GetRolHash endp    
94.    
95._GetApi proc _hDllHandle, _iHashApi    
96.        
97.    push ebp    
98.    mov eax, _hDllHandle    
99.    mov ecx, _iHashApi    
100.    mov ebx, eax    
101.    mov edi, ecx    
102.        
103.    mov eax, [ebx + 3ch]    
104.    mov esi, [ebx + eax + 78h]          ; Get Export RVA    
105.    lea esi, [esi + ebx + IMAGE_EXPORT_DIRECTORY.NumberOfNames]    
106.    lodsd    
107.    xchg eax, edx                   ; edx = NumberOfNames    
108.    lodsd    
109.    push eax                        ; [esp] = AddressOfFunctions    
110.    lodsd    
111.    xchg eax, ebp                   ; ebp = AddressOfNames    
112.    lodsd    
113.    xchg eax, ebp                   ; ebp = AddressOfNameOrdinals, eax = AddressOfNames    
114.    add eax, ebx    
115.    xchg eax, esi                   ; esi = AddressOfNames    
116.        
117._LoopScas:    
118.    dec edx    
119.    js _Ret    
120.    lodsd               ; eax = api名称的rva  
121.    add eax, ebx            ; eax = api名称的实际地址  
122.    push edx    
123.    invoke _GetRolHash, eax         ; 计算hash字符串    
124.        
125.    pop edx    
126.    cmp eax, edi            ; 比较和传入的hash参数是否相等  
127.    jz _GetAddr    
128.        
129.    add ebp, 2              ; 递增,和esi相对应  
130.    jmp _LoopScas    
131.        
132._GetAddr:    
133.    movzx eax, word ptr [ebp + ebx]     ; 取得序号值  
134.    shl eax, 2              ; 乘4  
135.    add eax, [esp]              ; 在AddressOfFunctions取得相应序号值位置的值  
136.    mov eax, [ebx + eax]        ; 取得函数地址  
137.    add eax, ebx    
138.        
139._Ret:    
140.    pop ecx    
141.    pop ebp    
142.    ret    
143.        
144._GetApi endp    
145.    
146._WinMain proc    
147.    
148.    invoke _GetKrnl32    
149.    invoke _GetApi, eax, 016ef74bh      ; "WinExec"的hash    
150.    push SW_SHOW    
151.    lea ebx, szCalc    
152.    push ebx    
153.    call eax    
154.    ret    
155.        
156._WinMain endp  
157.    
158.start:    
159.    call _WinMain    
160.    invoke ExitProcess, 0    
161.        
162.    end start    
View Code

 

hash扫描获得api函数地址学习笔记

标签:

原文地址:http://www.cnblogs.com/Acg-Check/p/4269397.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!