首页
社区
课程
招聘
[Anti Virus专题]1.2 - 3.hash扫描获得api函数地址
发表于: 2009-4-13 23:34 35064

[Anti Virus专题]1.2 - 3.hash扫描获得api函数地址

2009-4-13 23:34
35064

4. hash扫描获得api函数地址

       
        今天这篇文章也是病毒编写中很重要的一篇文章,“hash扫描获得api函数地址”,通过它我们的病毒就可以在宿主程序中调用相应平台的库函数。呼,说白一点就是实现一个自己的GetProcAddress函数,然后通过上节课的所讲解的思路获得Kernel32基地址,然后来搜索api函数地址。

        这篇文章我分为2个栏目:

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

        2. hash算法搜索获得api函数地址的实现。
       
        ;;;;写文章比写代码累多了, 希望辛劳的成果能使大家有所收获,也不枉我此套专题。。。。。。。

;-------------------------------------------------
       
        1. 搜索获得api函数地址的实现:

        Windows和之前的dos最大的一个特色就是采用了动态链接库, 这样我们省了很多的内存。我们可以想象一下如果我们的程序都采用静态库的话,那么我们的所有程序所占内存是相当庞大的,而

Windows采用了动态链接库(这样保证了物理内存仅有一份此动态链接库的copy),这些动态链接库分别提供给我们用户了各种各样的编程接口来实现相应的功能,当我们用户程序调用它时必须包含相应的

库文件、函数名称,这样我们的PE LOADER在加载时才会将相应的动态链接库加载我们的进程的内存空间(实际上windows是通过分页机制将这些DLL的物理内存地址指向我们程序虚拟空间的页表中,这样

我们共享的是同一物理内存)。

        那么我们上面介绍了,windows通过动态链接库提供给我们用户了各种各样的编程接口函数,那么一般的动态链接库的后缀是“.dll”,当然其他的后缀也可以,例如“.exe”, “.sys”,只不

过它们不常用罢了。因为加载器判断的不仅仅是文件后缀名,还有我们的文件结构。
       
        所以一般我们调用某个动态库的函数,我们必须增加这个动态库的导入表结构,这样我们的windows 加载器才会把这个动态库加载到我们的内存空间,并修正导入表结构的导入函数地址,以便

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

能正常的调用此动态链接库的输出函数。

        那么关键的点我们已经知道了,那就是动态链接库是采用输出表结构来描述导出函数。那么如果我们调用某动态链接库的输出函数,首先我们肯定是要查找到这些输出函数的地址?在那里查找

?我们肯定是在输出表结构。好明白这点后,我们来看输出表结构。

struct IMAGE_EXPORT_DIRECTORY
  Characteristics           dd      ?  ;未使用
  TimeDateStamp             dd      ?  ;文件生成时间
  MajorVersion              dw      ?  ;主版本号,一般为0
  MinorVersion              dw      ?  ;次版本号,一般为0
  nName                     dd      ?  ;模块的真实名称
  nBase                     dd      ?  ;基数, 加上序数就是函数地址数组的索引值
  NumberOfFunctions         dd      ?  ;AddressOfFunctions阵列的元素个数
  NumberOfNames             dd      ?  ;AddressOfNames阵列的元素个数
  AddressOfFunctions        dd      ?  ;指向函数地址数组
  AddressOfNames            dd      ?  ;函数名字的指针地址
  AddressOfNameOrdinals     dd      ?  ;指向输出序列号数组
ends
       

        为了更好的理解输出表结构,我们采用fasm编写一段自己定义输出表结构的dll代码。



	format PE GUI 4.0 DLL
	include 'win32ax.inc'
	entry	__DllEntry
	
.text

 ;++
 ;
 ; BOOL
 ;   DllMain(
 ;   IN HINSTANCE hDllHandle, 
 ;   IN DWORD     nReason,    
 ;   IN LPVOID    Reserved    
 ;   )
 ;
 ; Routine Description:
 ;
 ;    测试文件是否是PE文件格式。
 ;
 ; Arguments:
 ;
 ;    (esp)          - return address
 ;
 ;    Data   (esp+4) - hDllHandle
 ;	     (esp+8) - nReason
 ;	     (esp+12)- Reserved
 ;
 ; Return Value:
 ;
 ;    eax = TRUE, initialization succeeds; eax = FALSE, initialization fails。
 ;
 ;--

 __DllEntry:
 	xor	eax, eax
 	inc	eax			
 	ret	4*3



 __MyMessageBox:
	xor	eax, eax
	push	eax
	@pushsz	'Dll'
	@pushsz	'一个dll自定导出表结构例子'
	push	eax
	call	[MessageBox]
	ret
	
.idata

section	'.edata' export data    readable

 __IMAGE_EXPORT_DIRECTORY:
 	dd	0, 0, 0, rva szName, 0, 1, 1
 	dd	rva Address_Tab
 	dd 	rva FuncName_Tab
 	dd	rva Ordinals_Tab
 	

	;dll name
	szName	db 'Msg.dll', 0
	
	;
	
	Address_Tab:
		dd rva __MyMessageBox 	;取__MyMessageBox过程 rva地址
	
	FuncName_Tab:
		dd rva ($+4) 		; ($ + 4) ptr "MyMessageBox"
		db 'MyMessageBox', 0
	
	Ordinals_Tab:
		dw 0
	
	
.fixups


 ;++
 ;
 ; int
 ;  GetApi(
 ;   IN HINSTANCE hModule, 
 ;   IN char *    lpApiString,     
 ;   )
 ;
 ; Routine Description:
 ;
 ;    获取指定函数的内存地址
 ;
 ; Arguments:
 ;
 ;    (esp)          - return address
 ;
 ;    Data   (esp+4) - hDllHandle
 ;	     (esp+8) - lpApiString
 ; Return Value:
 ;
 ;    eax -> Function Mem Address。
 ;
 ;--

 GetApi:
	pop 	edx
	pop	eax			;hModule
	pop	ecx			;lpApiString
	push	edx	
	pushad
	mov	ebx, eax		;hModule	ebx
	mov	edi, ecx		;lpApiString	edi	
	xor	al, al
  .Scasb:
	scasb
	jnz	.Scasb
	dec	edi
	sub	edi, ecx
	xchg	edi, ecx		; edi = lpApiString, ecx = ApiLen
	
	mov	eax, [ebx+3ch]	
	mov	esi, [ebx+eax+78h]	;Get Export Rva
	lea	esi, [esi+ebx+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
	lodsd
	xchg	eax, edx		; edx = NumberOfNames
	lodsd
	push	eax			; [esp] = AddressOfFunctions
	lodsd
	xchg	eax, ebp
	lodsd
	xchg	eax, ebp		; ebp = AddressOfNameOrdinals, eax = AddressOfNames
	add	eax, ebx
	
	mov	[esp+4*6], edi		;临时存储
	mov	[esp+4*5], ecx		;临时存储

  .LoopScas:	
	dec	edx
	jz	.Ret
	mov	esi, [eax+edx*4]
	add	esi, ebx
	repz	cmpsb
	jz	.GetAddr
	mov	edi, [esp+4*6]		
	mov	ecx, [esp+4*5]		
	jmp	.LoopScas
	
  .GetAddr:
  	shl	edx, 1
  	add	ebp, edx
	movzx	eax, word [ebp+ebx]
	shl	eax, 2
	add	eax, [esp]
	mov	eax, [ebx+eax]
	add	eax, ebx
  .Ret:
  	pop	ecx
  	mov	[esp+4*7], eax
	popad
	ret


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (55)
雪    币: 419
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
先沙发,写得太好了
2009-4-13 23:37
0
雪    币: 268
活跃值: (40)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
3
板凳,学习了!
2009-4-13 23:49
0
雪    币: 215
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
我发个参考资料
标 题: 克图鲁的呼唤
作 者: forgot
时 间: 2006-10-28,19:17
链 接: http://bbs.pediy.com/showthread.php?t=33985
2009-4-14 00:02
0
雪    币: 315
活跃值: (23)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
5
支持下forgot大哥。

fasm预编译阶段进行hash计算,很简单。

macro RolHash poffset, [szFuncName]{
  local hash, len, char, temp
  virtual at 0
       db  szFuncName
       len = $
       hash = 0
       temp = 0
       repeat len
         hash = (((hash shl 3) and 0FFFFFFFFh) or (hash shr (32 - 3))); hash = hash rol 3
         load char byte from % - 1
         temp = ((hash and 000000FFh) xor char)
         hash = (hash and 0FFFFFF00h) or temp
       end repeat
  end virtual
    dd hash
  poffset dd 0
}

通过此宏配合我之前这个帖子http://bbs.pediy.com/showthread.php?t=83646的。可以整个以
                      dd hash string
_ApiName_Address  dd Adress
表形式建立api函数地址表,方便调用。。

这些无外乎技巧而已,故而放到
1.3 如何编写病毒代码?
    包括如何有效的优化病毒代码

中进行讲解。
2009-4-14 00:09
0
雪    币: 615
活跃值: (1212)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
2009-4-14 08:10
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
认真学习中,收获不少,我会一直关注LZ的杰作,LZ辛苦了.
2009-4-14 09:21
0
雪    币: 181
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
若若问一句:怎么看起来像字符串匹配丫... ...
2009-4-14 09:23
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
要是是C写的,就好了...
2009-4-14 10:09
0
雪    币: 370
活跃值: (52)
能力值: ( LV13,RANK:350 )
在线值:
发帖
回帖
粉丝
10
lz辛苦了,学习了,我一定要顶下去啊
2009-4-14 10:56
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
11
楼主辛苦了,顶。
2009-4-14 11:38
0
雪    币: 101
活跃值: (88)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
12
先顶后看~~~~~
2009-4-14 12:00
0
雪    币: 2362
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
学习。。。支持。。。
要是能提供c/c++版的
就好学习多了
汇编看着还是有点晕
2009-4-14 12:01
0
雪    币: 379
活跃值: (40)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
14
lz辛苦了,支持你!
2009-4-14 12:21
0
雪    币: 107
活跃值: (1693)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
15
学习 这种方法不错 我得去下个编译器跑跑看
2009-4-14 13:48
0
雪    币: 133
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
你的这个
IMAGE_EXPORT_DIRECTORY是通过
IMAGE_OPTIONAL_HEADER的
DataDirectory的前4个字节找到的吗?
2009-4-14 14:17
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
楼主:  .LoopScas:  
  dec  edx
  jz  .Ret
  lodsd
我觉得是不是要改为:  .LoopScas:  
  test edx,edx
  jz  .Ret
dec  edx
  lodsd好些,如果我们用自定义的输出表,表里只定义一个函数,那这个好象就不行了呀.我是菜鸟,不知道我说的对不对,望指正,谢谢楼主大哥.
2009-4-14 14:44
0
雪    币: 315
活跃值: (23)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
18
to Gaoqing:
恩。

to heroxing:
恩。正确的。其实这个如果要完善还要注意很多,例如定位export输出表偏移等,但是这将浪费大量的byte。

   在病毒编写中一般没有必要这么做,因为这些系统库文件格式都是很常规的, 我们一般的病毒代码仅仅是为了获得常用的库函数。例如获得LoadLibraryA以及GetPorcAddress就可以利用获得后的函数地址来进行调用了,所以就没有必要再去处理这些,一切为了优化。
2009-4-14 15:20
0
雪    币: 414
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
   支持~支持~
2009-4-14 17:01
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
谢谢楼主的回复,小弟受教了.
2009-4-14 17:27
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
21
非常希望能在最后附一个汇总了的本系列CHM,便于收藏学习
2009-4-14 18:34
0
雪    币: 193
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
克鲁图的呼唤看着有点晕,宏没法用od看啊!
2009-4-14 20:33
0
雪    币: 622
活跃值: (65)
能力值: ( LV13,RANK:290 )
在线值:
发帖
回帖
粉丝
23
good ...
2009-4-14 21:23
0
雪    币: 260
活跃值: (39)
能力值: ( LV9,RANK:144 )
在线值:
发帖
回帖
粉丝
24
不错
2009-4-14 21:24
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
这样hash会不会发生碰撞?有没有得到证明?MD5都已经证明会碰撞。
2009-4-15 21:10
0
游客
登录 | 注册 方可回帖
返回
//