(转载).NET代码混淆实践

今天突然对反编译、混淆感兴趣,在网上查了一些资料,文章大部分内容来源http://www.cnblogs.com/hsapphire/archive/2010/11/21/1883514.html。

1,名称混淆

1.1控制台程序

class Program
    {
        static void Main(string[] args)
        {
            string userID="asldjf3333djf";
            string pwd = GetPassword(userID);
            ChangeInfo(userID, pwd);
        }
          public static string GetPassword(string userID)
        {
            return "123456";
        }
       public static void ChangeInfo(string userID,string pwd)
        {
        }
    }

  使用ILSPY进行反编译,界面如下:

1.2 使用PE文件查看工具CFF载入可执行文件。定位到#Strings流(程序集的类型、引用类型、成员等的定义都在该字符串中),使用CFF来修改#Strings流的内容。查找到字符串“ChangeInfo”和字符串“GetPassword”,随意替换,保存后双击程序,运行验证是否有问题。

结果显示能够正常运行。然后用ILSPY打开后,显示原先的“ChangeInfo”和“GetPassword”为乱码(图略,使用ILSPY时需要重新打开exe文件)

1.3作者的总结:

  •   第一种替换方法为无意义替换。我们知道在实际的开发过程中,我们都必须遵守一定的命名规范来为类型、属性、字段命名。但是当我们对编译成功的代码进行名称混淆的时候就是要将这些规范的、规律的名称变得毫无意义,毫无规律可循。
  •   第二种替换方法称为不可打印字符替换。在UNICODE字符集中,一些特殊字符,目前无法得到正确的显示,比如从0x01到0x20之间的字符。如果把名称替换成这些字符,显示出来的就是奇怪的乱码。
  •   第三种替换方法为空字符替换。空字符替换就是把名称替换为空串。但是这种方法并不适合实际的应用,因为如果名称都为空,那么势必要产生二义性。

2 流程混淆是指打乱方法的流程,让反编译软件无法将代码正确的反编译为高级语言。流程混淆即可保护代码又可增加破解者分析代码的难度。目前流程混淆基本上都是基于跳转实现的,我们在程序中使用的跳转有如下三种方式:

  • 1) goto跳转;
  • 2) if-else跳转
  • 3) switch跳转。

.NET的流程混淆和传统windows应用程序的流程混淆本质上是有区别的。传统的流程混淆的目的是防止反汇编,而.NET流程混淆的目的是防止反编译。传统的流程混淆是基于汇编指令进行的,操作层次较低,可更改堆栈调用,可操作方式较多;.NET流程混淆是基于IL指令进行的,操作层次较高,不可触及堆栈调用,可操作方式较少。

2.1 测试流程混淆代码

class Program 
    { 
        static void Main(string[] args) 
        { 
            string h = "hello"; 
            if (h == "hell0") 
            { 
                OutString(); 
            } 
             else 
            { 
                Console.WriteLine("There is no hello!"); 
            } 
        } 
         public static void OutString() 
        { 
             Console.WriteLine("hello"); 
        } 
    }

核心IL代码

  .class private auto ansi beforefieldinit FlowObufscation.Program

  extends [mscorlib]System.Object

  {

    .method private hidebysig static void Main(string[] args) cil managed

    {

      .entrypoint

    .maxstack 2

    .locals init ([0] string h,

    [1] bool CS$4$0000)

    IL_0000: nop

    IL_0001: ldstr "hello"

    IL_0006: stloc.0

    IL_0007: ldloc.0

    IL_0008: ldstr "hell0"

    IL_000d: call bool [mscorlib]System.String::op_Equality(string, string)

    IL_0012: ldc.i4.0

    IL_0013: ceq

    IL_0015: stloc.1

    IL_0016: ldloc.1

    IL_0017: brtrue.s IL_0023

    IL_0019: nop

    IL_001a: call void FlowObufscation.Program::OutString()

    IL_001f: nop

    IL_0020: nop

    IL_0021: br.s IL_0030

    IL_0023: nop

    IL_0024: ldstr "There is no hello!"

    IL_0029: call void [mscorlib]System.Console::WriteLine(string)

    IL_002e: nop

    IL_002f: nop

    IL_0030: ret

  } // end of method Program::Main

  .method public hidebysig static void OutString() cil managed

  {

  // 代码大小 13 (0xd)

  .maxstack 8

  IL_0000: nop

  IL_0001: ldstr "hello"

  IL_0006: call void [mscorlib]System.Console::WriteLine(string)

  IL_000b: nop

  IL_000c: ret

  } // end of method Program::OutString

  .method public hidebysig specialname rtspecialname

  instance void .ctor() cil managed

  {

  // 代码大小 7 (0x7)

  .maxstack 8

  IL_0000: ldarg.0

  IL_0001: call instance void [mscorlib]System.Object::.ctor()

  IL_0006: ret

  } // end of method Program::.ctor

  } // end of class FlowObufscation.Program

作者将Main方法的IL代码分成了三段,分段并没有什么依据,完全是随意的。下面将这三段代码重新组合,按照第一段、第三段、第二段的顺序排列,然后在第一段的结尾加上跳转语句“br IL_0012”,在第二段代码的后面加上跳转语句“br IL_0029”。修改之后的代码如下

  .method private hidebysig static void Main(string[] args) cil managed

  {

  //第一段开始

  .entrypoint

  // 代码大小 49 (0x31)

  .maxstack 2

  .locals init ([0] string h,

  [1] bool CS$4$0000)

  IL_0000: nop

  IL_0001: ldstr "hello"

  IL_0006: stloc.0

  IL_0007: ldloc.0

  IL_0008: ldstr "hell0"

  IL_000d: call bool [mscorlib]System.String::op_Equality(string, string)  

  br IL_0012

  //第一段结束

  //第三段开始

  IL_0029: call void [mscorlib]System.Console::WriteLine(string)

  IL_002e: nop

  IL_002f: nop

  IL_0030: ret

  //第三段结束

  //第二段开始

  IL_0012: ldc.i4.0

  IL_0013: ceq

  IL_0015: stloc.1

  IL_0016: ldloc.1

  IL_0017: brtrue.s IL_0023

  IL_0019: nop

  IL_001a: call void FlowObufscation.Program::OutString()

  IL_001f: nop

  IL_0020: nop

  IL_0021: br.s IL_0030

  IL_0023: nop

  IL_0024: ldstr "There is no hello!"

  br IL_0029

  //第二段结束

  } // end of method Program::Main

用ILASM重新编译修改后的IL代码(注意是ILASM,不是ILDASM)

这样就可以尝试反编译了。。原作者用Reflector测试抛出异常(可能因为版本等原因),我用的ILSPY竟然能够成功,没有异常。

原文中还有一个逻辑跳转的例子,但是测试没有成功,如果想尝试请参考文章开头的链接。

作者还有两篇文章,关于混淆和保护的一些观点,参考文章如下:

http://www.cnblogs.com/hsapphire/archive/2009/12/28/1634159.html

http://www.cnblogs.com/hsapphire/archive/2010/07/30/1788355.html

感谢原作者的分享。

附:

PE修改工具:

    CFF,http://www.ntcore.com/exsuite.php

.NET 反编译:

    ILSPY,http://ilspy.net/

    dotPeek,http://www.jetbrains.com/decompiler/

JAVA反编译:

    jd-gui,http://jd.benow.ca/

时间: 2024-07-31 13:47:18

(转载).NET代码混淆实践的相关文章

Android开发实践:利用ProGuard进行代码混淆

由于Android的代码大都是Java代码,所以挺容易被反编译的,好在Android ADT为我们集成了混淆代码的工具,一来可以混淆我们的代码,让程序被反编译后基本看不懂,另外还能起到代码优化的作用.发布项目前,建议打开Android的代码混淆功能. Android ADT主要通过ProGuard工具来提供代码混淆,网上也有挺多博客文章讲这个的,但感觉很多都介绍得太过于复杂,这里我就以问答的方式来更加简洁地介绍下ProGuard吧. 1. ProGuard是什么 ProGuard是一个工具,用来

Android代码混淆(新版本ADT)

说明: 看网上说的很多Android代码混淆的方法都是旧版本ADT的方法,今天用到代码混淆,记下来分享一下,有需要的可以看看. 实践: 我就不写东西了,还是直接给大家看我项目中用到的文件就好了.注释我写在图片上了. 第二个文件的代码我还是写出来吧,可能有朋友用的和我的差不多.(注意不要把注释写进去) 这两个文件都是项目生成的时候由ADT自动生成的,不用自己加. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 -keep public class * extend

【转】Android代码混淆指南

请尊重他人的劳动成果,转载请注明出处:<Android代码混淆指南> 本文将和大家分享使用Eclipse+ADT与使用Android Studio进行代码混淆打包的方法. 使用Eclipse+ADT进行代码混淆打包的方法 新版本SDK下进行打包混淆 如果使用新版本的SDK创建的Android项目会在项目的根目录下生成一个proguard-project.txt文件,该文件是默认的混淆规则文件. 混淆打包的步骤: 1.在proguard-project.txt文件中添加混淆的项目规则,大家可参考

Android代码混淆之混淆规则

请尊重他人的劳动成果,转载请注明出处:<Android代码混淆技巧> 因为Android是使用Java开发的,所以开发者可以使用ProGuard对代码进行混淆.SDK已经集成了ProGuard工具,开发者可以从SDK目录下的\tools\proguard目录中进行查看. ProGuard是一个免费的Java类文件收缩,优化,混淆和预校验器.它可以检测并删除未使用的类,字段,方法和属性.它可以优化字节码,并删除未使用的指令.它可以将类.字段和方法使用短无意义的名称进行重命名.最后,预校验的Jav

Android代码混淆指南

请尊重他人的劳动成果,转载请注明出处:<Android代码混淆指南> 本文将和大家分享使用Eclipse+ADT与使用Android Studio进行代码混淆打包的方法. 使用Eclipse+ADT进行代码混淆打包的方法 新版本SDK下进行打包混淆 如果使用新版本的SDK创建的Android项目会在项目的根目录下生成一个proguard-project.txt文件,该文件是默认的混淆规则文件. 混淆打包的步骤: 1.在proguard-project.txt文件中添加混淆的项目规则,大家可参考

【转】 Android代码混淆之混淆规则

请尊重他人的劳动成果,转载请注明出处:<Android代码混淆技巧> 因为Android是使用Java开发的,所以开发者可以使用ProGuard对代码进行混淆.SDK已经集成了ProGuard工具,开发者可以从SDK目录下的\tools\proguard目录中进行查看. ProGuard是一个免费的Java类文件收缩,优化,混淆和预校验器.它可以检测并删除未使用的类,字段,方法和属性.它可以优化字节码,并删除未使用的指令.它可以将类.字段和方法使用短无意义的名称进行重命名.最后,预校验的Jav

77.Android之代码混淆

转载:http://www.jianshu.com/p/7436a1a32891 简介 作为Android开发者,如果你不想开源你的应用,那么在应用发布前,就需要对代码进行混淆处理,从而让我们代码即使被反编译,也难以阅读.混淆概念虽然容易,但很多初学者也只是网上搜一些成型的混淆规则粘贴进自己项目,并没有对混淆有个深入的理解.本篇文章的目的就是让一个初学者在看完后,能在不进行任何帮助的情况下,独立写出适合自己代码的混淆规则. 说在前面 这里我们直接用Android Studio来说明如何进行混淆,

Android项目外接高德地图代码混淆注意事项

如今好多项目中都加入了第三方jar包,可是最大的问题就是打包的时候代码混淆报错,下面是高德地图混淆报错解决方式: 在proguard-project.txt中加入例如以下代码: -libraryjars   libs/android-support-v4.jar -dontwarn android.support.v4.** -keep class android.support.v4.** { *; } -keep interface android.support.v4.app.** { *

Android学习心得(14) --- Android代码混淆(2)

我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下Android混淆如何自定义proguard参数来实现 环境配置参考 Android学习心得(1) --- MAC下Android环境的搭建 签名参考Android学习心得(12) --- APK签名keystore 代码混淆Android学习心得(13) --- Android代码混淆(1) dex2jar&jd-guiAndroid学习心得(8) --- MAC下dex2jar和jd-gui使用 1.介绍p