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

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

Info

Publication number
CN105512548B
CN105512548B CN201510873591.8A CN201510873591A CN105512548B CN 105512548 B CN105512548 B CN 105512548B CN 201510873591 A CN201510873591 A CN 201510873591A CN 105512548 B CN105512548 B CN 105512548B
Authority
CN
China
Prior art keywords
dll
mirror
flag
iat
function
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Active
Application number
CN201510873591.8A
Other languages
English (en)
Other versions
CN105512548A (zh
Inventor
欧阳建权
陈灵锋
刘任任
唐欢容
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
Xiangtan University
Original Assignee
Xiangtan University
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Xiangtan University filed Critical Xiangtan University
Priority to CN201510873591.8A priority Critical patent/CN105512548B/zh
Publication of CN105512548A publication Critical patent/CN105512548A/zh
Application granted granted Critical
Publication of CN105512548B publication Critical patent/CN105512548B/zh
Active legal-status Critical Current
Anticipated expiration legal-status Critical

Links

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F21/00Security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F21/50Monitoring users, programs or devices to maintain the integrity of platforms, e.g. of processors, firmware or operating systems
    • G06F21/51Monitoring users, programs or devices to maintain the integrity of platforms, e.g. of processors, firmware or operating systems at application loading time, e.g. accepting, rejecting, starting or inhibiting executable software based on integrity or source reliability
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F2221/00Indexing scheme relating to security arrangements for protecting computers, components thereof, programs or data against unauthorised activity
    • G06F2221/03Indexing scheme relating to G06F21/50, monitoring users, programs or devices to maintain the integrity of platforms
    • G06F2221/033Test or assess software

Landscapes

  • Engineering & Computer Science (AREA)
  • Computer Security & Cryptography (AREA)
  • Software Systems (AREA)
  • Theoretical Computer Science (AREA)
  • Computer Hardware Design (AREA)
  • Physics & Mathematics (AREA)
  • General Engineering & Computer Science (AREA)
  • General Physics & Mathematics (AREA)
  • Storage Device Security (AREA)

Abstract

基于隐藏可执行镜像并注入dll保护镜像代码的方法,该方法包括以下步骤:1)将目标镜像在内存中解密;2)PE镜像的加载:装载程序所需要的dll文件,执行镜像重定位,最后执行OEP;3)检查exe模块的IAT是否被劫持;4)主动劫持LoadLibrary和LoadLibraryEx,对加载成果的dll镜像执行上述步骤3中的操作,如果判定没有异常,则执行第5步;5)劫持CreateProcess函数,并且在创建子进程同时,注入本保护dll;6)通过远程线程注入,在Ring3环境下注入被保护的目标进程,防止恶意加载器的线程注入;和7)建立一个优先度比较低的线程,做死循环检查,检查它是否属于Debugger加载,每一秒检查一次。

Description

基于隐藏可执行镜像并注入dll保护镜像代码的方法
技术领域
本发明涉及系统低层安全领域,具体涉及到隐藏可执行镜像、防止恶意代码注入和保护API。
背景技术
代码注入通常是线程注入,即另一个进程获有较高权限,能够打开目标经常的Token,在目标进程中开辟一段新的代码执行空间,并且在这写入劫持dll加载的ShellCode,然后把这段代码作为线程函数进行加载。劫持dll在加载的时候,系统将自动调用DllMain入口函数,并且此时ul_reason_for_call的值为DLL_PROCESS_ATTACH。如果此时DllMain中有做Hook之类的代码,那么这个进程就能被随便修改,做一些开发者不希望发生的事。
对于代码劫持,基本上有两种方式,第一种是加壳,第二种是代码虚拟化。但是加壳只能静态保护代码,如果一旦壳被脱,那么保护也就没有了。并且在加壳的时候,我们可以去Hook进程中系统dll里的API对目标进程进行监控,所以这种方法只能用于对安全要求比较低的场合。然而第二种代码虚拟化的方式有比较强的保护,但是因为代码是在虚拟机中运行的,所以性能损失很大,如果对程序运行效率有比较高的要求,代码虚拟化方式是不可取的。而本发明就是在两者之间做了一下折衷,既能动态保护代码,也不会大幅度降低代码执行效率。
发明内容
本发明目的是提供一种基于隐藏可执行镜像并注入dll保护镜像代码的方法,即,一种加载加密镜像,并且使用dll动态链接库实现对应用程序实现保护的方法。
所述方法包括以下步骤:
1、将目标镜像在内存中解密
将加密镜像读取到内存,读取文件首部的文件头(例如文件前16字节)来判断目标镜像是否是加密镜像(例如判断Magic值是否和预设值相同),1.1)如果不是指定的加密镜像,则放弃加载;1.2)如果是指定的加密镜像,则执行对应的解密(例如,解密的算法基于快速的xor算法,将文件头Null作为解密Key,对文件进行解密),然后判断解密后的数据是否是一个合法PE镜像,1.2.1)如果不是一个合法PE镜像,则退回到待解密状态,1.2.2)如果是一个合法PE镜像,则执行第2步。
其中加密镜像的扩展名使用*.vpe。
根据文件前16字节判断目标镜像是否是加密镜像,如果不是则放弃加载。
文件头的成员列表如下所示,详细的结构在图1中记录。
Magic:为一个校检码,这个值被设定为(ULONG)'XMOE',然后与常数0x4fd63a15做一次异或运算所得到的值。
DecompressLength:记录了文件原始的长度,在解压文件的时候需要用到。
ImageInfo:当这个值为0的时候,代表目标加密镜像就一个exe,如果是1,就认为这个镜像是dll文件。
Null:加密的使用的key
最初的状态下,受保护的exe是处于加密状态的。因为加密后完全破坏了exe本应该有的PE镜像格式,是无法使用调试器打开的。
将加密镜像读取到内存,读取文件首部的文件头。先判断Magic值是否和预设值相同,如果不同,就认为这个镜像含有不正确的数据,加载器放弃加载。
文件头之后的数据就是压缩后的文件数据,这里使用的开源zlib作为压缩方式,使用zlib的uncompress进行解压,并且释放原始的未解压数据。
使用VirtualAlloc根据文件头中记录的DecompressLength为解压后的数据分配内存。使用VirtualAlloc是为了使分配的内存是页面对其的,防止代码未对其访问而造成的中断。
最后对解压后的数据做一次解密操作,解密的算法基于快速的xor算法,将文件头Null作为解密Key,对文件进行解密。
解密算法在图2中有详细描述。
2、PE镜像的加载
装载程序所需要的dll文件,执行镜像重定位,最后执行OEP(入口点函数),如果装载程序所需要的dll文件、执行镜像重定位、最后执行OEP的过程有出错,则返回到本步骤2的待装载状态(即PE镜像的待加载状态),如果装载程序所需要的dll文件、执行镜像重定位、最后执行OEP的过程都没有出错,就执行第3步。
步骤2包括以下子步骤:2.1)加载器会模拟操作系统的加载器,依次修改PE镜像的区段的属性,并且对齐区段,2.2)执行预加载函数(比如TLS回调函数),加载IAT中对应的dll动态链接库,然后获取到OEP(入口点函数),和2.3)注入保护dll,执行入口点函数,PE镜像的加载就完成了。
一般,所加载的受保护的PE镜像是基于加载器进程执行的,即,受保护的PE镜像对于操作系统来说是不可见的;优选的是,在受保护的PE镜像执行时既不是加载器的子进程,又不是加载器的代码段,只是堆上一块可执行区段,dumper无法将这个区段准确提取出来。
此时加载器会依次加载区段。
exe镜像在内存中是严格制定方式对其的,对其的值在OptionalHeader中的SectionAlignment成员这里指定的。分配的内存应该是这个值的倍数。
为了更好地模拟系统PE加载器的加载过程,保护系统使用的加载函数都是VirtualAlloc这个API。
VirtualAlloc分配内存最快,分配出的内存也是和cpu页面对齐的,并且支持虚拟内存,能够分配超过物理内存大小的空间,这个几点都满足现代应用程序执行的标准。
复制代码镜像:
OptionalHeader的详细结构记录在图3中。
OptionalHeader.ImageBase记录了exe镜像的基地址,对于没有重定向的exe、来说,这个值为0x00400000;
OptionalHeader.SizeOfImage记录了exe镜像的大小,然后使用VirtualAlloc,在制定的内存地址开始分配该段内存。这样分配是不一定会成功的。如果加载器本身占用了这段内存,就要考虑让VirtualAlloc随机分配这段内存,那么内存基址也会发生改变。相应的,OptionalHeader中的ImageBase所记录的值也会发生变化。
加载Dos Header和PE Header:
将DOS Header复制到内存镜像首部:
memcpy(headers,dos_header,old_header->OptionalHeader.SizeOfHeaders);
做重定位:
如果分配的首地址和OptionalHeader.ImageBase的值不同的话,那么就需要做一次重定向,不然代码是无法执行的。
locationDelta=(SIZE_T)(code-old_header->OptionalHeader.ImageBase);
如果Delta不为0,说明需要重定向:
isRelocated=PerformBaseRelocation(result,locationDelta)
建立导入表:
BuildImportTable(result)
依次遍历导入的dll项:
!IsBadReadPtr(importDesc,sizeof(IMAGE_IMPORT_DESCRIPTOR))&&importDesc->Name;importDesc++
通过名字把这个dll加载到内存:
loadLibrary((LPCSTR)(codeBase+importDesc->Name),module->userdata)
依次遍历导入的API,在内存中获取到对应的地址,如果地址为0,表示这个API在当前系统里是不支持的,则加载失败。
对不同地方内存区段做不同的内存保护,使用的API为VirtualProtect
FinalizeSections(result)
根据CPU的页面大小,再次对每个页面就行对其操作,并且使用设置不同的内存保护,让这些内存能以对应的方式访问。
执行TLS回掉函数
ExecuteTLS(result)
TLS回掉函数是需要在执行入口点函数(OEP)之前执行的。
获取到tls的偏移量。
PIMAGE_TLS_DIRECTORY tls=(PIMAGE_TLS_DIRECTORY)(codeBase+directory->VirtualAddress);
获取到TLS开始项的值
获取exe的OEP,代码的执行是从这里开始的。
result->exeEntry=(ExeEntryProc)(code+
result->headers->OptionalHeader.AddressOfEntryPoint);
到此,exe的镜像初始化工作已经完成。
接下来只需要使用LoadLibraryW主动加载保护dll,然后执行上面提及的OEP(入口函数),就能运行被保护的程序了:
module->exeEntry()
这个函数是不会立刻放回的,除非程序执行完毕。
这样做是为了反dump,如果使用dump软件,dump出的内存镜像只是加载器的,和受保护的exe镜像无关。这样加载器只是一个外壳,而真正的执行部分对操作系统来说是不可见的。
3、检查exe模块的IAT是否被劫持
该步骤包括以下两个子步骤:3.1)获取IAT中的dll带入列表,对其中每一个API地址做一次检查,看记录的API地址和实际的API是否一致,如果有不同的,则认为该exe模块的IAT被做了改动(可能是被Hook过的),接着,3.2)对每个导入的系统dll做一次镜像Hash检查,使用的Hash函数是zlib中的Adler32函数,如果内存镜像的Hash值和本地文件的Hash不同,则认为这个dll的内存镜像被修改过,
如果发现子步骤3.1)、3.2)存在问题,则返回至步骤3.1)的初始状态或返回到步骤2),如果子步骤3.1)、3.2)都没有问题,则执行第4步。
API是至关重要的,程序和系统的交互全靠API调用。如果API被劫持,程序的很多功能都能被随意修改。API方面的Hook分为两种,第一种是IAT Hook,第二种则是InlineHook。
进过第二步以后,除了延迟加载的dll,其他的dll文件都已经加载好了。
然后程序代码中使用LoadLibrary所加载的dll都是在保护dll加载之后才注入程序空间的。这个时候为了防止exe的IAT做了修改,嵌入了别的恶意dll,保护系统会做如下检查:
针对IAT Hook,对IAT做一次遍历,先检查每个API的的地址是否和备份中的值相同,如果不同的话,认为被保护的进程受到了IAT Hook。
主要的检查对象为系统dll(如:kernel32.dll、Gdi32.dll)
对于每一个表项都是如此获取的。
char*pszDllName=(char*)((BYTE*)hModule+pImportDesc->Name);
获取当前dll名字。
IMAGE_THUNK_DATA*pThunk=(IMAGE_THUNK_DATA*)((BYTE*)hModule+pImportDesc->OriginalFirstThunk);
char*pszFuncName=(char*)((BYTE*)hModule+pThunk->u1.AddressOfData+2);
pszFunName就是当前API的名字
PDWORD lpAddr=(DWORD*)((BYTE*)hModule+pImportDesc->FirstThunk)+n;
DWORD Addr=*(DWORD*)((DWORD)lpAddr);
Addr记录的是当前API的入口地址
其中hModule是exe内存镜像偏移量的开始。
然后保护程序自动获取到API本来的地址:
HMODULE hTemp=GetModuleHandleA(pszFuncName);
PDWORD OriAddr=(PDWORD)GetProcAddress(hTemp,pszFuncName);
如果OriAddr和Addr的值相同,我们就认为,API没有被IAT Hook。
针对InlineHook,需要到对用的系统dll段做一次检查。
因为用户自己的dll动态链接库可能做了smc或者代码虚拟化加密,故不做检查。
在上述的IAT检查中,对每次遍历到一个dll导入的时候,先判断是否是系统dll。
使用zlib中的Adler32Hash算法,对整个dll内存镜像模块做一次Hash计算,最后使用GetModuleFileNameW获取该dll的完整路径,然后对这个文件做一次Hash,如果二者Hash值不同的话,认为这个dll镜像被恶意修改。
4、主动劫持LoadLibrary和LoadLibraryEx(包含多字节版本和Unicode版本)
劫持LoadLibrary和LoadLibraryEx(这两个API都包含多字节版本和Unicode版本,所以实际上是4个API),对加载成果的dll镜像执行上述步骤3中的操作,如果判定有异常,则返回步骤2或步骤3,如果判定没有异常,则执行第5步。
exe模块每主动调用一个dll的时候,必定会使用到LoadLibrary或者是LoadLibraryEx,对这两个API做劫持,每当加载一个dll镜像的时候,使用步骤3中的方法,对dll的IAT做一次检查,如果有导入函数的地址和原本的地址不同,就认为这个dll的IAT被做了修改。
5、劫持CreateProcess函数
劫持CreateProcess函数,并且在创建子进程同时,注入本保护dll。如果该进程创建了子进程,本保护dll是和子进程无关的,这样一来,子进程是未受保护的状态。所以本保护劫持了CreateProcess函数,在创建子进程的时候,同时注入本保护dll。主要的方式就是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建。
原理就是在建立子进程的时候,劫持EIP,保存CPU的Context,注入ShellCode来加载内存保护dll,最后释放ShellCode的内存,并且恢复CPU的Context。这样注入之后,不仅子进程得到了保护,并且不会因为加载了保护dll而在子进程的内存镜像中留下痕迹。
如果该进程创建了子进程,本保护dll是和子进程无关的,这样一来,子进程是未受保护的状态。所以本保护劫持了CreateProcess函数,在创建子进程的时候,同时注入本保护dll。
具体而言,主要的方式就是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建:
dwCreaFlags=dwCreationFlags|CREATE_SUSPENDED;
保存当前的Context值。
将CPU寄存器的值依次保存到CONTEXT里面。
开辟在子进程中,使用VirutalAlloc开辟一段长度大小为0x1000的内存,清空为0.
劫持EIP,注入本保护dll:
将EIP的值改为之前VirtualAlloc的分配内存的首部。
一次输入以下的ShellCode
Push offset
Call LoadLibraryW
其中offset是欲注入的dll名字的字符串地址,因为以上的指令只需要花费10字节的空间,所以在
分配的内存0x20处写入字符串,而offset就是这个字符串的地址。
而LoadLibraryW就是LoadLibraryW的地址,这样就能保证子进程能加载到保护dll。
执行ShellCode:
使用ResumeThread(hThread)恢复主线程,并执行这段ShellCode。
恢复当前Context值:
将CPU寄存器的值恢复,因为Win32API都是stdcall,所有加载保护dll之后不需要主动去清理堆栈。然后使用SuspendThread这个API暂停主线程。
做清理工作,释放用VirtualAlloc分配出来的内存。
如果当前原始的创建flag中就有CREATE_SUSPEND,则直接返回结果;如果没有这个flag,在返回结果之前需要恢复主线程。
由于步骤5不是立刻能获取到结果的,就继续执行第六步。因此,上述方法还可进一步包括以下步骤
6、防止线程注入
通过远程线程注入,在Ring3环境下注入被保护的目标进程,防止恶意加载器的线程注入。
也就是说,到此为止,如果还想在Ring3环境下注入被保护的目标进程,方法只有一种,那就是远程线程注入。恶意加载器在远程调用以下三种CreateRemoteThread、VirtualAlloc和LoadLibrary相关的API(如LoadLibraryEx)就行完成线程注入。
CreateRemoteThread注入dll的时候,这个时候,在内核会为被注入的进程创建一个线程。自然而然,所有已加载的dll会在DllMain入口点收到DLL_THREAD_ATTACH的通知,此时调用GetCurrentThreadId的时候就能获取当前创建的线程id。接着保护dll从ntdll处HookZwCreateThreadExZwTerminateThread,系统可以劫持到本地创建的全部线程。
其中,对这些值的维护,保护系统使用stl中的map来维护ThreadId的值。
一旦有一个本地的线程建立,操作系统的ZwCreateThreadEx函数一定会被调用,保护系统将从map中加入当前值。
一旦有一个本地线程主动退出或者被动退出,操作系统会自动调用ZwTerminateThread这个API函数,保护系统会从map中删除这个值。
当保护dll的DllMain入口点收到DLL_THREAD_ATTACH的通知,此时调用GetCurrentThreadId就能检查是否是当前进程创建的线程,如果不是,那么就是远程注入的。
但是因为不是本地调用的,就算我们在本地是无法通过劫持这三个API达到监视目的的。然而全局Hook不仅可能不稳定,还可能大幅度降低系统的运行速度,这个做法也是不可取的。
然而,在exe被远程调用CreateRemoteThread注入dll的时候,这个时候,在内核会为被注入的进程创建一个线程。自然而然,所有已加载的dll会在DllMain入口点收到DLL_THREAD_ATTACH的通知,此时调用GetCurrentThreadId的时候就能获取当前创建的线程id。接着保护dll从ntdll处Hook ZwCreateThreadEx ZwTerminateThread,系统可以劫持到本地创建的全部线程。
其中,对这些值的维护,保护系统使用stl中的map来维护ThreadId的值。
一旦有一个本地的线程建立,操作系统的ZwCreateThreadEx函数一定会被调用。
一旦有一个本地线程主动退出或者被动退出,操作系统会自动调用ZwTerminateThread这个API函数。
map<ULONG,ULONG>ThreadTree;
在这,我们可以先判断一下,整个进程是不是由Debugger启动的:
STARTUPINFOW si;
GetStartupInfoW(&si);
获取该应用程序的启动信息,如果其中的si.dwFillAttribute值不为0,就认为该进程是由Debugger启动的,如果不是,则继续。
获取当前进程的主线程。
加载器不会启动本身不会启动附带的线程,别的dll线程对于加载器都是透明的,所以保护系统使用时间比较的方式找出当前系统的主线程。
先使用Thread32First这个API遍历当前系统中的全部API。
THREADENTRY32pe32={sizeof(pe32)};
if(Thread32First(hThreadSnap,&pe32)){…}
对于每个API使用GetThreadTimes获取创建时间:
GetThreadTimes(handle,&createtime,&time2,&time3,&time4);
最后再遍历一次全部Thread的创建时间,最小的那个就是当前进程的主线程,因为它最早被创建。
将这个值添加到ThreadTree中。
全部的用户线程创建都是从ZwCreateThreadEx提交到内核进行创建的,同理ZwTerminateThread也是用于结束对用的代码。
和公开的CreateThread不同的是,ZwCreateThreadEx可以提交进程的Handle来指定为哪个进程创建线程。只要进程为自己创建了一个进程,就在ThreadTree中插入该线程id,同理,调用ZwTerminateThread的时候也从查找树中删除对应的记录。
当保护dll的DllMain入口点收到DLL_THREAD_ATTACH的通知,此时调用GetCurrentThreadId就能检查是否是当前进程创建的线程,如果不是,那么就是远程注入的。
考虑到有些和安全相关的进程,不得不使用线程注入来保证自己的安全性能,所以系统主动劫持了位于ntdll中的LdrpLoadDll来检查当前注入系统的dll镜像是否是受验证的,或者是自己希望的添加的dll动态链接库。保护系统提供了QueryLisence的函数接口来过滤线程注入的dll。
因为这个步骤6不能立刻获取到结果,所以做完底层劫持以后,直接执行第7步。
7、建立保护线程
建立一个优先度比较低的线程(THREAD_PRIORITY_BELOW_NORMAL),做死循环检查,为了防止影响应用程序的性能,所以每一秒检查一次。检查的内容有:是否是Debugger加载。
直接从BYTE Ptr fs:[30]+2处获取到一个BYTE长度的数据,如果检测到flag为1的时候,使用SetUnhandledExceptionFilter设置一个Filter函数,然后返回EXCEPTION_EXECUTE_HANDLER,表明当前的异常已经处理,程序可以直接退出了。这样就可以做到反Debugger的功能。
如果Debugger能够很简单地加载被保护的应用程序的话,破解者可以直接在Debugger中修改,最后dump内存镜像到达破解的目的。
检查Debugger的最简单方式就是调用Kernel32下的IsDebuggerPresent API函数进行判断,但是这种调用API的方式不是最好的,如果破解者从别的地方恶意修改到了这个API,那么Debugger检查也相当于形同虚设。
当一个Debugger连接上目标进程的时候,进程中的PEB中的Debugger Flag会被置为1,也就是BYTE Ptr fs:[30]+2处的数据会被置为1。保护器可以直接监视这个数值以达到监视Debugger注入的功能。
保护器使用eax的低8位来保存这个flag。
首先保持eax寄存器的值,然后将这个flag复制到al寄存器中,即eax寄存器的低8位。最后把al寄存器的值保存到DebuggerFlag中并且恢复eax的原始值。
如果检测到flag为1的时候:
使用SetUnhandledExceptionFilter设置一个Filter函数,函数中可以输出一些出错信息,然后返回EXCEPTION_EXECUTE_HANDLER,表明当前的异常已经处理,程序可以直接退出了。
SetUnhandledExceptionFilter(DebuggerFilter)
这样,系统达到了反Debugger的功能。
反Dump:
如果加载器使用了虚拟化之类的加密技术,那么dump出来镜像也是不可运行的。
如果加载器没有做任何加密,问题也不大,因为本发明的加载方式可以保证受保护的文件不能直接被dump,因为当前运行进程是加载器的进程,如果不使用Debugger进行调试,受保护程序的代码段不能直接通过API获取到。
采用本专利可以达到如下有益效果:
当前保护界中,一般有三种保护手段:
第一种是加壳,其中分为加密壳和压缩壳。
第二种是驱动保护。
第三种是代码虚拟化技术。
加壳保护保护能有效防止受保护对象被静态反编译或者是一定程度上防止动态调试。但是因为保护较弱,现在一般不会单独拿出来使用。
而具有强大保护功能的代码虚拟化技术本质上就是将本地代码转换成虚拟机代码,然后在运行的时候又即时翻译成本地代码。这种技术能有效防止代码被静态编译或者是代码被动态调试,因为调试器无法识别对方镜像中的代码。但是代码虚拟化技术不具有保护API的功能。由于代码需要及时翻译才能执行,代码虚拟化后,受保护镜像的运行速度会大幅度降低。所以这种保护方式只适用于较小的软件产品而且对执行速度要求不高。
本发明的优点:
1、本发明提供了一种新的方式,将代码植入加载器中。由于加载器加载镜像的时候是随机定位的,并且受保护镜像对操作系统是透明的,所以目前的进程监视器是无法监视到受保护镜像的。并且本专利使用了dll劫持作为镜像保护的方法,从镜像加载前就获取到镜像的控制权,对受保护镜像进行监视,能有效防止受保护镜像受到恶意修改。再加上受保护镜像是完全加密的,所以调试器是完全无法加载受保护的镜像,从根本上防止了反调试。
2、本发明使用的保护机制都是快速的,甚至能使保护后代码执行的速度比保护前更快。而本发明适用的范围就是软件受保护的同时,还能快速执行的场合。
附图说明
图1为:加密镜像所使用的文件头。
图2为:本发明使用的加密算法,每隔32个字节更新一次加密数组。
图3为:PE文件头文本提及的部分。
图4为;尝试使用OllyDbg加载受保护的镜像。
图5为;已经加载的镜像模块。
图6为:查看本进程的内存区域,找不到可疑的模块。
图7为:直接使用Debugger加载CrystalLoader的时候,会检查到当前进程是由Debugger启动的。
图8为:被线程注入之后的结果。
图9为:在保护线程中启动子进程。
图10为:保护进程主动加载一个IAT被Hook过的Dll动态链接库。
图11为:在保护进程运行中,植入Debugger后的结果。
图12为:保护程序在windows 10中运行。
图13为:保护程序在windows 8.1中运行。
图14为:保护程序在windows xp中运行。
图15为:保护程序在windows 2003中运行。
图16为:保护前和保护后程序运行速度。
具体实施方式
下面结合实例对本发明做进一步说明:
实施例:
使用的平台为Windows7 64bit版本,使用的调试器为OllyDbg V2.01版本。
测试的步骤如下:
使用OllyDbg加载AppTestWin32.vpe这个保护镜像:
如图4所示,反汇编器无法分析这个镜像文件,在打开的16进制编辑器中,也无法看见熟悉的PE镜像格式。
去掉Debugger检测保护,在已经加载的镜像模块中,如图5所示,找不到受保护的镜像模块,所以Dumper也无法准确抓去内存镜像文件。打开本进程的内存Map,也找不到可疑的区段。受保护的模块对于操作系统是隐藏的。
加上全部的保护,再次使用OllyDbg加载CrystalLoader.exe,发现会直接如图7的错误,当前系统已经发现自己是由一个Debugger加载的,就提示错误并且拒绝执行。
测试线程注入:
运行CrystalLoader.exe使用loader的形式注入一个dll,如图8所示。
当保护dll检测到一个未记录的线程被添加进来的时候,会直接报错。
测试子进程保护:
关闭防止线程注入保护,在受保护的进程中,创建一个新的进程。
如图9所示,可以看见CrystalProtector.dll成果注入子进程空间中,对子进程开始执行保护代码。
测试dll保护验证:
在受保护的镜像代码中,主动加载一个IAT被修改的dll。
如图10所示,进程主动提示dll是被修改过的,是不合法的。
在受保护镜像运行的时候,植入Debugger进程,如图11所示,会同时提示系统被Debugger注入和系统检测到线程注入。
图12到图15,分别给出了保护程序在windows10,windows 8.1,windows xp,windows 2003这几个主流平台上运行的例子。
图16给出了对比测试。图片上面是未经过保护的原始程序运行的结果,图片下面是进过保护后,程序执行的结果。两段程序都执行的是蒙特卡洛算法求圆周率,枚举的范围从0到0xFFFFFF,为了防止CPU Cache对时间的准确性的影响,先将程序预先加载一次,然后在依次执行保护和未保护的程序。可以看到,由于加载器对做了足够的优化并且选择性模拟c runtime library的功能,使得程序能在受保护情况下,得到更加高效的执行结果。

Claims (29)

1.基于隐藏可执行镜像并注入dll保护镜像代码的方法,该方法包括以下步骤:
1)将目标镜像在内存中解密:
将加密镜像读取到内存,读取文件首部的文件头来判断目标镜像是否是加密镜像,1.1)如果不是指定的加密镜像,则放弃加载;1.2)如果是指定的加密镜像,则执行对应的解密,然后判断解密后的数据是否是一个合法PE镜像,1.2.1)如果不是一个合法PE镜像,则退回到待解密状态,1.2.2)如果是一个合法PE镜像,则执行第2步;
2)PE镜像的加载:
装载程序所需要的dll文件,执行镜像重定位,最后执行OEP,如果装载程序所需要的dll文件、执行镜像重定位、最后执行OEP的过程有出错,则返回到本步骤2)的待装载状态,如果装载程序所需要的dll文件、执行镜像重定位、最后执行OEP的过程都没有出错,就执行第3步;
3)检查exe模块的IAT是否被劫持:
该步骤包括以下两个子步骤:3.1)获取IAT中的dll带入列表,对其中每一个API地址做一次检查,看记录的API地址和实际的API是否一致,如果有不同的,则认为该exe模块的IAT被做了改动,接着,3.2)对每个导入的系统dll做一次镜像Hash检查,使用的Hash函数是zlib中的Adler32函数,如果内存镜像的Hash值和本地文件的Hash不同,则认为这个dll的内存镜像被修改过,
如果发现子步骤3.1)、3.2)存在问题,则返回至步骤3.1)的初始状态或返回到步骤2),如果子步骤3.1)、3.2)都没问题,则执行第4步;
4)主动劫持LoadLibrary和LoadLibraryEx:
劫持LoadLibrary和LoadLibraryEx,它们分别包含多字节版本和Unicode版本,所以需要劫持的API实际上有4个,对加载成果的dll镜像执行上述步骤3)中的操作,如果判定有异常,则返回步骤2)或步骤3),如果判定没有异常,则执行第5步;和
5)劫持CreateProcess函数:
劫持CreateProcess函数,并且在创建子进程同时,注入本保护dll。
2.根据权利要求1所述的方法,其特征在于:步骤1)中所述文件首部的文件头为文件前16字节;判断目标镜像是否是加密镜像为判断Magic值是否和预设值相同;
步骤2)中所述待装载状态为PE镜像的待加载状态;
步骤3)中所述改动为被Hook过的。
3.根据权利要求1或2所述的方法,其中所述方法进一步包括以下步骤
6)防止线程注入:
通过远程线程注入,在Ring3环境下注入被保护的目标进程,防止恶意加载器的线程注入;
7)建立保护线程:
建立一个优先度比较低的线程,做死循环检查,其中检查的内容包括:是否是Debugger加载。
4.根据权利要求3所述的方法,其特征在于:所述优先度比较低的线程为THREAD_PRIORITY_BELOW_NORMAL;所述检查为每一秒检查一次。
5.根据权利要求1或2所述的方法,其中在步骤1中加密镜像的扩展名使用*.vpe,和/或,执行对应的解密是指:解密的算法基于快速的xor算法,将文件头Null作为解密Key,对文件进行解密。
6.根据权利要求1、2或4所述的方法,其中步骤2)包括以下子步骤:2.1)加载器会模拟操作系统的加载器,依次修改PE镜像的区段的属性,并且对齐区段,2.2)执行预加载函数,加载IAT中对应的dll动态链接库,然后获取到OEP,和2.3)注入保护dll,执行入口点函数,从而完成PE镜像的加载。
7.根据权利要求3所述的方法,其中步骤2)包括以下子步骤:2.1)加载器会模拟操作系统的加载器,依次修改PE镜像的区段的属性,并且对齐区段,2.2)执行预加载函数,加载IAT中对应的dll动态链接库,然后获取到OEP,和2.3)注入保护dll,执行入口点函数,从而完成PE镜像的加载。
8.根据权利要求5所述的方法,其中步骤2)包括以下子步骤:2.1)加载器会模拟操作系统的加载器,依次修改PE镜像的区段的属性,并且对齐区段,2.2)执行预加载函数,加载IAT中对应的dll动态链接库,然后获取到OEP,和2.3)注入保护dll,执行入口点函数,从而完成PE镜像的加载。
9.根据权利要求6所述的方法,其中预加载函数为TLS回调函数。
10.根据权利要求7或8所述的方法,其中预加载函数为TLS回调函数。
11.根据权利要求6所述的方法,其中所加载的受保护的PE镜像是基于加载器进程执行的,即,受保护的PE镜像对于操作系统来说是不可见的。
12.根据权利要求7-9中任一项所述的方法,其中所加载的受保护的PE镜像是基于加载器进程执行的,即,受保护的PE镜像对于操作系统来说是不可见的。
13.根据权利要求10所述的方法,其中所加载的受保护的PE镜像是基于加载器进程执行的,即,受保护的PE镜像对于操作系统来说是不可见的。
14.根据权利要求1-2、4、7-9、11、13中任何一项所述的方法,其中在步骤4)中,每当exe模块主动调用一个dll时,必定会使用到LoadLibrary或者是LoadLibraryEx,对LoadLibrary和LoadLibraryEx的API做劫持,每当加载一个dll镜像时,使用步骤3)中的方法,对dll的IAT做一次检查,如果有导入函数的地址和原本的地址不同,就认为这个dll的IAT被做了修改。
15.根据权利要求3所述的方法,其中在步骤4)中,每当exe模块主动调用一个dll时,必定会使用到LoadLibrary或者是LoadLibraryEx,对LoadLibrary和LoadLibraryEx的API做劫持,每当加载一个dll镜像时,使用步骤3)中的方法,对dll的IAT做一次检查,如果有导入函数的地址和原本的地址不同,就认为这个dll的IAT被做了修改。
16.根据权利要求5所述的方法,其中在步骤4)中,每当exe模块主动调用一个dll时,必定会使用到LoadLibrary或者是LoadLibraryEx,对LoadLibrary和LoadLibraryEx的API做劫持,每当加载一个dll镜像时,使用步骤3)中的方法,对dll的IAT做一次检查,如果有导入函数的地址和原本的地址不同,就认为这个dll的IAT被做了修改。
17.根据权利要求6所述的方法,其中在步骤4)中,每当exe模块主动调用一个dll时,必定会使用到LoadLibrary或者是LoadLibraryEx,对LoadLibrary和LoadLibraryEx的API做劫持,每当加载一个dll镜像时,使用步骤3)中的方法,对dll的IAT做一次检查,如果有导入函数的地址和原本的地址不同,就认为这个dll的IAT被做了修改。
18.根据权利要求1-2、4、7-9、11、13、15-17中任何一项所述的方法,其中在步骤5)中,主要的方式是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建。
19.根据权利要求3所述的方法,其中在步骤5)中,主要的方式是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建。
20.根据权利要求5所述的方法,其中在步骤5)中,主要的方式是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建。
21.根据权利要求6所述的方法,其中在步骤5)中,主要的方式是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建。
22.根据权利要求14所述的方法,其中在步骤5)中,主要的方式是在创建进程的时候,先保存原来的创建flag,为创建flag中添加CREATE_SUSPEND的flag进行创建。
23.根据权利要求18所述的方法,其中:在建立子进程的时候,劫持EIP,保存CPU的Context,注入ShellCode来加载内存保护dll,最后释放ShellCode的内存,并且恢复CPU的Context。
24.根据权利要求19-22中任何一项所述的方法,其中:在建立子进程的时候,劫持EIP,保存CPU的Context,注入ShellCode来加载内存保护dll,最后释放ShellCode的内存,并且恢复CPU的Context。
25.根据权利要求3所述的方法,其中在步骤6)中,需要防止的线程注入是:恶意加载器在远程调用以下三种CreateRemoteThread、VirtualAlloc和LoadLibrary相关的API完成线程注入。
26.根据权利要求4、7、15、19中任何一项所述的方法,其中在步骤6)中,需要防止的线程注入是:恶意加载器在远程调用以下三种CreateRemoteThread、VirtualAlloc和LoadLibrary相关的API完成线程注入。
27.根据权利要求3所述的方法,其中在步骤7)中,直接从BYTE Ptr fs:[30]+2处获取到一个BYTE长度的数据,如果检测到flag为1,则使用SetUnhandledExceptionFilter设置一个Filter函数,函数中输出一些出错信息,然后返回EXCEPTION_EXECUTE_HANDLER,表明当前的异常已经处理,程序直接退出。
28.根据权利要求4、7、15、19、25中任何一项所述的方法,其中在步骤7)中,直接从BYTEPtr fs:[30]+2处获取到一个BYTE长度的数据,如果检测到flag为1,则使用SetUnhandledExceptionFilter设置一个Filter函数,函数中输出一些出错信息,然后返回EXCEPTION_EXECUTE_HANDLER,表明当前的异常已经处理,程序直接退出。
29.根据权利要求26所述的方法,其中在步骤7)中,直接从BYTE Ptr fs:[30]+2处获取到一个BYTE长度的数据,如果检测到flag为1,则使用SetUnhandledExceptionFilter设置一个Filter函数,函数中输出一些出错信息,然后返回EXCEPTION_EXECUTE_HANDLER,表明当前的异常已经处理,程序直接退出。
CN201510873591.8A 2015-12-02 2015-12-02 基于隐藏可执行镜像并注入dll保护镜像代码的方法 Active CN105512548B (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN201510873591.8A CN105512548B (zh) 2015-12-02 2015-12-02 基于隐藏可执行镜像并注入dll保护镜像代码的方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN201510873591.8A CN105512548B (zh) 2015-12-02 2015-12-02 基于隐藏可执行镜像并注入dll保护镜像代码的方法

Publications (2)

Publication Number Publication Date
CN105512548A CN105512548A (zh) 2016-04-20
CN105512548B true CN105512548B (zh) 2018-03-30

Family

ID=55720522

Family Applications (1)

Application Number Title Priority Date Filing Date
CN201510873591.8A Active CN105512548B (zh) 2015-12-02 2015-12-02 基于隐藏可执行镜像并注入dll保护镜像代码的方法

Country Status (1)

Country Link
CN (1) CN105512548B (zh)

Cited By (1)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN108491237A (zh) * 2018-03-29 2018-09-04 山东华软金盾软件股份有限公司 一种隐蔽的Dll文件注入方法

Families Citing this family (6)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN109086183B (zh) * 2018-07-12 2022-08-16 武汉斗鱼网络科技有限公司 一种应用程序的监控方法、装置、电子设备及存储介质
CN110968867B (zh) * 2018-09-29 2022-04-08 武汉斗鱼网络科技有限公司 防止不良dll注入的方法、存储介质、电子设备及系统
CN111625296B (zh) * 2020-05-27 2023-03-14 重庆夏软科技有限公司 一种通过构建代码副本保护程序的方法
CN112632536B (zh) * 2020-12-22 2023-03-21 四川大学 基于pe文件改造的内存加载方法
CN115543586B (zh) * 2022-11-28 2023-03-17 成都安易迅科技有限公司 应用层系统进程的启动方法、装置、设备及可读存储介质
CN116775147B (zh) * 2023-06-08 2024-03-15 北京天融信网络安全技术有限公司 一种可执行文件处理方法、装置、设备及存储介质

Family Cites Families (4)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US8272052B2 (en) * 2009-01-15 2012-09-18 The Garden City Group, Inc. Method and system for filing and monitoring electronic claim submissions in multi-claimant lawsuits
CN103116714A (zh) * 2013-03-01 2013-05-22 中标软件有限公司 一种Windows平台可执行文件双进程保护方法
CN104036183B (zh) * 2013-05-17 2015-04-08 腾讯科技(深圳)有限公司 沙箱内安装软件的方法和系统
CN104123492A (zh) * 2014-07-21 2014-10-29 蓝盾信息安全技术有限公司 一种Windows的进程保护方法

Cited By (2)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN108491237A (zh) * 2018-03-29 2018-09-04 山东华软金盾软件股份有限公司 一种隐蔽的Dll文件注入方法
CN108491237B (zh) * 2018-03-29 2020-11-27 山东华软金盾软件股份有限公司 一种隐蔽的Dll文件注入方法

Also Published As

Publication number Publication date
CN105512548A (zh) 2016-04-20

Similar Documents

Publication Publication Date Title
CN105512548B (zh) 基于隐藏可执行镜像并注入dll保护镜像代码的方法
Salamat et al. Orchestra: intrusion detection using parallel execution and monitoring of program variants in user-space
US8261359B2 (en) Systems and methods for preventing unauthorized use of digital content
Yang et al. Appspear: Bytecode decrypting and dex reassembling for packed android malware
Volckaert et al. Cloning your gadgets: Complete ROP attack immunity with multi-variant execution
US8015608B2 (en) Systems and methods for preventing unauthorized use of digital content
EP2979219B1 (en) Suspicious program detection
KR101054318B1 (ko) 정보 처리 시스템 및 프로그램을 기록한 컴퓨터로 읽을 수 있는 매체
Zeng et al. Cruiser: concurrent heap buffer overflow monitoring using lock-free data structures
CN107690645A (zh) 使用解释器虚拟机的行为恶意软件检测
US20160275019A1 (en) Method and apparatus for protecting dynamic libraries
CN109255235B (zh) 基于用户态沙箱的移动应用第三方库隔离方法
CN105608391A (zh) 多elf文件保护方法及系统
US9047448B2 (en) Branch auditing in a computer program
Hawkins et al. Dynamic canary randomization for improved software security
Huang et al. Return-oriented vulnerabilities in ARM executables
de Assumpção et al. Forensic method for decrypting TPM-protected BitLocker volumes using Intel DCI
Piromsopa et al. Survey of protections from buffer-overflow attacks
EP3229164B1 (en) Devices for measuring and verifying system states
Nasim et al. Uncovering self code modification in Android
Gülmez et al. Rewind & Discard: Improving software resilience using isolated domains
Salamat Multi-Variant Execution: Run-Time Defense against Malicious Code Injection Attacks DISSERTATION
AU2002219852B2 (en) Systems and methods for preventing unauthorized use of digital content
Roth et al. Implicit buffer overflow protection using memory segregation
AU2002219852A1 (en) Systems and methods for preventing unauthorized use of digital content

Legal Events

Date Code Title Description
C06 Publication
PB01 Publication
C10 Entry into substantive examination
SE01 Entry into force of request for substantive examination
GR01 Patent grant
GR01 Patent grant