设为首页 收藏本站
查看: 762|回复: 0

动态获取API函数地址---对抗win7 aslr安全机制(转)

[复制链接]

尚未签到

发表于 2015-5-14 11:13:43 | 显示全部楼层 |阅读模式
  本人近期在研究缓冲区溢出,在学习中发现,win7下系统关键函数的地址随机化了(每次重启后地址有变),为了解决地址定位问题,在偌大的互联网上找了好久,贴来分享下,以作备用。
  ------------------------------------------------------------------------------------------------------------------
  cvc论坛里好久没人写基础文章了,我就大胆地来个大家写个有关API函数地址获取的文章,希望对初学病毒的你有所帮助
要想动态地获得一个API函数的地址,我们通常都是调用系统的LoadLibraryA()函数和GetProcAddress()函数来动态地获得。LoadLibraryA()用来加载API函数对应的动态链接库
(dll),GetProcAddress()函数用来获得API函数对应的入口地址。
那如何获得LoadLibraryA()和GetProcAddress()的地址呢?
我们知道LoadLibraryA()和GetProcAddress()都是由kernel32.dll引出的函数,所以LoadLibraryA()的地址也可以由GetProcAddress()得到。那么GetProcAddress()的地址如何得
到呢?
上面说过GetProcAddress()的地址在kernel32.dll中,具体地说是在kernel32的引出表中,所以我们的问题变为如何找到kernel32的引出表地址,或者说如何找到kernel32的基地
址。
找kernel32基地址的方法一般有三种:暴力搜索法、异常处理链表搜索法、PEB法。
暴力搜索法是最早的动态查找kernel32基地址的方法。它的原理是几乎所有的win32可执行文件(pe格式文件)运行的时候都加载kernel32.dll,可执行文件进入入口点执行后esp
存放的一般是Kernel32.DLL 中的某个地址,所以沿着这个地址向上查找就可以找到kernel32的基地址。
那么如何知道我们找到的地址是kernel32的基地址呢?
因为kernel32.dll也是标准的pe结构文件,pe结构文件的开始是IMAGE_DOS_HEADER结构,IMAGE_DOS_HEADER结构的第一个字段是e_magic,它的值为’MZ’用于证明这是DOS兼容的
文件类型,所以如果我们找到的地址所指向的字符串为’MZ’,那么我们可以确信这是kernel32的基地址,具体代码如下:
Find_kernel32:
     mov eax,[esp]
     and eax,00000fffh;因为kernel32的基地址在内存中页的开始处,这代码的作用是对齐
     ;页的开始
compare:
     cmp eax,40000h;比较是否低于应用程序的边界
     jb find_kernel32_fail;低于则查找失败
     cmp word ptr[eax],’ZM’;比较e_magic,因为Intel CPU是小端模式所以是’ZM’不是’MZ’
     je kernel32_found
     sub eax.100h;以页为单位加速查找
     jmp compare
kernel32_found:
补充:and eax,00000ffffh也可以换成or eax,00000fffh xor eax,00000fffh 我更喜欢后一种:)
暴力搜索是有缺陷的,如果搜索的内存为不可读时,电脑就会蓝屏,所以毒客们发明了更安全的异常处理链表搜索法。所谓异常处理链表就是系统提供的处理异常的机制,当系统
遇到一个不知道如何处理的异常时就会查找异常处理链表,找到对应的异常处理程序,把保存的处理程序地址赋给eip,并执行处理程序,避免系统崩溃,异常处理链表的最后一项
是默认异常处理函数UnhandledExceptionFilter,因为UnhandledExceptionFilter在kernel32中,所以从UNhandledExceptionFilter地址向上搜索即可找到kernel32的基地址,具体
代码如下:
Load_SEH:
     xor edx, edx;edx=0
     push dword ptr fs:{edx];注册SEH异常处理函数
     mov fs:[edx],esp
     mov eax,[esp+(12*4)];指向SEH末尾
     xor ax,ax; 对齐于64k边界开始
find_kernel32:
     cmp eax,40000h;比较是否低于应用程序的边界
     jb find_kernel32_fail;低于则查找失败
     cmp word ptr[eax],’ZM’;比较MZ标志
     je kernel32_found
     sub eax,65536;以64k为边界加速查找
     jmp find_kernel32
kernel32_found:
补充:找UnhandledExceptionFilter函数地址还可以用如下方法UnhandledExceptionFilter指针是在异常链表的最后,它的上一个值是指向下一个处理点的地址,因为后面没有异常
处理点了,所以是0xffffffff,所以获得UnhandledExceptionFilter地址的代码为:
GetExceptionFilter:
     cmp [eax],0xffffffff
     je GetedExceptionFilter
     mov eax,[eax]
jmp GetExceptionFilter
GetedExceptionFilter:
     mov eax,[eax+4]
随着黑客技术的发展,后来出现了更为简单的方法:PEB法。(结构示意图来自 http://bbs.nyasama.com/forum.php?mod=viewthread&tid=585 )
  -----------------------------------------PEB--结构--------------------------------------------------------------


DSC0000.gif DSC0001.gif View Code


  1 01.typedef struct _PEB
  2
  3 02.{
  4
  5 03.    UCHAR InheritedAddressSpace; // 00h
  6
  7 04.    UCHAR ReadImageFileExecOptions; // 01h
  8
  9 05.    UCHAR BeingDebugged; // 02h
10
11 06.    UCHAR Spare; // 03h
12
13 07.    PVOID Mutant; // 04h
14
15 08.    PVOID ImageBaseAddress; // 08h
16
17 09.    PPEB_LDR_DATA Ldr; // 0Ch
18
19 10.    PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
20
21 11.    PVOID SubSystemData; // 14h
22
23 12.    PVOID ProcessHeap; // 18h
24
25 13.    PVOID FastPebLock; // 1Ch
26
27 14.    PPEBLOCKROUTINE FastPebLockRoutine; // 20h
28
29 15.    PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
30
31 16.    ULONG EnvironmentUpdateCount; // 28h
32
33 17.    PVOID* KernelCallbackTable; // 2Ch
34
35 18.    PVOID EventLogSection; // 30h
36
37 19.    PVOID EventLog; // 34h
38
39 20.    PPEB_FREE_BLOCK FreeList; // 38h
40
41 21.    ULONG TlsExpansionCounter; // 3Ch
42
43 22.    PVOID TlsBitmap; // 40h
44
45 23.    ULONG TlsBitmapBits[0x2]; // 44h
46
47 24.    PVOID ReadOnlySharedMemoryBase; // 4Ch
48
49 25.    PVOID ReadOnlySharedMemoryHeap; // 50h
50
51 26.    PVOID* ReadOnlyStaticServerData; // 54h
52
53 27.    PVOID AnsiCodePageData; // 58h
54
55 28.    PVOID OemCodePageData; // 5Ch
56
57 29.    PVOID UnicodeCaseTableData; // 60h
58
59 30.    ULONG NumberOfProcessors; // 64h
60
61 31.    ULONG NtGlobalFlag; // 68h
62
63 32.    UCHAR Spare2[0x4]; // 6Ch
64
65 33.    LARGE_INTEGER CriticalSectionTimeout; // 70h
66
67 34.    ULONG HeapSegmentReserve; // 78h
68
69 35.    ULONG HeapSegmentCommit; // 7Ch
70
71 36.    ULONG HeapDeCommitTotalFreeThreshold; // 80h
72
73 37.    ULONG HeapDeCommitFreeBlockThreshold; // 84h
74
75 38.    ULONG NumberOfHeaps; // 88h
76
77 39.    ULONG MaximumNumberOfHeaps; // 8Ch
78
79 40.    PVOID** ProcessHeaps; // 90h
80
81 41.    PVOID GdiSharedHandleTable; // 94h
82
83 42.    PVOID ProcessStarterHelper; // 98h
84
85 43.    PVOID GdiDCAttributeList; // 9Ch
86
87 44.    PVOID LoaderLock; // A0h
88
89 45.    ULONG OSMajorVersion; // A4h
90
91 46.    ULONG OSMinorVersion; // A8h
92
93 47.    ULONG OSBuildNumber; // ACh
94
95 48.    ULONG OSPlatformId; // B0h
96
97 49.    ULONG ImageSubSystem; // B4h
98
99 50.    ULONG ImageSubSystemMajorVersion; // B8h
100
101 51.    ULONG ImageSubSystemMinorVersion; // C0h
102
103 52.    ULONG GdiHandleBuffer[0x22]; // C4h
104
105 53.    PVOID ProcessWindowStation; // ???
106
107 54.} PEB, *PPEB;
  -----------------------------------------------------------------------------------------------------------------
  原理如下:在NT内核系统中fs寄存器指向TEB结构,TEB+0x30处指向PEB结构,PEB+0x0c处指向PEB_LDR_DATA结构,
PEB_LDR_DATA+0x1c处存放一些动态链接库地址,第一个指向ntdl.dll,第二个就是kernel32.dll的基地址了。对于非NT内核的9x系统也有类似的链接结构,但是我没有找到相关的
文档,无法做出解释,记下来就好了,就像考前临时抱佛脚那样:) PEB法的代码如下:
find_kernel32:
push esi
xor eax, eax
assume fs:nothing
mov eax, fs:[eax+030h]; eax指向PEB结构
test eax, eax;是否为9x
js find_kernel32_9x
find_kernel32_nt:
mov eax, [eax + 0ch];eax指向PEB_LDR_DATA结构
mov esi, [eax + 01ch];
lodsd
mov eax, [eax + 08h];eax中为kernel基地址
jmp find_kernel32_finished
find_kernel32_9x:
mov eax, [eax + 034h]
lea eax, [eax + 07ch]
mov eax, [eax + 03ch]
kernel32_found:
pop esi
补充:只用cmp word ptr[eax],’ZM’来验证kernel32的基地址有点太单薄,我们可以加点东西,在IMAGE_DOS_HEADER+0x3ch处是e_lfanew存放IMAGE_NT_HEAGER结构的文件地址,
IMAGE_NT_HEAGER结构的第一个字段是signature值为’PE’用于证明是PE文件头,我们还可以检查文件的重分配表的文件地址值是否为40h,子系统是否为win32系统,是否是DLL文
件,具体代码为:
assume esi :ptr IMAGE_DOS_HEADER
cmp [esi].e_magic,IMAGE_DOS_SIGNATURE 
jne find_kernel32
cmp [esi]. e_lfarlc,040h;比较重分配表的文件地址
jne find_kernel32
add esi,[esi].e_lfanew ;此时edx指向PE文件头
assume esi:ptr IMAGE_NT_HEADERS
cmp [esi].Signatur,IMAGE_NT_SIGNATURE ;是PE文件吗?
jne find_kernel32
cmp word ptr [esi].OptionalHeader.Subsystem,2比较子系统
jne find_kernel32
cmp word ptr [esi]. OptionalHeader.DllCharacterstics,00100000b;比较DLL状态
得到了kernel32的基地址,下一步就是定位到引出表找到GetProcAddress的地址。PE头+120处就是引出表的文件地址,引出表中有三个我们需要的字段:
AddressOfFunction字段:指向模块中所有函数地址的数组
AddressOfNames字段:指向模块中所有函数名称的数组
AddressOfNameOrdinals字段:指向AddressOfNames数组中函数对应序数的数组
我们可以这样差找函数地址,用函数的名称在AddressOfName指向的数组中找对应的序号,序号乘以2后在AddressOfNameOrdinals指向的数组中找对应的序数,序数乘以4后在
AddressOfFunction指向的数组中找对应的函数地址,具体代码如下:
输入;
esi=要查找的函数名
eax=函数所在动态链接库地址
输出:
eax=函数的地址
GetFunctionAddress PROC
mov ebx, [eax + 3Ch];指向PE头
add ebx, eax
add ebx, 120
mov ebx, [ebx]
add ebx, eax ;ebx=引出表地址
xor edx, edx
mov ecx, [ebx + 32];AddressOfNames
add ecx, eax
push     esi
push edx
CompareNext: 在函数名数组中查找下一个函数名
pop edx
pop esi
inc edx
mov edi, [ecx]
add edi, eax
add ecx, 4
push     esi
push     edx
CompareName:   ;比较要查找的函数名是否于函数名数组中的一致
mov dl, [edi]
mov dh, [esi]
cmp dl, dh
jne CompareNext
inc edi
inc esi
cmp byte ptr [esi], 0
je GetAddress
jmp CompareName
GetAddress:
pop edx
pop esi
dec edx
shl edx, 1;乘2
mov ecx, [ebx + 36];AddressOfNameOrdinals
add ecx, eax
add ecx, edx
xor edx, edx
mov dx, [ecx]
shl edx, 2;乘4
mov ecx, [ebx + 28];AddressOfFunction
add ecx, eax
add ecx, edx
add eax, [ecx]
ret
GetFunctionAddress ENDP
通过以上讨论GetProcAddress地址可以用如下代码获得
mov eax ,[ebp+Kernel32]
lea esi,[ebp+API_GetProcAddress]
call GetFuntionAddress
mov [ebp+ADDR_GetProcAddress],eax
对于kernel32引出的函数都可以用如上的方法获得地址
对于非kernel32引出的函数如MessageBoxA()可以这样获取:
Lea ebx,[ebp+offset_DLL_User32]
push ebx
call [ebp+ADDR_LoadLibraryA];装载 user32.dll  
push eax ;eax = user32的基地址
lea esi,[ebp+API_MessageBoxA]
push esi
call [ebp+ADDR_GetProcAddressA]
mov [ebp+ADDR_MessageBoxA],eax
补充:现在的杀毒软件都对GetProcAddress函数挂钩,会对调用GetProcAddress函数的程序进行检查,所以为了绕过这保护机制我们不用GetProcAddress函数,像获取kernel32引
出函数那样,直接在动态链接库中找相应的函数地址,还是以MessageBoxA为例,代码如下:
lea esi, [ebp + API_LoadLibraryA]
call GetFunctionAddress
lea ebx, [ebp + offset_DLL_User32]
push     ebx
call eax ; 装载 user32.dll   eax = user32的基地址
lea esi, [ebp + API_MessageBoxA]
call GetFunctionAddress
mov [ebp + ADDR_MessageBoxA], eax
就写这么多了有错误还希望大家指出来,下一次写有关简单病毒优化的文章希望大家支持:)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-66888-1-1.html 上篇帖子: win7修改文件夹图标 下篇帖子: 图像视频处理中Win7 32位+VS2010+OpenCV2.3.1的配置过程
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表