接着之前预习的知识,我观察的自己编译出来的bin
LoadLibraryExA
LoadLibraryExA函数进去,现时用RtInitAnsiString函数初始化了ANSI的计数字符串,底层是调用了LoadLibraryExW函数,在LoadLibrarExW函数里做了unicode的计数字符串的初始化,这里过后主要看都了路径(path)函数,我看了LoadLibrarExW底层一路到LoadDL函数,没看懂,然后一路跳出来,最后就是释放最开始的字符串的函数了
RtlAnsistringTounicodestring
RtlInitansistringEx
LdrGetolIPath
LdrLoadDLL
RtIfreeUnicodestring
RtlReleasePath
GetProcAdderss
GetProcAdderss用的底层函数GetProcAddressforcaller,同样内部也是做计数字符串的初始化,然后底层函数是LdrGetProcedureAddressForCaller,进入这个函数又看到了RtlAcquireSRWLockExclusive函数,这个函数进行读写,应该是获取地址,这里没有跟进去,后面跳出来了RtIReleasesRhLockExcIusive又做了释放,奇怪的是释放后又再次掉用了RtIAcqurieRhLockExcIusive,大致是这样
讲解
在学长的电脑上看到的
RtlpImageNtHeader
RtlImageDirectoryEntryToData
LdrLoadDll
LdrpCreateDllSection
ZwMapViewOfSection
NtUnmapViewOfSection
LdrpAllocateDataTableEntry
LdrpFetchAddressOfEntryPoint
ZwProtectVirtualMemory
DecodeSystemPointer
EncodeSystemPointer
其实LoadLibrary 核心流程其实是:把一个DLL 拷贝到内存,然后解析DLL PE结构中的导入表,把该DLL 所需要的DLL模块,都加载到内存,然后处理导入函数从stub到真正函数指针的过程,所有函数指针处理完之后,就可以返回了
RtlpImageNtHeader、RtlImageDirectoryEntryToData 这两个函数操作,PE结构,获取PE头,获取导入表项的
加载DLL 的时候,首先从文件到内存,那么既然需要找文件,就会有很多默认的路径,去搜索并打开文件
打开文件,读取到内存之后,就需要把DLL 模块内容,映射到进程内存中
ZwMapViewOfSection 映射
NtUnmapViewOfSection 解除映射
DLL 装入内存后,DLL内存区域的不同的节,用途不同,所以内存赋予的内存属性也不一样
和之前我们学过的,内存申请函数VirtualAlloc 的参数中提到一样,最常用的读 写 执行
一般存数据的是只读,代码节,必须得赋予可执行属性内存,才能最后跑起来
ZwProtectVirtualMemory 这个函数就可以修改内存的属性,其顶层函数是VirtuallProtect(应该是这个)
那么有一个问题:如果每次加载一个动态链接库,到同一个地址上,是不是使用GetProcAddress 获取同一个函数的指针地址值是相同的?
答案是显而易见的,相同的
如果每次基址都一样,硬编码地址值之后,就能直接这个函数了,这个就涉及到一个安全问题,一段注入EXE的SHELLCODE,能很简单获取到系统接口地址,是不是就能很容易的搞事情了
PIE(要记得,学安全技术,一定要懂得这个东西产生的背景)
也叫漏洞利用缓解及对抗技术ALSR ,ALSR地址空间布局随机化
不过,PE结构上有个项,可以关闭这个地址空间布局随机化,所有SHELLCODE 加载之后,第一件事,就是去找所用函数的指针,没有例外
所以在获取函数指针的途径上,一定会有各种技术,来提高这个操作的门槛
还有就是刚才提到的两个函数,在比较新的windows上才有: EncodePointer/DecodePointer,
在传递函数指针的过程中,对指针值,进行编码解密的一个处理
ALSR 开启后,每次加载的DLL基址,都会不同所以做DLL函数HOOK ,或者是捞函数指针,就不太容易了
在很早以前,那种恶意代码中,经常发现硬编码的函数指针
就是利用基址,重复的特性
目前shellcode 做法,就是寻找,进程的DLL保存指针,获取到kernel32的基址,之后,自己遍历PE结构,获取函数指针
进程空间中,也存在,一种监控程序的HOOK,IAT HOOK,通过在PE加载时,把PE导入表函数地址改了,先走监控函数,再走系统函数来达到监控的目的
杀软的HOOk,是做在内核层的,而且WINDOWS 是不希望第三方程序去监控系统底层接口的
只不过,现在搞内核钩子的难度比较大而已
而且稳定性,这块很考验功夫的
IAT钩子,最致命的一点,就是仅仅在加载时做完操作了
所以对抗手法就是,病毒实现把原始函数的入口点若干字节,crc 存好,用之前先匹配crc,crc不对,直接退
还有一种对抗手法,就是单独复制一份DLL到内存,用自己复制这份DLL的代码
不是用LoadLibrary 加载,而是自定义的加载函数
其实,知道懂得原理,就能写出来 链接:
https://www.cnblogs.com/StudyCat/p/16001615.html
(链接帖子里面的信息一定要认知看)
PE内存加载,内存执行SHELLCODE,目前已经是恶意代码用得最多的方式
传统的安全软件,大多是基于文件的
而且,内存不好搞监控
搞PE,动态加载,就绕不开PE结构
有种极限方法,是把函数头搞出来,然后改汇编跳到,HOOK之后,的DLL代码上
只要能绕过HOOK代码就行
什么是HOOK?
https://blog.csdn.net/qq_36381855/article/details/79962673
说白了,HOOK对抗,比的就是谁更猥琐
对于汇编代码修改,这块,安全技术上,对抗手法,就是局部代码crc校验
英雄联盟用的TP ,里面全是好几个线程,校验关键crc
就是不停的去读取那块代码,然后算CRC,CRC校验失败,直接下狠手
本课的另一个函数,GetProcAddress ,其实就是PE结构的导出表的结构的解析
RtlAcquireSRWLockExclusive 这个是一个同步手段
类似自旋锁的东西
如果需要访问需要遵循读写分离的内存区域时,就需要用到
拿锁,之后,非该线程的代码时不允许访问目标块的
只有拿锁的,释放锁之后,才能访问LdrGetProcedureAddress
这节课的知识点,关联的几个好玩的编码项目:PE内存加载,进程hollow 技术https://jev0n.com/2020/03/11/65.html
虽然这个IATHook 技术很老,但是也要看用的地方
进程hollow 简单点,就是把当前进程代码挖空,给进程换代码
卸载当前进程的代码块,就用NtUnmapViewOfSection
必须对PE结构熟悉,才能搞
https://zhuanlan.zhihu.com/p/30990104?from_voters_page=true
https://www.cnblogs.com/bonelee/p/15866488.html