当前位置:首页 >焦点 >.Net JIT的骚操作DNGuard HVM原理简析 就算知道了它的操作原理

.Net JIT的骚操作DNGuard HVM原理简析 就算知道了它的操作原理

2024-06-30 16:58:27 [百科] 来源:避面尹邢网

.Net JIT的操作骚操作DNGuard HVM原理简析

作者:江湖 开发 前端 r11里面就是存储是HVMRun64.dll里面的MSIL二进制代码,rdi寄存器是原理JIT函数invokeCompileMethod参数methodInfo的地址,加上0x10为methodInfo.ILCode也即是简析混淆的MSIL二进制代码的起始地址,把从HVMRun64.dll里面取出来的操作MSIL二进制代码起始地址替换掉这个被混淆的MSIL二进制代码的地址,然后运行完整流程。原理

前言

DNGuard HVM的简析牛掰之处在于,就算知道了它的操作原理,但是原理你依然无法很优雅的去破解它。本篇来看下。简析友情提示,操作看本篇前,原理可先预热下前一篇:DNGuard HVM是简析如何加密.Net的

概括

1.MSIL保存

DNGuard HVM类库模式编译第一步就是把MSIL的原字节码(你需要加密的托管DLL)二进制代码保存在HVMRun64.dll里面以汇编的形式呈现。注意这里保存的操作是原字节码二进制代码,而不是原理保存字节码编译之后的汇编代码。如果是简析保存的MSIL编译后的汇编代码,可以通过逆向汇编。但保存的字节码的二进制代码还需要通过CLR+JIT编译成机器码之后进行即时运行,RunHVM函数随时可以Hook JIT或者CLR的某个地方或者几个地方,对它进行篡改。逆向的难度呈几何指数的增加。这里的MSIL的二进制代码可以参考文章:罕见的技术:MSIL的机器码简析举个例子以下C#代码:

.Net JIT的骚操作DNGuard HVM原理简析 就算知道了它的操作原理

static void Main(string[] args){   Console.ReadLine();  Console.WriteLine("Call Main");}

它的MSIL二进制代码是:

.Net JIT的骚操作DNGuard HVM原理简析 就算知道了它的操作原理

00 28 0e 00 00 0a 26 72 01 00 00 70 28 0f 00 00 0a 00 2a 00 af 93 65 6c 00 13 00 80 00 00 00 00 00 00 00 00  //后面省略

这种二进制代码,DNGuard会把它保存在HVMRun64.dll里面。

.Net JIT的骚操作DNGuard HVM原理简析 就算知道了它的操作原理

2.MSIL加密

当它保存好了原MSIL之后,就着手加密托管的DLL里面的MSIL,把它变成了

Dnspy/ILSpy/Dotpeek这种工具无法修改的MSIL。比如以上C#代码被加密成了如下:

[NullableContext(1)][MethodImpl(MethodImplOptions.NoInlining)]private static void Main(string[] args){     throw new Exception("Error, DNGuard Runtime library not loaded!");}

实际上就算是没有加密MSIL,因为它是hook jit,也无法通过修改MSIL来变更源码。

3.hook JIT

当运行被DNGuard修改的托管DLL的时候,RunHVM函数就会调用HVMRun64.dll里面的代码hook即时.Net编译器JIT里面的invokeCompileMethod函数,因为JIT会按照预定的顺序编译MSIL,而原有的MSIL被保存到了HVMRun64.dll里面去。被JIT执行的托管DLL里的MSIL则是被DNGuard加密过的是个错误的数据或者一堆乱数据。它hook这个函数的目的就是修改这个函数的参数里面保存的被DNGuard修改过了的MSIL二进制代码的起始地址,它把这个起始地址Hook成上面第一步存放的MSIL二进制代码的起始地址。此后JIT就会用Hook出来的地址,逐个编译里面的MSIL二进制代码,把它编译成机器码,然后运行。这样就完成了完整的整个执行。以下是HVMRun64.dll对托管主函数Main进行Hook调用地址的顺序,按照堆栈顺序从下往调用。

0000000180497AB8 4C 89 5F 10          mov         qword ptr [rdi+10h],r1100000001804839DF FF 1A                call        fword ptr [rdx]0000000180015759 E8 A2 61 FF FF       call        000000018000B9000000000180040918 E8 43 4B FD FF       call        0000000180015460

这四个地址里面的地址:00000001804839DF进行了四字节位移运行。这种加固静态逆向的难度。

4.难点

这个过程的难点在于,DNGuard是以何种方式把原MSIL保存到HVMRun64.dll,然后又通过何种方式取出来。MSIL的二进制代码存放在HVMRun64.dll的哪个地方?只要找到了存储的位置,即可轻松破解DNGuard的加密。难点一:逆向调试DNGuard的时候,发现HVMRun64.dll里面的数据无法调试,只要下了断点或者调试器进去就会导致数据更改,更改后的数据要么是空的那么就全是0xCCCC这种东西,然后报异常。下断点导致数据更改,这跟.Net7里面的内存映射有点相似,这点还待研究。难点二:DNGuard在Hook JIT的时候,它会进行字节位移。比如本来的地址如下:

00000001804839DB: E8 1C F9 DD FF   call   00000001802632FC00000001804839E0: 1A E8            sbb    ch,al

00000001804839DB这个地址看着没问题,但是实际上它是个伪装地址,在运行的过程中,会把这个地址加上4字节,也就是到了地址00000001804839DF这个地址,然后再运行。这样导致了机器码也跟着改变,静态逆向完全无法展开,而动态逆向则坑爹的一笔,最新的DNGuard近5M大小,里面的汇编代码高达153万多行。基本上属于无法逆向的存在。

5.破除按照以上原理认知,以下是个人认为 可行的理论上的破解之法

如下代码:

0000000180497AB8 4C 89 5F 10   mov   qword ptr [rdi+10h],r11

r11里面就是存储是HVMRun64.dll里面的MSIL二进制代码,rdi寄存器是JIT函数invokeCompileMethod参数methodInfo的地址,加上0x10为methodInfo.ILCode也即是混淆的MSIL二进制代码的起始地址,把从HVMRun64.dll里面取出来的MSIL二进制代码起始地址替换掉这个被混淆的MSIL二进制代码的地址,然后运行完整流程。

破一:根据RunHVM函数的原理,它hook JIT的函数,这里就hook这个JIT函数后面的一个函数,照样获取ILCode,修改后返回运行。这里是照猫画虎,RunHVM怎么玩JIT,这里就怎么玩RunHVM。

破二:这里还有个破绽,因为0000000180497AB8这个地址则写死在了HVMRun64.dll里面,再者[rdi+10h]这个地址是否能够替换成别的地址,获取MSIL的二进制起始地址,进行修改然后,也是可以从这里入手的。

可以看到即使知道了RunHVM原理,要想破除它依然有一定的难度。真正的实现可能比较不优雅的方式。

注:以上用于学习用途,其它用途均跟本人完全无关。

责任编辑:武晓燕 来源: 江湖评谈 ILCode二进制代码

(责任编辑:焦点)

    推荐文章
    热点阅读