.net软件反编译笔记

时间:2021-12-16 11:42:17

在软件的破解及源码获取及重新编译的道路上会遇到一些问题,书此备查。

大名鼎鼎的Reflector以及开源的ILSPY都是.NET程序集的反编译利器,但是它们不能为你做全部的工作。

0x01:

遇到反编译所得源码里面调用类的属性时多出set_Name或者get_Name之类的说明程序集引用没有加载完全,

因为反编译工具在没有元数据的情况下无法判断这丫的到底是个方法还是个属性。

有时候反编译得到的源码里的-1实际上可能是某个数的最大值,因为反编译时没有加载依赖的程序集,工具将类似int.MaxValue等值变成了-1。

所以将依赖项加载完是一个好的习惯,Reflector在反编译时找不到依赖会提示你手动选择,你可以手动选择或者忽略(点取消就是忽略),而ILSPY不会提示。

0x02:

Reflector新版支持C#6的特性,会把一些属性赋值反编译为Lumbda表达式,而实际该项目可能不是C#6的项目,造成无法通过编译器检查。

解决办法是生成程序集的源码之前从主界面工具栏那个下拉列表选择合适的.NET框架版本。

0x03:

一些WPF强名称应用程序会由于资源文件也是强名称引用导致反编译为项目后跑不起来。

或许重新签名然后替换资源引用的公钥可以解决此问题但我没有试过。

0x04:

在反编译MVC Web项目时,控制器类和预编译视图里(如果发布时启用的了话)会出现很多类似这样的类

    [CompilerGenerated]
private static class <Index>o__SiteContainer19
{
public static CallSite<Func<CallSite, object, string, object>> <>p__Site1a; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1b; public static CallSite<Func<CallSite, object, DateTime, object>> <>p__Site1c; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1d; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1e; public static CallSite<Func<CallSite, object, string, object>> <>p__Site1f; public static CallSite<Func<CallSite, object, string, object>> <>p__Site20; public static CallSite<Func<CallSite, object, string, object>> <>p__Site21;
}

这些代码是编译器为优化性能生成的静态类及静态属性。参考源码后你会发现,这些代码简单的还是比较容易还原的,复杂的你就留着吧。

我的做法是干掉[CompilerGenerated]特性,批量替换类名<Index>o__SiteContainer19为o__SiteContainer19,然后替换属性中的<>为空字符串。

0x05:

有时候我们并不需要源码,只是需要修改某个值或一处代码。所以我们需要的是Reflexil,当然还有更厉害的mono.cecil和dnlib,这是我知道的最牛逼的三个项目。

个人喜欢用ILSPY看源码,然后用mono.cecil改程序。

可是修改后发现并不能运行,因为开发商使用了程序集强名称。所以我一般会使用mono.cecil做如下操作来干掉干掉公钥签名

         var asmdef = AssemblyDefinition.ReadAssembly(dll.FullName);
if (asmdef.Name.PublicKey != null && asmdef.Name.PublicKey.Any())
{
asmdef.Name.PublicKey = new byte[];
asmdef.Name.PublicKeyToken = new byte[];
asmdef.Name.Attributes = AssemblyAttributes.SideBySideCompatible;
asmdef.MainModule.Attributes &= ~ModuleAttributes.StrongNameSigned;
         }

可是修改后发现并不能运行,因为开发商使用了强名称的InternalsVisibleTo特性,所以我一般会使用mono.cecil做如下操作来干掉InternalsVisibleToAttribute值里面的公钥

         #region//0x02 干掉InternalsVisibleToAttribute值里面的公钥
var internalsVisibleToAttrs = asmdef.CustomAttributes.Where(x => x.AttributeType.Name == "InternalsVisibleToAttribute").ToList();foreach (CustomAttribute item in internalsVisibleToAttrs)
{
var argsctor = item.ConstructorArguments.Single();
var commaIndex = argsctor.Value.ToString().IndexOf(",");
if (argsctor.Value != null && commaIndex != -) //format is "AsmName,PublicKey=..."
{
var newValue = item.ConstructorArguments[].Value.ToString().Substring(, commaIndex);//format is "AsmName"
item.ConstructorArguments[] = new CustomAttributeArgument(argsctor.Type, newValue);
var index = asmdef.CustomAttributes.IndexOf(item);
asmdef.CustomAttributes.RemoveAt(index);
asmdef.CustomAttributes.Insert(index, item);
}
}

可是修改后发现并不能运行,因为开发商使用了程序集强名称导致编译引用的也是强名称的程序集,所以我一般会使用如下操作来干掉引用定义里面的公钥

         
         //这里假设引用的该产品的程序集都是SupperApp.*.dll格式的
         var asmRefs = asmdef.MainModule.AssemblyReferences
.Where(x => x.FullName.StartsWith("SupperApp", StringComparison.OrdinalIgnoreCase)
&& x.PublicKeyToken != null && x.PublicKeyToken.Any())
.ToList();foreach (var item in asmRefs)
{
item.PublicKeyToken = new byte[];
}

以上操作后把变更写入该软件就可以顺利运行了。

0x06:

当然有些加了壳的,一般情况需要找到对应的脱壳工具。如果只是修改关键逻辑通过关键词查找应该能找到蛛丝马迹,但前提是要能看到源码。

对于既没有工具能脱壳也没有工具能看源码的可以求助C++反汇编高手。