提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
转帖|其它|编辑:郝浩|2010-11-29 16:33:20.000|阅读 1876 次
概述:传统的非托管程序,加壳的对象是汇编指令;对.NET程序的加壳对象则是元数据和IL代码。对.NET程序的加壳,在理论和方式上并没有什么创新,目前都是直接继承与Windows程序的加壳理论和方法。大部分.NET加壳工具也是传统的加壳工具在自身功能上提供了扩展。纯.NET实现的加壳工具还是很少。加壳的方式很多,我们这里以常见的托管压缩壳为例进行讲解。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
加壳是一种常用的保护应用程序的方式,确切的说是一种加密方式。取名为壳,意思是说这种对程序的保护方式就像植物种子的外壳,我们使用一段程序将我们的主程序包裹在其中,不能轻易被其他人看见。
被加壳的程序在运行时先要运行一段附加的指令,这段附加的指令完成相关操作后会启动主程序。
加壳的方法大致可分为压缩和加密。
传统的非托管程序,加壳的对象是汇编指令;对.NET程序的加壳对象则是元数据和IL代码。对.NET程序的加壳,在理论和方式上并没有什么创新,目前都是直接继承与Windows程序的加壳理论和方法。大部分.NET加壳工具也是传统的加壳工具在自身功能上提供了扩展。纯.NET实现的加壳工具还是很少。加壳的方式很多,我们这里以常见的托管压缩壳为例进行讲解。
本节就.NET程序加壳的基本原理和方式做实践性的分析。
为了探究其压缩原理,我们先创建一段代码用于实验,该段代码如代码清单9-14所示。
代码清单9-14 用于加壳的程序源码
class Program
{
static void Main(string[] args)
{
DoSth();
}
public static void DoSth()
{
}
}
代码清单9-14的代码最终生成ForCompress.exe文件。使用Reflector查看其IL代码,Main方法的如代码清单9-15所示。
代码清单9-15 Main方法的IL代码 .method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
L_0000: nop
L_0001: call void ForCompress.Program::DoSth()
L_0006: nop
L_0007: ret
}
此时,ForCompress.exe在Reflector中结果如图9-19所示。
图9-19 ForCompress.exe的结构
此时ForCompress.exe
下面我们启动一款.NET压缩工具,NETZ来对ForCompress.exe进行加壳。加壳之后,我们再次启动Reflector来查看加壳的文件。如图9-20所示。
图9-20 加壳之后的ForCompress.exe文件
对比图9-19和图9-20,我们发现名称空间ForCompress变成了netz,类Progress变成了NetzStartter。程序集多多了个资源文件app.resources。下面我们展开NetzStartter类,来查看其下的方法。如图9-21所示。
图9-21 NetzStartter类的方法
从图9-21中我们可以看出,NetzStartter类定义了一系列我们"不认识"的方法,但是却没有代码清单9-14中的DoSth方法。下面我们来分析一下加壳之后的exe文件的启动过程。
首先定位到Main方法,查看其源代码,如代码清单9-16所示。
代码清单9-16 NetzStartter类的Main方法
[STAThread]
public static int Main(string[] args)
{
try
{
InitXR();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(NetzStarter.NetzResolveEventHandler);
return StartApp(args);
}
catch (Exception exception)
{
string str = " .NET Runtime: ";
Log(string.Concat(new object[] { "#Error: ", exception.GetType().ToString(), Environment.NewLine, exception.Message, Environment.NewLine, exception.StackTrace, Environment.NewLine, exception.InnerException, Environment.NewLine, "Using", str, Environment.Version.ToString(), Environment.NewLine, "Created with", str, "2.0.50727.4927" }));
return -1;
}
}
代码清单9-16中的Main方法中,首先调用了InitXR方法,然后为AppDomain.CurrentDomain.AssemblyResolve事件添加处理方法,最后调用StartApp方法。我们首先看看InitXR方法做了些什么事情。InitXR方法源码如代码清单9-17所示。
代码清单9-17 InitXR方法源码
private static void InitXR()
{
try
{
string str = @"file:\";
string str2 = "-netz.resources";
string directoryName = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
if (directoryName.StartsWith(str))
{
directoryName = directoryName.Substring(str.Length, directoryName.Length - str.Length);
}
string[] files = Directory.GetFiles(directoryName, "*" + str2);
if ((files != null) && (files.Length > 0))
{
xrRm = new ArrayList();
for (int i = 0; i < files.Length; i++)
{
string fileName = Path.GetFileName(files[i]);
ResourceManager manager = ResourceManager.CreateFileBasedResourceManager(fileName.Substring(0, fileName.Length - str2.Length) + "-netz", directoryName, null);
if (manager != null)
{
xrRm.Add(manager);
}
}
}
}
catch
{
}
}
代码清单9-17的代码很明了,在特定的文件路径中搜索资源文件,然后添加到全局变量xrRm中。
Main方法中的AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(NetzStarter.NetzResolveEventHandler)一句代码也无需多言,只是指定程序集解析失败时的事件处理。
现在我们看StartApp方法的源码,如代码清单9-18所示。
代码清单9-18 StartApp方法源码
public static int StartApp(string[] args)
{
byte[] resource = GetResource("A6C24BF5-3690-4982-887E-11E1B159B249");
if (resource == null)
{
throw new Exception("application data cannot be found");
}
int num = InvokeApp(GetAssembly(resource), args);
resource = null;
return num;
}
StartApp方法,从名字上看,应该是调用被加密的源程序。在方法体内,首先调用了GetResource方法,返回了指定的资源,然后调用InvokeApp方法进入主程序。为了弄清楚来龙去脉,我们先看看GetResource方法到底做了什么?代码清单9-19是GetResource方法的源码。
代码清单9-19 GetResource方法源码
private static byte[] GetResource(string id)
{
byte[] buffer = null;
if (rm == null)
{
rm = new ResourceManager("app", Assembly.GetExecutingAssembly());
}
try
{
inResourceResolveFlag = true;
string name = MangleDllName(id);
if ((buffer == null) && (xrRm != null))
{
for (int i = 0; i < xrRm.Count; i++)
{
try
{
ResourceManager manager = (ResourceManager) xrRm[i];
if (manager != null)
{
buffer = (byte[]) manager.GetObject(name);
}
}
catch
{
}
if (buffer != null)
{
break;
}
}
}
if (buffer == null)
{
buffer = (byte[]) rm.GetObject(name);
}
}
finally
{
inResourceResolveFlag = false;
}
return buffer;
}
现在我们对代码清单9-19的代码做简要的分析。
if (rm == null)
{
rm = new ResourceManager("app", Assembly.GetExecutingAssembly());
}
上面这句代码从当前程序集中获取名称为app的资源文件,回到图9-20,我们可以看到app. Resources文件是内嵌在程序集中的,可以被获取。接下来的代码获取指定名称的资源,然后以byte数组的形式返回。返回的资源的用途是什么呢?我们继续分析。
InvokeApp(GetAssembly(resource), args);
上面是StartApp方法最后的调用,GetAssembly方法,从名字上看是获取程序集,其参数是GetResource方法返回的byte数组。我们到它的源码中一探究竟。GetAssembly方法的源码如代码清单9-20所示。
代码清单9-20 GetAssembly方法源码
private static Assembly GetAssembly(byte[] data)
{
MemoryStream stream = null;
Assembly assembly = null;
try
{
stream = UnZip(data);
stream.Seek(0L, SeekOrigin.Begin);
assembly = Assembly.Load(stream.ToArray());
}
finally
{
if (stream != null)
{
stream.Close();
}
stream = null;
}
return assembly;
}
代码清单9-20的代码也很简单,从byte数组转化到程序集。这里我们唯一需要注意的地方是下面这句代码:
stream = UnZip(data);
UnZip方法对byte数组进行解压缩。这个方法是整个程序运行的最关键的方法,但是解压缩的具体实现我们不去关注。如果您感兴趣的话可以自行研究。
得到程序集之后,才真正的开始执行InvokeApp方法,我们看代码清单9-21。
代码清单9-21 InvokeApp源码
private static int InvokeApp(Assembly assembly, string[] args)
{
MethodInfo entryPoint = assembly.EntryPoint;
ParameterInfo[] parameters = entryPoint.GetParameters();
object[] objArray = null;
if ((parameters != null) && (parameters.Length > 0))
{
objArray = new object[] { args };
}
object obj2 = entryPoint.Invoke(null, objArray);
if ((obj2 != null) && (obj2 is int))
{
return (int) obj2;
}
return 0;
}
从代码清单9-21中我们看到,这段代码首先获取程序集的入口函数,也就是Main方法,然后执行。到这里,程序才真正的从外壳程序转到真正的主程序。
结合上面的分析,我们总结一下一个纯.NET压缩壳程序的运行流程:
1) 程序运行时首先运行外壳程序。
2) 外壳程序从其资源中读取主程序的原始数据。
3) 对原始数据解压缩,转化成程序集。
4) 运行主程序。
这种加壳方法的两个关键点,一个是主程序作为壳程序的资源文件存在,第二个是先对资源文件解密然后再反射执行。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn
文章转载自:博客转载面对“数字中国”建设和中国制造2025战略实施的机遇期,中车信息公司紧跟时代的步伐,以“集约化、专业化、标准化、精益化、一体化、平台化”为工作目标,大力推进信息服务、工业软件等核心产品及业务的发展。在慧都3D解决方案的实施下,清软英泰建成了多模型来源的综合轻量化显示平台、实现文件不失真的百倍压缩比、针对模型中的大模型文件,在展示平台上进行流畅展示,提升工作效率,优化了使用体验。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@pclwef.cn
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢