.NET托管非托管区别和引申

点击上方蓝字 江湖评谈设为关注

5d7591eec1a0f202846e36fbfde36497.png

区别

一般的简单点来说,托管就是C#语言写的代码,非托管就是C++语言写的代码。离了C++,C#完全无法运行。本质上来说,C#和C++是不分家的。那么更确切一点以最常用的win平台为例,C#语言生成的PE文件托管DLL和C++非托管DLL,区别在于有无.NET头结构体IMAGE_COR20_HEADER。

托管加固

一些加密软件,为了对托管DLL的逆向难度进行增强。来回的在托管和非托管中切换,理论上来说托管的函数都遵循CLR/JIT的规则。一个托管函数的编译过程本身就是极其复杂的工程,即使是一个最简单的托管函数,比如以下简单的C#代码:

static void ABC()
{
    Console.WriteLine("Call ABC");
}

ABC函数需要经过Roslyn构建MSIL,经过CLR加载,经过IR变形,经过JIT优化以及生成机器码。这个中间的过程,辗转了整个runtime的几百万行代码的亲密接触,经历了二进制,汇编,C/C++,以及最上层的C#的各种骚操,形成了最后的那简单的几行汇编代码,存放在内存里,用即时编译器运行出来。

中间的过程是难点,最后的结果更是难点。即时编译的结果是存放在内存里的,内存里的东西运行的时候可以更改,但是程序结束了,它又恢复了原样,且地址亦不固定。所以这里需要从非托管作为切入口对它进行hook以及一些其它工作。

以上面的ABC函数为例,它里面只是调用了System.Console.dll库里面的函数Console.WriteLine打印出字符串Call ABC。这个看似简单的过程,可以通过加密软件把这个ABC函数的MSIL进行重新构建。它原有的MSIL如下:

IL_0000  00                nop
IL_0001  28 06 00 00 06    call         0x6000006
IL_0006  00                nop
IL_0007  28 07 00 00 06    call         0x6000007
IL_000c  00                nop
IL_000d  72 25 00 00 70    ldstr        0x70000025
IL_0012  28 10 00 00 0a    call         0xA000010
IL_0017  00                nop
IL_0018  28 11 00 00 0a    call         0xA000011
IL_001d  26                pop
IL_001e  2a                ret

被加密之后的MSIL如下:

IL to import:
IL_0000  28 c7 00 00 0a    call         0xA0000C7
IL_0005  6f c8 00 00 0a    callvirt     0xA0000C8
IL_000a  2b 18             br.s         24 (IL_0024)
IL_000c  06                ldloc.0
IL_000d  04                ldarg.2
IL_000e  16                ldc.i4.0
IL_000f  0b                stloc.1
IL_0010  38 5e 00 00 00    br           94 (IL_0073)
IL_0015  06                ldloc.0
IL_0016  07                ldloc.1
IL_0017  9a                ldelem.ref
IL_0018  2b 13             br.s         19 (IL_002d)
IL_001a  17                ldc.i4.1
IL_001b  2d 02             brtrue.s     2 (IL_001f)
IL_001d  18                ldc.i4.2
IL_001e  26                pop
IL_001f  0a                stloc.0
IL_0020  2b ec             br.s         -20 (IL_000e)
IL_0022  2b 09             br.s         9 (IL_002d)
IL_0024  17                ldc.i4.1
IL_0025  2d 02             brtrue.s     2 (IL_0029)
IL_0027  1b                ldc.i4.5
IL_0028  26                pop
IL_0029  2b ef             br.s         -17 (IL_001a)
IL_002b  2b df             br.s         -33 (IL_000c)
IL_002d  0c                stloc.2
IL_002e  08                ldloc.2
IL_002f  2b 08             br.s         8 (IL_0039)
IL_0031  6f 5e 00 00 0a    callvirt     0xA00005E
IL_0036  0d                stloc.3
IL_0037  de 44             leave.s      68 (IL_007d)
IL_0039  16                ldc.i4.0
IL_003a  2c 02             brfalse.s    2 (IL_003e)
IL_003c  1e                ldc.i4.8
IL_003d  26                pop
IL_003e  2b 05             br.s         5 (IL_0045)
IL_0040  18                ldc.i4.2
IL_0041  2b 0b             br.s         11 (IL_004e)
IL_0043  4e                ldind.r4
IL_0044  56                stind.r4
IL_0045  16                ldc.i4.0
IL_0046  2c 02             brfalse.s    2 (IL_004a)
IL_0048  1c                ldc.i4.6
IL_0049  26                pop
IL_004a  2b 0b             br.s         11 (IL_0057)
IL_004c  2b f3             br.s         -13 (IL_0041)
IL_004e  17                ldc.i4.1
IL_004f  2d 02             brtrue.s     2 (IL_0053)
IL_0051  1b                ldc.i4.5
IL_0052  26                pop
IL_0053  2b dc             br.s         -36 (IL_0031)
IL_0055  2b ee             br.s         -18 (IL_0045)
IL_0057  17                ldc.i4.1
IL_0058  2d 02             brtrue.s     2 (IL_005c)
IL_005a  1c                ldc.i4.6
IL_005b  26                pop
IL_005c  02                ldarg.0
IL_005d  2b ed             br.s         -19 (IL_004c)
IL_005f  2b 04             br.s         4 (IL_0065)
IL_0061  02                ldarg.0
IL_0062  03                ldarg.1
IL_0063  de 08             leave.s      8 (IL_006d)
IL_0065  17                ldc.i4.1
IL_0066  2d 02             brtrue.s     2 (IL_006a)
IL_0068  17                ldc.i4.1
IL_0069  26                pop
IL_006a  26                pop
IL_006b  2b f6             br.s         -10 (IL_0063)
IL_006d  07                ldloc.1
IL_006e  17                ldc.i4.1
IL_006f  58                add
IL_0070  2b 0d             br.s         13 (IL_007f)
IL_0072  0c                stloc.2
IL_0073  07                ldloc.1
IL_0074  06                ldloc.0
IL_0075  8e                ldlen
IL_0076  69                conv.i4
IL_0077  2b 0e             br.s         14 (IL_0087)
IL_0079  4e                ldind.r4
IL_007a  53                stind.i2
IL_007b  14                ldnull
IL_007c  2a                ret
IL_007d  09                ldloc.3
IL_007e  2a                ret
IL_007f  17                ldc.i4.1
IL_0080  2d 02             brtrue.s     2 (IL_0084)
IL_0082  18                ldc.i4.2
IL_0083  26                pop
IL_0084  0b                stloc.1
IL_0085  2b ec             br.s         -20 (IL_0073)
IL_0087  17                ldc.i4.1
IL_0088  2d 02             brtrue.s     2 (IL_008c)
IL_008a  15                ldc.i4.m1
IL_008b  26                pop
IL_008c  3f 84 ff ff ff    blt          -124 (IL_0015)
IL_0091  2b e8             br.s         -24 (IL_007b)

以上是一个逆向的实例,这里面包含了大量的跳转br.s,brtrue.s这种东西。不说它在JIT里面的IR变形和优化,也不说变成机器码之后的程序结果。只看当前就非常艰涩。它某些跳转里面包含了一些函数的调用,这些函数的调用里面又包含了十几个跳转。这些十几个跳转里面又包含了几个函数,每个函数里面再包函十几个跳转,这些跳转从托管到非托管,然后跳转回来,来来回回往复循环。耗尽耐心之后,防护加固就成功了。这是托管层面的,下面看下非托管层面的加固模式。为了解决这个困惑,可以在JIT的PreStubworker上入手,查看其汇编结果,逐步推导。

非托管加固

非托管里面的实质是,可以通过加密软件加密的托管代码,调用一些非托管库函数,然后运行这些非托管库函数,比如zlibc这种压缩库。一般的来说,在.NET里面压缩它是有intel特别定制的库文件,比如System.IO.Compression.Native.dll,它一共导出了如下函数

CompressionNative_Crc32
CompressionNative_Deflate
CompressionNative_DeflateEnd
CompressionNative_DeflateInit2_
CompressionNative_DeflateReset
CompressionNative_Inflate
CompressionNative_InflateEnd
CompressionNative_InflateInit2
CompressionNative_InflateReset
等函数,还有一些没写出来

它里面进行了代码的压缩以及解压,这个过程非常复杂。基本上在耗尽耐心。虽然它看似足够牛逼,为了解决这个非托管困惑。这里依然有足够宽松的切入点,那就上面所说的非托管DLL。

今天的分享大致就这么多。关于更多的知识点,可以加入知识星球,里面的硬核知识,骚操技术,你难以想象,持续性分享。

往期精彩回顾

.NET8 JIT核心:分层编译的原理

新版.Net性能有没有达到C++90%?

欢迎加入C#12.NET8技术交流群

面试官问.Net对象赋值为null,就会被GC回收吗?

79d63b37bd0300fbbfb0f02db376ec49.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值