[原创]病毒木马常用手段之Debug Blocker
2022-6-14 00:01 10591
在Debug Blocker中,调试进程与被调试进程它们之间是一种父子关系
文章中提到的父进程和子进程对应调试器和被调试进程
Debug Blocker是一种高级的反调试技术,而且这种技术常常被木马病毒利用
Debug Blocker是进程以调试方式运行自身或者其他可执行文件的技术。目的是增加逆向分析难度
1.父子关系
根据进程当时所扮演的不同角色(调试与被调试的关系),会执行不同的代码分支,并做出不同的动作
2.子进程不能再被其他调试器调试
在Windows中,一个进程无法同时被多个调试器调试,换言之,此时无法用OD进行调试(后面会讲如何调试)
3.父进程会影响子进程的执行
在Dbug Blocker技术中,调试器用来操纵被调试进程的执行分支,对子进程进行修改等操作,而且是一个连续的过程,换言之,在缺少调试进程的前提下,单独调试子进程无法正常运行
4.骨肉相连(这不是一道菜)
如果强制终止调试进程,那么子进程也会被终止,这也是Debug Blocker技术中非常高明的一点(仔细体会)
5.父进程处理子进程的异常
调试器和被调试者关系中,被调试进程发生的所有异常都由调试器处理,子进程故意触发异常(如内存访问异常),如果没有得到处理,程序将崩溃。子进程发生异常时,控制权转移到父进程,此时父进程修改被调试进程的执行分支,也可以对被调试进程进行加解密操作,或者修改寄存器、栈等特定值
基于以上特点,Debug Blocker 是一种比较高级的反调试手段。而且必须对调试进程进行分析调试,确定调试进程是如何处理异常的(执行逻辑),这样才能准确获取被调试进程的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
|
进入main函数之后,先是调用CreateMutexW创建互斥体,然后根据返回值判断是以父进程运行还是以子进程运行,如果返回值是0xB7(0xB7说明已经有一个互斥体),就以子进程运行,此时程序是第一次运行,所以会执行父进程
进入父进程函数之后,先调用GetMouduleHandleW和GetModuleFileNameW获取到文件路径,然后调用CreateProcessW通过传入DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS参数以调试方式创建子进程
一般使用了Debug Blocker技术的程序,其核心代码基本上在子进程运行,所以我们重新运行程序,在判断CreateMutexW返回值的时候我们修改ZF标志位强制跳转到子进程函数。
可以看到将要执行的指令是LEA EAX,EAX ,很明显是一条非法指令,(而且后面的指令不是正常的指令),会触发异常,从而将程序执行流程转移到父进程,父进程可能对此处的代码执行解密操作或者修改EIP为其他地址。
这是第一次异常,我们自己一共预设了两个异常
以调试方式创建子进程后,会调用WaiteForDebugEvent等待子进程发生Debug事件,(此处会涉及到Debug_EVENT结构体和调试相关的知识,不清楚的同学请自行补课)
然后根据WaiteForDebugEvent返回的信息进行三次判断,第一次判断是否属于调试事件(EXCEPTION_DEBUG_EVENT),第二次判断发生异常的原因是否属于执行无效指令(EXCEPTION_ILLEGAL_INSTRUCTION),第三次判断发生异常的地址是否是我们提前预设好的地址(0x0040103F),如果这三个条件都成立,那么继续往下执行ReadProcessMemory,否则继续等待,直到条件成立
通过在第三次判断的下一条指令下断点,我们直接进入到ReadProcessMemory这一步,通过ReadProcessMemory读取加密的代码到一个Buffer中,然后进行异或解密
解密后的代码(大家仔细看解密后的代码)
然后调用WriteProcessMemory将解密后的代码写入到子进程
再调用GetThreadContext获取子进程的线程环境块
修改子进程的EIP后,调用SetContextThread将修改后的值设置到子进程
执行ContinueDebugEvent使子进程继续运行起来
子程序继续运行后还会触发第二个异常
第二次触发异常时,子进程仍然会把控制权交给父进程,通过一系列的判断之后,确定触发异常的地址是我们预设好的地址401048
然后将触发异常的地址处的内容(原来是8DC0,也就是LEA EAX,EAX)修改两个字节,修改为681C
接着调用ContinueDebugEvent使子进程继续执行
可以看到子进程顺利执行
如果我们要对一个使用了Debug Blocker技术的程序逆行分析,可以使用OD的编辑功能(Ctrl+E),直接修改401041-101054处的代码即可
上面这种情况适合要修改的代码量和代码逻辑简单的情况下使用,一旦要修改的代码比较多,情况比较复杂,而且分布在程序各个地方,就不方便了。
接下来就介绍一种适合复杂情况下使用的调试方法
记住 hProcess ThreadId ProcessId 这三个值
使用OD的编辑功能(Ctrl+E)在407EDF处写入2个字节的无限循环代码(EBFE),然后在4012D4-4012E4地址处写入汇编代码,实际上就是调用WriteProcessMemory给子进程写一个无限循环,如下图
接着手动调用(将之前记下来的进程ID 和线程ID当作参数传进去)ContinueDebugEvent使子进程继续运行(这里注意要使用寄存器传参,否则可能会出错)
调用DebugActiveProcessStop()函数将被调试进程从调试器中分离出来
直接F9运行
然后F12暂停
将401041处的无限循环代码还原为6A00
将401048处的代码修改为681C
Debug Blocker 技术调试起来非常繁琐,即使是简单的示例程序应用了此技术后调试起来也非常麻烦,其实原理并不复杂,主要是理清楚它们之间的关系,一般都是子进程故意触发异常,然后由父进程处理该异常,在日常分析中许多病毒木马会将Debug Blocker层层嵌套,并结合多种反调试技术,给逆向分析人员带来极大的挑战。最后,革命尚未成功,同志仍需努力!!
看雪2022 KCTF 秋季赛 防守篇规则,征题截止日期11月12日!(iPhone 14等你拿!)
最后于 2022-6-14 22:52 被寒江独钓_编辑 ,原因: