基于隐藏可执行镜像并注入dll保护镜像代码的方法

文档序号:9751202阅读:804来源:国知局
基于隐藏可执行镜像并注入dll保护镜像代码的方法
【技术领域】
[0001] 本发明涉及系统底层安全领域,具体涉及到隐藏可执行镜像、防止恶意代码注入 和保护API。
【背景技术】
[0002] 代码注入通常是线程注入,即另一个进程获有较高权限,能够打开目标经常的 Token,在目标进程中开辟一段新的代码执行空间,并且在这写入劫持dll加载的 ShellCode,然后把这段代码作为线程函数进行加载。劫持dll在加载的时候,系统将自动调 用 D1 IMain入 口函数,并且此时 ul_reason_f or_cal 1 的值为DLL_PROCESS_ATTACH。如果此时 DllMain中有做Hook之类的代码,那么这个进程就能被随便修改,做一些开发者不希望发生 的事。
[0003] 对于代码劫持,基本上有两种方式,第一种是加壳,第二种是代码虚拟化。但是加 壳只能静态保护代码,如果一旦壳被脱,那么保护也就没有了。并且在加壳的时候,我们可 以去Hook进程中系统dll里的API对目标进程进行监控,所以这种方法只能用于对安全要求 比较低的场合。然而第二种代码虚拟化的方式有比较强的保护,但是因为代码是在虚拟机 中运行的,所以性能损失很大,如果对程序运行效率有比较高的要求,代码虚拟化方式是不 可取的。而本发明就是在两者之间做了一下折衷,既能动态保护代码,也不会大幅度降低代 码执行效率。

【发明内容】

[0004] 本发明目的是提供一种基于隐藏可执行镜像并注入dll保护镜像代码的方法,即, 一种加载加密镜像,并且使用dll动态链接库实现对应用程序实现保护的方法。
[0005] 所述方法包括以下步骤:
[0006] 1、将目标镜像在内存中解密
[0007] 将加密镜像读取到内存,读取文件首部的文件头(例如文件前16字节)来判断判断 目标镜像是否是加密镜像(例如判断Magic值是否和预设值相同),1.1)如果不是指定的加 密镜像,则放弃加载;1.2)如果是指定的加密镜像,则执行对应的解密(例如,解密的算法基 于快速的xor算法,将文件头Null作为解密Key,对文件进行解密),然后判断解密后的数据 是否是一个合法PE镜像,1.2.1)如果不是一个合法PE镜像,则退回到待解密状态,1.2.2)如 果是一个合法PE镜像,则执行第2步。
[0008] 其中加密镜像的扩展名使用*.vpe。
[0009] 根据文件前16字节判断目标镜像是否是加密镜像,如果不是则放弃加载。
[0010] 文件头的成员列表如下所示,详细的结构在图1中记录。
[0011] Magic:为一个校检码,这个值被设定为(UL0NG) 'ΧΜ0Ε ',然后与常数0x4fd63al5做 一次异或运算所得到的值。
[0012] DecompressLength:记录了文件原始的长度,在解压文件的时候需要用到。
[0013] Imagelnfo:当这个值为0的时候,代表目标加密镜像就一个exe,如果是1,就认为 这个镜像是dll文件。
[0014] Null:加密的使用的key
[0015] 最初的状态下,受保护的exe是处于加密状态的。因为加密后完全破坏了exe本应 该有的PE镜像格式,是无法使用调试器打开的。
[0016] 将加密镜像读取到内存,读取文件首部的文件头。先判断Magic值是否和预设值相 同,如果不同,就认为这个镜像含有不正确的数据,加载器放弃加载。
[0017] 文件头之后的数据就是压缩后的文件数据,这里使用的开源zlib作为压缩方式, 使用zlib的uncompress进行解压,并且释放原始的未解压数据。
[0018] 使用Virtual Alloc根据文件头中记录的DecompressLength为解压后的数据分配 内存。使用VirtualAlloc是为了使分配的内存是页面对其的,防止代码未对其访问而造成 的中断。
[0019] 最后对解压后的数据做一次解密操作,解密的算法基于快速的x〇r算法,将文件头 Nul 1作为解密Key,对文件进行解密。
[0020] 解密算法在图2中有详细描述。
[0021] 2、PE镜像的加载
[0022]装载程序所需要的dll文件,执行镜像重定位,最后执行0ΕΡ(入口点函数),如果上 面的过程有出错,则返回到本步骤2的待装载状态(即PE镜像的待加载状态),如果上面的过 程都没有出错,就执行第3步。
[0023]步骤2包括以下子步骤:2.1)加载器会模拟操作系统的加载器,依次修改PE镜像的 区段的属性,并且对齐区段,2.2)执行预加载函数(比如TLS回调函数),加载IAT中对应的 dl 1动态链接库,然后获取到0ΕΡ(入口点函数),和2.3)注入保护dl 1,执行入口点函数,PE镜 像的加载就完成了。
[0024] 一般,所加载的受保护的PE镜像是基于加载器进程执行的,即,受保护的PE镜像对 于操作系统来说是不可见的;优选的是,在受保护的PE镜像执行时既不是加载器的子进程, 又不是加载器的代码段,只是堆上一块可执行区段,dumper无法将这个区段准确提取出来。 [0025]此时加载器会依次加载区段。
[0026] Exe镜像在内存中是严格制定方式对其的,对其的值在OptionalHeader中的 SectionAlignment成员这里指定的。分配的内存应该是这个值的倍数。
[0027]为了更好地模拟系统PE加载器的加载过程,保护系统使用的加载函数都是 VirtualAlloc 这个 API。
[0028] VirtualAl loc分配内存最快,分配出的内存也是和cpu页面对齐的,并且支持虚拟 内存,能够分配超过物理内存大小的空间,这个几点都满足现代应用程序执行的标准。 [0029]复制代码镜像:
[0030] OptionalHeader的详细结构记录在图3中。
[0031 ] OptionalHeader · ImageBase记录了exe镜像的基地址,对于没有重定向的exe、来 说,这个值为0x00400000;
[0032] OptionalHeader · SizeOf Image记录了 exe镜像的大小,然后使用VirtualAl loc,在 制定的内存地址开始分配该段内存。这样分配是不一定会成功的。如果加载器本身占用了 这段内存,就要考虑让VirtualAlloc随机分配这段内存,那么内存基址也会发生改变。相应 的,OptionalHeader中的ImageBase所记录的值也会发生变化。
[0033] 加载Dos Header和PE Header:
[0034] 将DOS Header复制到内存镜像首部:
[0035] memcpy(headers,dos-header,old-header->0ptionalHeader·SizeOfHeaders);
[0036] 做重定位:
[0037] 如果分配的首地址和OptionalHeader · ImageBase的值不同的话,那么就需要做一 次重定向,不然代码是无法执行的。
[0038] locationDelta=(SIZE-T)(code-old-header->0ptionalHeader·ImageBase);
[0039] 如果Delta不为0,说明需要重定向:
[0040] isRelocated = PerformBaseRelocation(result,locationDelta)
[0041] 建立导入表:
[0042] BuildlmportTable(result)
[0043] 依次遍历导入的dll项:
[0044] !IsBadReadPtr(importDesc,sizeof(IMAGE-IMPORT-DESCRIPTOR))&& importDesc->Name;importDesc++
[0045] 通过名字把这个dll加载到内存:
[0046] loadLibrary((LPCSTR)(codeBase+importDesc->Name),module->userdata)
[0047] 依次遍历导入的API,在内存中获取到对应的地址,如果地址为0,表示这个API在 当前系统里是不支持的,则加载失败。
[0048]对不同地方内存区段做不同的内存保护,使用的API为VirtualProtect
[0049] FinalizeSections(result)
[0050] 根据CPU的页面大小,再次对每个页面就行对其操作,并且使用设置不同的内存保 护,让这些内存能以对应的方式访问。
[0051 ]执行TLS回掉函数
[0052] ExecuteTLS(result)
[0053] TLS回掉函数是需要在执行入口点函数(0ΕΡ)之前执行的。
[0054]获取到tls的偏移量。
[0055] PIMAGE_TLS_DIRECTORY tls=(PIMAGE_TLS_DIRECT0RY)(codeBase+directory-> VirtualAddress);
[0056] 获取到TLS开始项的值
[0057]
[0058] 依次执行TLS,使得在0ΕΡ被执行之前,全部的TLS函数都被执行过了。
[0059]
[0060]获取exe的0ΕΡ,代码的执行是从这里开始的。
[0061 ] result->exeEntry=(ExeEntryProc)(code + result->headers-> OptionalHeader.AddressOfEntryPoint);
[0062]到此,exe的镜像初始化工作已经完成。
[00
当前第1页1 2 3 4 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1