博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
插桩valgrind_狡猾的恶意代码:浅谈反插桩技术
阅读量:6654 次
发布时间:2019-06-25

本文共 3234 字,大约阅读时间需要 10 分钟。

2: kd> bc 02: kd> gBreakpoint 1 hit001b:012cd4b0 8955f4 mov dword ptr [ebp-0Ch],edx2: kd> reax=c000000d ebx=7ffdb000 ecx=0023fcac edx=012cd4b0 esi=00000000 edi=0023fd88eip=012cd4b0 esp=0023fcac ebp=0023fd88 iopl=0 nv up ei ng nz na pe nccs=001b ss=0023 ds=0023 es=0023 fs=0030 gs=0000 efl=00000286001b:012cd4b0 8955f4 mov dword ptr [ebp-0Ch],edx ss:0023:0023fd7c=cccccccc2: kd> u .001b:012cd4b0 8955f4 mov dword ptr [ebp-0Ch],edx

上述结果显示,int 0x2e返回时,edx寄存器保存有当前的eip数值,即0x012cd4b0,也就是即将执行的mov指令地址。

而在插桩状态下,由于代码缓存的引入,int 0x2e执行完成后,其edx则会指向代码缓存的内存区,而不会指向原程序的内存地址区域。

由此可见,可利用int 0x2e指令,检测指令返回时的edx,实现对插桩平台的检测。

自修改代码

自修改代码(Self-modifying code,SMC)是指在程序动态运行期间,修改自身的指令。主要应用场景为各类病毒用来躲避杀软的查杀,达到保护自身的目的。

如果程序位于动态二进制插桩的执行状态,由于其执行的是位于代码缓存中的指令,也就意味着其执行的指令是并未经过修改的指令,从而导致程序的执行与其预期存在差异。利用这种执行的差异性,即可实现对插桩平台的检测。

如下图所示:

在插桩执行状态下,原始指令被拷贝至代码缓存

执行代码缓存内的指令ins1,由于其自修改代码的特性,会修改原始指令的wrong_ins3为ins3,而代码缓存内的指令并未发生改变

随着程序的执行,最终执行了位于代码缓存内的wrong_ins3,触发了crash。由于插桩平台的代码缓存特性,造成了执行的差异性。

因此,利用这种自修改代码的特性,结合具体指令类型,可有多种不同变换的反插桩方法,但其道理相通。

基于环境特征的反插桩技术

PE特征

如图所示,观察到pin和dynamorio在插桩时,分别会将pinvm.dll和dynamorio.dll注入进程内。

以pinvm.dll为例,其包含的特殊字符串和汇编代码片段等均可作为检测的依据。

pinvm.dll特殊字符串:

pinvm.dll特殊汇编代码片段:

pinvm.dll导出函数:PinWinMain等

pinvm.dll的section name:.charmve

除了pinvm.dll之外,pin在分析时,还会将用户根据需求自行编写开发的pintools注入到进程中,其导出函数和section names 等特征也可作为插桩环境的检测依据:

父进程

由于目标进程是由动态二进制插桩平台启动进行分析的,因此目标进程是DBI平台的子进程。换言之,目标进程可以通过检测父进程来判断插桩平台是否存在。例如,Pin和DynamoRIO在分析时,目标程序的父进程信息如下:

基于JIT编译的反插桩技术

何为JIT编译?

JIT,just-in-time,翻译为即时编译,是一种动态编译技术。通常,编译技术包括静态编译和动态编译。静态编译的程序在执行前已被全部翻译为机器码,然后再载入运行;动态编译则更加强调“运行时”的概念,边运行边翻译。

在JIT编译下,二进制程序的原始汇编代码加载到内存中,作为插桩变换的参考底本,其本身永远不会被执行。真正执行则是经过JIT编译、位于code cache中插桩变换后的指令。

因此,JIT编译自身的某些特征特性可作为检测的依据。

内存页权限 WX

JIT编译所需的前提条件是能够将编译后的指令代码写入(W)内存中然后执行指令(X),因此,插桩程序的所具备RWX属性的内存页数量要高于未经插桩的程序。如下图所示,分别展示了pin插桩前后,calc.exe的内存页状态:

未经pin插桩的calc.exe:

经过pin插桩的calc.exe:

因此,可以通过扫描整个进程地址空间来计算标记为RWX属性的内存页数量,如果数量明显高于正常程序的结果,则可判定存在插桩分析。

DLL HOOK

DBI平台需要使用HOOK函数,从而在未经插桩的trace尾部拦截程序的执行。具体而言,DBI需要在某些函数的入口处插入jmp跳转指令(Hook)。如下图所示,在pin插桩前后,ntdll.KiUserApcDispatcher函数的入口变化如下:

未经pin插桩:

经过pin插桩后:

除此之外,具有类似情况的函数还包括:KiUserCallbackDispatcher、KiUserExceptionDispatcher、LdrInitializeThunk等。

Memory Allocation

正如前文所提到的,JIT编译需要使用RWX的内存页来进行指令的插桩变换和缓存。正因如此,DBI平台需要通过申请内存来满足其需求。而其所有的内存申请操作都是通过底层的ZwAllocateVirtualMemory函数调用所实现的。基于此行为特征,可以通过统计ZwAllocateVirtualMemory的调用次数,与无插桩时的执行状态相对比,实现对DBI的检测。

其中参数特征为:

ZwAllocateVirtualMemory=> AllocationType = MEM_COMMIT | MEM_RESERVE=>Protect = PAGE_EXECUTE_READWRITE

其他反插桩技术

运行时开销

毫无疑问,插桩技术在提供灵活的分析手段的同时,也会增加程序运行时的开销,主要表现在程序执行的时间上。

尽管各插桩平台努力提高插桩分析的性能,但这种运行时的开销仍然可以作为反插桩检测的依据。

利用NtQueryPerformanceCounter、GetTickCount、timeGetTime等API函数或者KUSER_SHARED_DATA数据结构来获取Windows Time,识别插桩环境。

示例如下:

Start = GetTickCount;...//执行耗时的操作Stop = GetTickCount;TimeUsed = Stop-Start;if(TimeUsed > Threshold){FindDBIandGoDie//检测到DBI}

也可通过rdtsc指令来获取CPU Time。

不支持指令

在pin平台的pinvm.dll中存在以下提示信息,说明pin插桩平台并不支持FAR RET

可以利用该指令触发的异常,来实现插桩平台检测。

总结

本文对多种反插桩技术进行梳理与分析,旨在为安全研究人员提供一些思路参考。

实际情况下,对不同插桩平台的检测手段多种多样,本文的不足与疏漏之处,敬请批评指正,共同学习提高。

参考资料

Hiding PIN’s Artifacts to Defeat Evasive Malware

Dynamic Binary Instrumentation Frameworks: I know you’re there spying on me

ACTIVE DETECTION AND ESCAPE OF DYNAMIC BINARY INSTRUMENTATION

Measuring and Defeating Anti-Instrumentation-Equipped Malware返回搜狐,查看更多

转载地址:http://qlvto.baihongyu.com/

你可能感兴趣的文章
【sql】连续出现至少3次的数 Consecutive Numbers
查看>>
交替取值判断第一个人能否赢过第二个人 Predict the Winner
查看>>
我的友情链接
查看>>
nginx安装lua/replace-filter-nginx-module
查看>>
图像模式识别 (二)
查看>>
iptables发布nfs服务器的配置及NFS简易配置
查看>>
NDK r7 的新特性
查看>>
安装SQLSERVER2008R2保持和SQL2000独立运行的安装过程
查看>>
Heartbeat+DRBD+mysql高可用方案
查看>>
smartbits的国产版本minismb-使用burst模式
查看>>
Datastore content not visible for multiple Hosts
查看>>
【深入浅出MyBatis系列七】分页插件
查看>>
成为JavaGC专家(2)—如何监控Java垃圾回收机制
查看>>
360软件的×××查杀、漏洞修复等组件不能使用,提示runtime error
查看>>
我的友情链接
查看>>
(转载)Spark-Streaming获取kafka数据的两种方式-Receiver与Direct的方式
查看>>
oracle锁表解锁
查看>>
土狗的小抄本 -- JVM工具集命令
查看>>
Redhat 使用Yum安装更新rpm包
查看>>
nginx教程全集汇总
查看>>