(转).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/
(转).NET代码混淆实践的更多相关文章
- iOS安全攻防之代码混淆
iOS 代码安全之代码混淆实践: 前言: 在8月份的时候写了个关于 class-dump 反编译的文章(使用 Class-dump 反编译),利用 class-dump 工具可以反编译出工程的头文件, ...
- Android 代码混淆 混淆方案
本篇文章:自己在混淆的时候整理出比较全面的混淆方法,比较实用,自己走过的坑,淌出来的路.请大家不要再走回头路,可能只要我们代码加混淆,一点不对就会导致项目运行崩溃等后果,有许多人发现没有打包运行好好地 ...
- Eclipse与Android源码中ProGuard工具的使用(代码混淆)
由于工作需要,这两天和同事在研究android下面的ProGuard工具的使用,通过查看android官网对该工具的介绍以及网络上其它相关资料,再加上自己的亲手实践,算是有了一个基本了解.下面将自己的 ...
- HTML 代码复用实践 (静态页面公共部分提取复用)
原文:HTML 代码复用实践 上面的链接里面安装配置步骤已经非常详细,这里主要记录我操作过程中遇到的几个问题 gulp-file-include 的使用 按上面的步骤安装之后,node_mod ...
- 【Android】Android Studio 进行代码混淆,打包release APK
整了一天,感觉坑挺多. 1. 选择如图中的选项Android Studio进行签名打包: 2. 填写APP对应的信息:(最好用个文本记下来放在项目中同步给Team) - Key store path: ...
- ProGuard代码混淆技术详解
前言 受<APP研发录>启发,里面讲到一名Android程序员,在工作一段时间后,会感觉到迷茫,想进阶的话接下去是看Android系统源码呢,还是每天继续做应用,毕竟每天都是画UI ...
- Android Studio代码混淆插件
之前给公司的App添加代码混淆,在代码的混淆过程也遇到了不少的问题,再加上最近学习了一下Android Studio插件的开发,所以就开发一个代码混淆插件方便项目的代码混淆. 截图 第三方库列表清单 ...
- 77.Android之代码混淆
转载:http://www.jianshu.com/p/7436a1a32891 简介 作为Android开发者,如果你不想开源你的应用,那么在应用发布前,就需要对代码进行混淆处理,从而让我们代码即使 ...
- Android 代码混淆、第三方平台加固加密、渠道分发 完整教程(图文)
第一步:代码混淆(注意引入的第三方jar) 在新版本的ADT创建项目时,混码的文件不再是proguard.cfg,而是project.properties和proguard-project.txt. ...
随机推荐
- Python基础教程【读书笔记】 - 2016/7/7
希望通过博客园持续的更新,分享和记录Python基础知识到高级应用的点点滴滴! 第四波:第5章 条件.循环和其他语句 [总览] 深入介绍条件语句和循环语句,随后会看到列表推导式如何扮演循环和条件语 ...
- (转)textarea去掉右侧滚动条,去掉右下角拖拽
本文转载自:http://blog.csdn.net/cctv_end/article/details/7946188 代码: <TEXTAREA style= "overflo ...
- js获取网页的各种高度和宽度
document.body.clientWidth ==> BODY对象宽度document.body.clientHeight ==> BODY对象高度document.document ...
- php计算字符串长度:utf8编码,包含中文
php计算字符串长度:utf8编码 中文当作1个字符处理(strlen默认当作两个字符) 上函数: /** * 计算 UTF-8 字符串长度 * * @param string $str * @ret ...
- VB TreeView控件使用详解
来源:http://www.newxing.com/Tech/Program/VisualBasic/TreeView_587.html 三小时快速掌握TreeView树状控件的使用.能不能掌握控件的 ...
- Eclipse无法进入Debug模式
转载自:http://blog.sina.com.cn/s/blog_4b3191950100v8h5.html 原因:多半是因为程序根本就没运行到断点处,所以重新检查自己设置的断点.
- 重新定位svn地址的方法(windows和linux),svn switch(sw)的帮助信息
今天公司的路由器出现问题,服务器的IP地址也跟着变了,但是原来的svn已经设置好了,现在需要更换地址 查询原地址的方法: root@jack-desktop:codes# svn info 路径: . ...
- 浅谈Redis数据库的键值设计(转)
丰富的数据结构使得redis的设计非常的有趣.不像关系型数据库那样,DEV和DBA需要深度沟通,review每行sql语句,也不像memcached那样,不需要DBA的参与.redis的DBA需要熟悉 ...
- Callable和Future
在并发编程时,一般使用runnable,然后扔给线程池完事,这种情况下不需要线程的结果. 所以run的返回值是void类型. 如果是一个多线程协作程序,比如菲波拉切数列,1,1,2,3,5,8...使 ...
- c++101rule
组织策略0,不拘于小结缩进, 行的长度,命名,注释,空格,制表,1-4,高警告级别干净利落地进行编译,使用构建系统,使用版本控制,代码审查风格5,一个实体应该只有一个紧凑的职责. (依赖性管理,继承, ...