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

原文: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   

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    

时间: 2024-09-27 15:11:18

hash扫描获得api函数地址学习笔记的相关文章

ArcGIS API for Silverlight 学习笔记

这里主要讲解展示不同的服务地图 先看一个实例: 新建一个Silverlight项目,在MainPage.xaml文件中,引入 ESRI.ArcGIS.Client 命名空间和 ESRI.ArcGIS.Client 所在的程序集 ESRI.ArcGIS.Client,并指定 该命名空间的名字为 esri,当然你也可以用自己的别名,比如myGIS. 接着写Map控件,并指定Map中的地图服务,一个简单的服务地图完成了,代码如下: <UserControl x:Class="ArcGISTile

初探C++函数模版学习笔记

泛型程序设计 特点:算法实现时不指定具体要操作的数据的类型.算法实现一遍但可适用于多种数据结构. 优势:减少重复代码的编写. 实现:大量编写模板, 使用模板的程序设计. 函数模版 为了交换两个int变量的值, 需要编写如下Swap函数: void Swap(int & x, int & y) { int tmp = x; x = y; y = tmp; } 为了交换两个double型变量的值, 还需要编写如下Swap函数: void Swap(double & x, double

JavaScript字符串常用操作函数之学习笔记

字符串简介 使用英文单引号或双引号括起来,如:’Hello’,”World”,但是不能首尾的单引号和双引号必须一致,交错使用,如果要打印单引号或者双引号,可以使用转义字符\’(单引号),\”(双引号)  代码如下 复制代码 var str_1 = 'Hello World!';  //Hello World!var str_2 = "Hello World!";  //Hello World!var str_3 = '他说:"这样可以的."';  //他说:&quo

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

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

JavaScript权威设计--JavaScript函数(简要学习笔记十)

1.函数命名规范 函数命名通常以动词为前缀的词组.通常第一个字符小写.当包含多个单词时,一种约定是将单词以下划线分割,就像"like_Zqz()". 还有一种就是"likeZqz()".有些些函数是用作内部用的或者为私有函数通常以一条下划线为前缀,就像"_zqzName()". 2.以表达式方式定义的函数 如: var zqz=function (){ return "zhaoqize"; } 在使用的时候必须把它赋值给一个变

Hive自定义函数的学习笔记(1)

前言: hive本身提供了丰富的函数集, 有普通函数(求平方sqrt), 聚合函数(求和sum), 以及表生成函数(explode, json_tuple)等等. 但不是所有的业务需求都能涉及和覆盖到, 因此hive提供了自定义函数的接口, 方便用户扩展. 自己好像很久没接触hadoop了, 也很久没博客了, 今天趁这个短期的项目, 对hive中涉及的自定义函数做个笔记. 准备: 编写hive自定义函数前, 需要了解下当前线上hive的版本. hive --vesion 比如作者使用到的hive

ArcGIS API for JavaScript 学习笔记 (一) --第一个WebGIS应用程序

说明:本地部署后续我会尝试. 简单介绍: 开发环境是Visual Studio 2012,因为它为所有的.aspx文件..htm文件以及外部的.js文件提供了IntelliSense(智能提示),相当于其他软件的代码自动补全功能,非常方便.接下来是我的第一个Javascript API 应用程序.ESRI在其arcgis online中提供了在线的ArcGIS API for JavaScript,在web应用中直接引用即可,无需下载安装:当然也可以下载API,然后部署到自己的web服务器,在这

Arcgis api for javascript学习笔记-初步尝试(3.2X版本)

Arcgis api for javascript(3.22版本)官方地址 :https://developers.arcgis.com/javascript/3/ 1. 根据官方示例实现一个简单地图展示功能. 示例代码: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <link rel="stylesheet" href="https://js.arcgis.com/3.22/esri/css/esri

JavaScript权威设计--JavaScript函数(简要学习笔记十一)

1.函数调用的四种方式 第三种:构造函数调用 如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内.这和函数调用和方法调用是一致的.但如果构造函数没有形参,JavaScript构造函数调用的语法是允许省略实参列表和圆括号的. 如: var o=new Object(); //->等价于 var o=new Object; 第四种:使用call()与apply()间接调用(放在后面详细说明) 2.函数的实参与形参——可选形参 先看一个例子: function getA(