2009年1月12日星期一

.NET程序加解密技术基础

一、什么是.NET?
微软官方给出的定义是:.NET框架是支持生成和运行下一代运行程序内部Windows的组件。同时,.NET还有很多别的平台上的实现,只要符合计算机制造商协会规定的公共语言基础框架CLI的平台,我们都可以实现.NET的平台。比如说MONO,还有SSCLI。
在本文中,我们主要的议题还是围绕在第一点,Windows平台的应用。
首先,.NET是一个面向对象的框架。
其次,它是个将软件部署和版本控制冲突最小化的代码执行环境。我讲这些涉及到以后加解密中间需要的一些特点。
第三,它是一个可提高代码执行安全性的代码执行环境,这点后面也会提到。.NET生成中间代码以后,通过即时编译,会比JAVA运行速度快一些。
最后一点,确保基于.NET的框架代码可以和任何代码集成,这是最大的特色,就是编程语言的统一。讲了这么多,还是我们来谈一谈从加解密的角度看,.NET是什么?
一句话,就是构建于操作系统之上的虚拟机。我们今天谈的.NET包含两个部分,一个是通用语言运行时,也就是CLR。
图1是一个Windows平台上的.NET框架基本结构示意图,大家可以看到有很多各种不同的编程语言,经过中间整合为元数据,在底层Windows与应用程序中间最大的框就是.NET,两个部分,左边这块是CLR,是它的运行环境,右边是.NET的类库,结构就是这样。
图1 Windows系统上的.NET框架结构
二、加解密所关注的.NET带来的改变
1、PE文件结构的改变
.NET作为新的平台,肯定引进一些新的技术。我们讨论的是加解密,因此我们有关注的特殊点,第一是在PE结构上带来的一些改变,大家很清楚PE结构是Windows平台上可执行程序的结构,普通的PE文件保存的是机器指令的编码,图2是典型的普通的PE文件示意图。
图2 普通的PE文件
但是.NET下的PE文件,在.NET下叫程序集,其保存的是另外两种数据,叫元数据和MSIL,当然同时还保存其他的数据,但主要是元数据和MSIL,中间语言。
在.NET下,PE结构中改变最大的属于.text节,它包含.NET的基本结构和数据的比如说CLR头,一些输入表,元数据等,这是.NET对PE文件的最大的改变。
讲到这里引出.NET中一个非常重要的概念,元数据。什么是元数据?字典上解释为描述数据的数据,用在.NET上也是非常合适的。首先它本身就是一个数据,0101,第二它描述了.NET程序的全部信息,一个.NET程序想正常运行,必须是一个合法的.NET程序,必须包含合法完整的元数据。因此我们说.NET程序是自描述的,运行的时候,程序集本身包含了所有信息,不需要寻找别的依赖性的信息(比如注册表中)。
但是程序集的这种自描述的性质,使得反编译时的信息更加丰富,结合MSIL的特征,使得反编译的结果近似源代码。如图3所示,左边这张图是一个反编译的图,再看看点击的方法,所有的名称都被显示了出来。所以说元数据还是挺可怕的。
图3 程序集的这种自描述性质使得反编译时信息更加丰富,结合MSIL的特性,使得反编译结果近似原代码
下面简单介绍一下元数据的基本结构,元数据是以数据流的形式保存在文件中,各种有不同的形式,一个是堆,一个是表,流是统称,而堆和表是存在形式。
图4表示了.NET下的六个元数据堆,第二个流是Blob,所有二进制数据都保存在Blob中,第三个是GUID,每个程序集唯,这个比较简单。第四个流,US代表用户字符串,红色标示的是比较常见的,和加解密关系比较重要的,因为用户字符串中保存了比较关键的信息,。下一个#-,最重要的流,所有的堆存在于这个流中。
图4 .NET下的6个元数据堆
图5的倒数第二行介绍了所有的元数据堆,也就是.NET的元数据表,从00到44,大家看看这些名称基本上反映了.NET面向框架结构,基本上结构从名称中就可以看出来了。
图5 .NET的元数据表
2、统一了编程语言
第二个主要的变化是统一了编程语言。像我们编程做应用开发的时候,可以使用C++,C#或者其他语言,但是这些语言经过静态编译以后,统一到MSIL。
MSIL的特点,第一它是高级语言,面向对象的,比汇编高级很多。第二它是是基于堆栈的运行机制。来看一段示例代码(图6)。
图6 代码
第一行定义局部变量,名称为VAL,下面为MSIL指令。我们一句一句的看。首先初始堆栈为空,然后第三句,出栈,又入栈,然后10和1两个参数入栈,最后运算,结果入栈。这是MSIL最显著的特点,但是也给.NET加解密带来一个非常常用的操作,叫往复操作(round-tripping),中文我不太清楚,就是拿到一个.NET的PE文件,我们反编译成源文件,在源码上进行操作以后,通过框架提供的ilasm,再编译成可执行文件,只有通过元数据和MSIL可以实现这种操作。
3、程序运行方式的改变
加解密所关注.NET的变化第三点我觉得是程序运行方式的改变。Windows不再直接负责程序的运行,而是由.NET框架进行管理。框架中JIT引擎负责在运行时将IL代码即时编译为本地汇编代码执行。.NET程序被称为托管程序,相反地,我们说的传统的Windows程序,就是非托管程序(或称为本地程序)。
下面简单介绍.NET的基本概念。
首先是我们常听到的程序集。程序集是.NET可执行程序的基本单元,程序集中间包含一个或多个模块,模块和程序集的区别是模块不含清单信息,所以模块是不能直接运行的,而程序集是可以的。在MSIL中编程时,程序集没有扩展名。
第二个是类型。面向对象大家非常熟悉了,差别不是很大,方法也是一样。类型有很多成员,其中最重要的是方法,因为加解密关注方法中的关键代码,所以方法是非常重要的成员。
然后是标识,就是怎么定位.NET中每一个元数据,在MSIL是一个32位的值,类似AABBBBBB的形式。比如方法元数据在元数据表中排第七个,从零开始计数的话,就是06,Main方法为第一个方法,排第一,因此Main方法的元数据标识就0x06000001。再比如某字符串为0X70000001,就是这样一个简单的标识。
最后,有一个概念叫签名,签名就是存储在Blob中的一段二进制数据,它的作用是描述特定元数据的性质。.NET下共有六个表包含签名项。
程序集的运行方式,即时编译,JIT,这是.NET运行可执行程序的基本方式,也就是在需要运行的时候,将对应的IL代码编译为本机指令,传入JIT中的是IL代码,导出的是本机代码。所以,部分加密软件通过挂钩JIT进行加解密操作。
以上讲解的是.NET程序加解密技术的一些基础知识。.NET加解密是一个很有意思的方向,.NET加解密技术的发展使得保护方式多样化,强度远大于诞生初期,已经不能单纯地从托管的层次解决了。

本文主要内容整理自软件安全专家单海波先生在2008中国软件安全峰会(www.sinoit.org.cn)上的演讲。

没有评论:

发表评论