{A}
{S0}简介
在我的工作经验,我不得不处理一些用户的错误报告,关于我们公司的产品之一。这些报告包括旨在帮助我们检测错误导致的调用堆栈信息。
我们使用后,我们的生产代码混淆工具,所以调用堆栈错误报告所提供的信息,需要一些Äúhopping左右??一个模糊的地图和手册文本搜索。这Äúhopping??并不总是一件容易的事情做??混淆图是一个巨大的的XML文件的大小超过25 MB,和大多数文本编辑器不欣赏这样的信息量。这类编辑器的偏好是合理的,假设人为一个普通的文件很少超过1 MB边界上运行。
事情变得更糟,当你需要一些语法突出显示,甚至更多??XML树解析/导航。另一个大问题是有数量庞大的XML元素具有相同的混淆名称,以确定其类型,您应该手动分析父XML元素。任务定义
面对这些问题,我决定由名称解析的过程自动化,以帮助我们的支持团队。自动化任务的要求有以下几条:这应该是一个工具,可以找到原始类或类成员名称上的混淆名称。应尽可能简单的UI??我认为简单的任务不应该要求复杂的用户操作。使用LINQ to XML??这是一个方便和简单的方法来处理XML数据。但是,也许,主要因素是,我,终于有机会在实践中使用这一技术。
,AOS的具体步骤。首先,我们定义输入数据。
下面是调用堆栈的内容例如:Type: System.ArgumentException
Stack:
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary.Insert(TKey key, TValue value, Boolean add)
at ne.c(IError A_0)
at ne.c(ErrorListEventArgs A_0)
at ne.c.a()
at ne.c(Object A_0, EventArgs A_1)
at System.Windows.Forms.Timer.OnTick(EventArgs e)
at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
IntPtr wparam, IntPtr lparam)
本公司使用的工具,其社区版是随Visual Studio中。混淆地图所有混淆器版本相同的格式,所以任何人都可以测试自己的代码名称解析工具。混淆地图是一个XML文件,其结构看起来像这样:{C}
LT; typegt;元素的结构如下:<type>
<name>type_name</name> <!--Original type name.-->
<newname>obfuscated_name</newname> <!--Obfusctaed type name (Optional).-->
<methodlist> <!--Types method list.-->
<method>
<signature>void(object, System.EventArgs)</signature> <!--Method signature.-->
<name>method_name</name> <!--Original method name.-->
<newname>obfuscated_name</newname> <!--Obfusctaed method name (Optional).-->
</method>
...
</methodlist>
<fieldlist> <!--Types filed list.-->
<field>
<signature>System.Windows.Forms.Button</signature> <!--Field signature.-->
<name>field_name</name> <!--Original field name.-->
<newname>obfuscated_name</newname> <!--Obfuscated field name (Optional).-->
</field>
...
</fieldlist>
</type>
你会发现,混淆的名字始终是放置在一个可选的LT; newnamegt;元素。如果省略此元素,则该对象使用其原始名称。
接下来,我们应该定义用户的输入。例如,我们需要找到一个类型混淆的名称,AUA??通常情况下,我们搜索LT;吗??newnamegt; ALT / newnamegt ;???/ EM>字符串,这将找到的所有类型,方法和领域有混淆的名称,AOA???有大约数千复杂项目的结果。要实现我们的搜寻目标,我们应该父元素分析和检测,如果它是一个LT; typegt;元素。
因此,用户通常使用两个参数:第一个参数是一个模糊的地图文件的路径,第二个参数是一个混淆的名称。也有一个(隐式)的参数吗??搜索结果类型(类型/方法/领域),但我们会尝试从第二推断此参数。据用户界面简单的要求,我觉得这就够了。键入名称搜索
第一个任务是一个原始类型的名称使用混淆的名称进行搜索。让,AOS做到这一点。首先,我们必须争取从所有类型的映射文件。这是一个简单的:// Construct the XElement to access map file.
XElement map = XElement.Load("sample.xml");
var types = map.Descendants("type");
这里的主要操作是map.Descendants("; typequot";)调用,它返回所有的LT; typegt从XElement的内容元素。
的descendants()方法返回一个XML元素的后代的纯集合。该集合包含子元素,孙子元素等,所以,如果我们写map.Descendants(),我们会从地图文档中的所有XML元素枚举。这种方法有一个重载,允许指定匹配的元素的名称过滤器过滤输出集合。我用这个重载筛选出的LT以外的所有元素; typegt;
注意:过滤器的名称应该是一个完全合格的名称,这意味着过滤元素,如果有一个命名空间,必须有过滤器的名称也。
注意:请记住,后人使用延后的执行,这意味着实际的访问底层的XML将执行当您第一次访问的后裔,而不是当你调用这个函数的结果。
map.Descendants("; typequot";)将指定元素类型的扫描的整个XML树,它是不是最有效的解决方案,但最简单的一个。使用直接降低了整个XML扫描会更有建设性的导航元素。例如,我们可以使用这样的表达:var types = map.Elements("mapping").Elements("module").Elements("type");
根据XML内容,这种表达可以给我们十倍的性能提升比后人来。但对于这种应用,我更喜欢简单的后裔功能。
现在我们都在LT; typegt;元素,需要找到与混淆的名称匹配。我实现了这个使用LINQ查询:string obfuscatedName = "a"; // Define the search criteria.
var found = from type in types
// Filter type elements by obfuscatedName matching.
where type.Element("newname").Value == obfuscatedName
select type;
类型的集合,是相匹配的类型过滤,AOS子元素LT; newnamegt;通过混淆名称的内容。这也可以使用Where扩展方法,lambda表达式:var found = types.Where(t => t.Element("newname").Value == obfuscatedName);
正如前文所述,LT; newnamegt;元素是可选的,所以返回null元素("newnamequot;)时,该类型是不混乱。为了避免可能的NRE,我,Äôve改变LINQ查询以下:var found = from type in map.Descendants("type")
// Declare name element variable.
let name = type.Element("newname") ?? type.Element("name")
// Filter type elements by obfuscatedName matching.
where name.Value == obfuscatedName
select type;
这段代码将搜索混淆或原来的名称匹配obfuscatedName类型。
let关键字引入了一个新的变量名,拥有一个LT; newnamegt;元素或LT; namegt;元素的情况下没有LT; newnamegt元素存在。这个新变量是一个匿名类型,包含了当前LT; typegt;元素和一个LT; namegt; /公升; newnamegt;元素。类似的东西:new { Type = type, Name = type.Element("newname") ?? type.Element("name") };
整个查询可代表在C#作为:IEnumerable<xelement> found = map.Descendants("type").
Select(type => new { Type = type, Name = type.Element("newname") }).
Where(tn => tn.Name.Value == obfuscatedName).
Select(tn => tn.Type);
我们可以看到,有第二个选择函数调用,(结合匿名类型的投影)会给我们带来一些性能损失,所以我重写查询以下:var found = from type in map.Descendants("type")
// Filter type elements by obfuscatedName matching.
where (type.Element("newname") ?? type.Element("name")).Value ==
obfuscatedName
select type;
接下来要做的事情是处理复杂类型的名称。在XML中,这些名称是分开的,而不是由?????? ??????例如,ÄúMyClass.MyInternalClass??/ em>的名称是,ÄúMyClass / MyInternalClass??/ em>的字符串值。我们只需要在obfuscatedName变量替换??????上??????允许匹配:obfuscatedName = obfuscatedName.Replace('.', '/');
最后,我们提供了匿名类型的投影,这将有助于我们处理在C#中的搜索结果:var types = from type in found
select new {
ModuleName = type.Parent.Element("name").Value,
TypeName = type.Element("name").Value
};
之后,你可以处理搜索结果如你所愿,例如,输出到控制台:
摘要foreach (var type in types) {
Console.WriteLine("Module: {0}", type.ModuleName);
Console.WriteLine("Type: {0}", type.TypeName);
Console.WriteLine();
}
,AOS。我们已经发现的类型,提供了混淆的名称。在,我会一步到LINQ查询更深提供的字段和方法的名称解决解决方案。
感谢您的时间,并欢迎您发表任何问题或建议。