周一上班接着补充。
这些天状态的确低迷。上午对着电脑发了好一会呆。但活还是要干的。
上午的改进完了以后,面对一些小问题。
1。如何自动在反编译后的工程中,加入引用路径。
2。 在资源文件中,如果用到了模板类,则编不过。
这个走了一点弯路。原以来为这个信息在.csproj文件,原来不是这样,而是在.csproj.user中。
有些吃惊,一直以为那个文件没有用。
先说一下为什么要有这样的需求:
因为正在看的程序,内容很多,但所感兴趣的东西,只是冰山一角,虽然这个一角也不小,所以,只需要看几个DLL就OK了。
但这些DLL是引用了其它的DLL的,所以,加引用路径是最简单的事情。当然,可以事先把一些可以注册的库用:gacutil /i
注册一下,也是个办法。
这里选择用引用路径。
发现引用路径在.csproj.user中,就好办了,只要找到一个地方,加一段代码就OK了:
在:ILSpy\TextView\DecompilerTextView.cs
中改进一下如下的代码就OK了。
Task<AvalonEditTextOutput> SaveToDiskAsync(DecompilationContext context, string fileName) { TaskCompletionSource<AvalonEditTextOutput> tcs = new TaskCompletionSource<AvalonEditTextOutput>(); Thread thread = new Thread(new ThreadStart( delegate { try { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); //haoyujie 写入工程文件 using (StreamWriter w = new StreamWriter(fileName)) { try { DecompileNodes(context, new PlainTextOutput(w)); } catch (OperationCanceledException) { w.WriteLine(); w.WriteLine("Decompiled was cancelled."); throw; } } //把user设置拷到指定位置 //最后,把引用路径模板拷过去,因为需要引用许多其它的库,引用路径在这个文件中指定 string strUserTmppath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory + "hayujie_usertmp.usertmp"); string strDescUserpath = fileName + ".user"; FileInfo fi = new FileInfo(strUserTmppath); if (File.Exists(strDescUserpath)) { File.Delete(strDescUserpath); } fi.CopyTo(strDescUserpath); stopwatch.Stop(); AvalonEditTextOutput output = new AvalonEditTextOutput(); output.WriteLine("Decompilation complete in " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds."); output.WriteLine(); output.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); }); output.WriteLine(); tcs.SetResult(output); } catch (OperationCanceledException) { tcs.SetCanceled(); #if DEBUG } catch (AggregateException ex) { tcs.SetException(ex); #else } catch (Exception ex) { tcs.SetException(ex); #endif } })); thread.Start(); return tcs.Task; }至于里面提到的模板文件hayujie_usertmp.usertmp,可以自己想名字,内容也可以自己写,给出一个例子:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <ReferencePath>E:\work\lib\</ReferencePath> </PropertyGroup> </Project>
提不起精神,感觉代码写得对不起观众。
在反编译过程中,发现有一个resource.baml反编译不成功。
仔细分析后一天多,才明白,这里面用到了模板类,不了解在C#中叫什么,在C++里要模板,
也就是说,在一个核心库中定义了一个模板类,然后这个模板类的实现(c#是不是叫装箱),相当于一个类,在使用者的库中才被编译器实现,也就是说,实际上它在两个地方,当然在C++只会存在于一个地方,因为C++的模板,是预编译阶段完成的。
但好象C#的实现,有点象容器(所以模板在C#中好象就叫容器类),两边的库都要用到,这也容易理解,C#是解释性语言。
各有各的好外,总得来说,还是装箱这种方式,简单得多。
而这个类,恰好被resource.baml里面引用到了。而ILSPY就是错了。
具体出错的内容
模板类名`[[实现类的命名空间,类名,版本号,KEY]]注意,那个`,是最奇怪的,不清楚是怎么出来的,60H,就是键盘上esc 下面的那个键。见鬼啊。
而正常的类名,只有一个模板类名。然后ILSpy ,以大无畏的猜测的精视,猜出前面是命名空间,后面是类名,当然,大多数时候,它都是对的。
对微软的统一语言进行时的二进制码不熟,所以,一步步逆推到这,就停下了,因为再整,就对不起老板给开的工资了,毕竟还有工作要做。
如果有哥们对二进制码非常熟的,请告知。
从内存中,看到ILSPY对于模板类的反编译,并不正确之后,决定稍稍修剪一下代码,虽然结果不完全正确,也总比什么都不出来强。
后来,真的可用了。
这里,把改完的代码放在这里:
ILSpy.BamlDecompiler\Ricciolo.StylesExplorer.MarkupReflection\XmlBamlReader.cs
void ReadTypeInfo() { short typeId = reader.ReadInt16(); short assemblyId = reader.ReadInt16(); string fullName = reader.ReadString(); assemblyId = (short)(assemblyId & 0xfff); TypeDeclaration declaration; int indexpp = fullName.IndexOf("[["); //说明这个类是模板类 if (indexpp >= 0) { int indexppend = fullName.IndexOf("]]"); //说明这个类是模板类 fullName=fullName.Substring(indexpp+2,indexppend-indexpp-2); } if (fullName.IndexOf(',')>=0) { int douhao = fullName.IndexOf(','); fullName = fullName.Substring(0,douhao); } int length = fullName.LastIndexOf('.'); if (length != -1) { string name = fullName.Substring(length + 1); string namespaceName = fullName.Substring(0, length); declaration = new TypeDeclaration(this, this.Resolver, name, namespaceName, assemblyId); } else { declaration = new TypeDeclaration(this, this.Resolver, fullName, string.Empty, assemblyId); } this.typeTable.Add(typeId, declaration); }
这段代码,对原有的代码进行了修改,最后可以工作,但结果应该是不正确的,但也不耽误用了。
另外,这里,把在逆推的过程中,用到的代码,也贴出来,减少大家逆推的时间。
因为逆推有几个难题:
1。 出错的时候,读入错误信息的地方,早就过去了。
2。文件流stream,是顺序的,不是随机的,这样,就不能再读一次。
所以,加了这段代码分析内存:
ILSpy.BamlDecompiler\Ricciolo.StylesExplorer.MarkupReflection\BamlBinaryReader.cs
public override string ReadString() { //haoyujie,为调试资源错误,因为现在在资源中,如果出现模板类,就会出错。 string strInner = base.ReadString(); if (strInner.StartsWith("出错的那个模板的类的名称")) //这句是重点,加入拦截 { int ll = 500; Stream basestream = base.BaseStream; byte[] buffer = new byte[ll]; if (basestream.CanSeek) { basestream.Seek(-ll, SeekOrigin.Current); } buffer = base.ReadBytes(ll); } return strInner; }
这个系列,也就基本上结束了。