开篇之郑重声明
本系列文章的目的在于通过研究底层,来加强编程技术,涉及到类似于破解等等方面的细节,将不会列出。
第一章 IL汇编语言精要
第二章 .Net反汇编工具
一 reflector
程序集,是.NET时代的动态链接库。Visual Studio内置的ILDASM成为最初挖掘程序集的上佳工具。但,Reflector能提供更多的程序集信息,而且免费。
在Visual studio中编译源代码(无论是VB还是C#)时,编译器都会将高级源代码翻译成MSIL,即“微软中间语言”,而不是特定的机器指令。具有更多安全性、版本控制、共享能力与其它相关元数据的中间语言(IL),是包在一个或多个DLL或可执行文件中的。
基于ILDASM检查程序集中的IL有时很有用。
Reflector可以将.NET程序集中的中间语言反编译成C#或者Visual Basic代码。除了能将IL转换为C#或Visual Basic以外,Reflector还能够提供程序集中类及其成员的概要信息、提供查看程序集中IL的能力以及提供对第三方插件的支持。
默认情况下,Reflector会打开一组公共程序集(mscorlib、System、System.Data、System.Drawing等等)。每个打开的程序集都列在Reflector的主窗口中。单击程序集旁边的+图标可以展开树结构并展示程序集的命名空间。每个命名空间旁边都有一个相关的+图标,单击这个图标将显示这个命名空间内的类。除此以外,还可以展开每个类,显示类的成员:事件、字段、方法与属性。想查看其它程序集(包括我们自己创建的程序集)的细节,你可以使用菜单命令“File→Open”。然后,浏览到想要查看的程序集。示例如下图,
只要浏览到类级的成员,就可以通过Tools菜单中的Disassembler项(或在该项上单击右键)反汇编此成员。这将打开第二个窗格,以C#、Visual Basic、Delphi或者IL显示反汇编后的内容。
目前市场上的.NET混淆产品有:PreEmptive Solution的Dotfuscator、WiseOwl的Dmeanor以及Remotesoft的.NET Obfuscator等。
Reflector还可以显示类与其成员的调用与被调用图、提供单键访问Google或MSDN搜索的能力并提供了允许第三方开发人员为Reflector创建插件的框架。
要查看调用或被调用图,只需要在树视图中选择一个成员,访问Tools菜单,选择Call Graph或Callee Graph选项即可。Call Graph会列出所选项所调用的成员,而Callee Graph列出调用所选项的成员。
通过使用插件,Reflector的功能可以得到进一步扩展。目前有能显示程序集依赖图、自动加载当前运行中的程序集、输出整个程序集的反汇编内容以及在Visual Studio中作为Reflector的宿主等的插件。
存在一个Reflector.VisualStudio插件。有了这个插件,就可以让Reflector集成在Visual Studio环境中,进而取代以前那个有些“拙漏”的对象浏览器。有人试过,似乎无大用。
第三章 VS调试
二 断点
为源行、程序集指令和调用堆栈函数设置断点。 指定条件、命中次数和执行位置。 打印跟踪点。 保存并导出断点。
创建可在需要时中断的断点
标准断点是开发人员的工具箱中最重要的调试技术之一,它会在每次命中源文件位置时中断调试器执行。 Visual Studio 可帮助您超出标准断点,以便创建对执行断点的时间和位置的细化控制。
您可设置当您的程序返回调用堆栈上的函数时暂停执行的运行时断点,并避免长序列的“跳出”命令。
如果您怀疑在一定数量的迭代后代码中的循环开始产生错误行为,则可以设置断点,以在指定的命中数传入相关的代码行后停止执行,而不是强制重复按 F5(“调试”、“继续”)以达到迭代级别。
通过使用代码表达式,您可以指定断点中断的具体条件。
您可使用“断点”窗口来管理大量断点的状态和行为。 如果您已小心构造断点序列以诊断常见的或特别复杂的问题,则可以使用“断点”窗口导入和导出命令来保存或共享这些断点。
避免当正在调试混合模式(本机和托管)代码时在系统组件上设置断点。 在混合模式调试期间,如果在系统组件上设置断点,则会导致公共语言运行时中断并使调试器停止响应。
在“调用堆栈”窗口中的函数返回处设置断点
可通过在“调用堆栈”窗口中设置断点来中断调用函数返回到的指令或行处的执行。 调试器必须处于中断模式。
打开“调用堆栈”窗口(快捷键:Ctrl + Alt + C),并选择要中断的调用函数。
在上下文菜单中选择“断点”、“插入断点”,或者仅使用快捷键:F9。
断点符号出现在函数调用名称旁的左边距中。
如果打开“断点”窗口(快捷键:Ctrl+Alt+B),则该断点显示为一个地址断点,此地址断点具有一个与函数中下一个可执行指令相对应的内存位置。 调试器在指令处中断执行。
若要在执行代码期间直观地跟踪断点,请参阅在 Visual Studio 中调试时映射调用堆栈上的方法。
在“反汇编”窗口中的程序集指令处设置断点
若要在程序集指令处设置断点,必须使调试器处于中断模式下。
打开“反汇编”窗口(快捷键:Ctrl + Alt + D)。
执行下列操作之一:
在窗口的滚动条槽中双击您想中断的行。
- 或 -
选择该行并选择 F9。
在源行、程序集指令或调用堆栈函数处设置断点
在源窗口、“调用堆栈”窗口或“反汇编”窗口中,打开断点的上下文菜单并选择属性。
在“断点”窗口中,选择断点行并打开上下文菜单。 也可在条件列中直接设置一些条件。
当断点被命中次数、表达式计算、执行位置或数据更改中断时进行指定
指定断点执行的命中次数 • 使用代码表达式指定断点条件 • 指定断点执行的设备、进程或线程 • 设置数据更改断点(仅限本机 C++)
指定断点执行的命中次数
“命中次数”跟踪断点的命中次数。 您设置值和条件,以使满足以下条件之一时执行断点:命中次数等于值、等于指定值的倍数或大于或等于值。 指定命中次数和条件:
打开“命中次数断点”对话框。
在源窗口、“反汇编”窗口或“调用堆栈”窗口中,选择包含断点的行,然后选择上下文菜单上的“断点”、“命中次数”。
- 或 -
在“断点”窗口中,选择一个断点行,然后选择上下文菜单上的“命中次数”。
选择条件并输入命中次数。
命中次数条件有助于在一定数量的迭代中中断循环。 如果您要对断点的命中次数进行计数,也可指定非常大的数量,但不要中断执行。
仅为调试会话保留指定的命中次数。 在调试会话结束时,命中次数将重置为零。
当断点被命中次数、表达式计算、执行位置或数据更改中断时进行指定
使用代码表达式指定断点条件
断点条件是一个到达断点时调试器将计算的表达式。 如果满足条件,调试器将中断执行。
条件可以是调试器能够识别的任何有效表达式。 例如,在一个银行业务程序中,您可以设置 balance < 0 等断点条件。
指定断点条件
打开该断点的上下文菜单,然后选择“条件”。
在“断点条件”对话框中,在“条件”框中输入有效表达式。
如果想要在满足表达式时中断,请选择“为 true”;如果想要在表达式的值已更改时中断,请选择“已更改”。
直到第一次到达该断点后,调试器才会计算该表达式。 对于本机代码,如果选择“已更改”,则调试器不会将条件的第一次计算当作一次更改,所以第一次计算时不会命中断点。 对于托管代码,如果选择“已更改”,则选择“已更改”之后的第一次计算时便会命中断点。
如果在设置断点条件时使用了无效语法,将立即出现警告消息。 如果在指定断点条件时使用的语法有效但语义无效,则在第一次命中断点将出现警告消息。 在这两种情况下,当命中无效断点时,调试器都会中断执行。 只有当条件有效并且条件的计算结果为 false 时,才会跳过断点。
当断点被命中次数、表达式计算、执行位置或数据更改中断时进行指定
指定断点执行的设备、进程或线程
打开该断点的上下文菜单,然后选择“筛选器”。
第四章 静态分析
一段关于窗口根据判断是否注册而选择显示“Licensed to XXXX”还是未注册的示例代码。
.method private instance void xb4fe74a3788695e0(object x897f7d97ff45d640,
class [mscorlib]System.EventArgs x92a51111cd18ca81) cil managed
{
// Code size 122 (0x7a)
.maxstack 7
.locals init (class CodeLib.xf9b7e3b2c792f083 V_0)
IL_0000: newobj instance void CodeLib.xf9b7e3b2c792f083::.ctor()
IL_0005: stloc.0
IL_0006: ldsfld bool CodeLib.x83a1ac2c48984168::x6ae105ff7a55905e //注意这句
IL_000b: brfalse.s IL_0017 //然后这里跳转
IL_000d: ldloc.0
IL_000e: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CodeLib.xf9b7e3b2c792f083::get_x6bc866479ed84b6d()
IL_0013: br.s IL_0043
IL_0015: br.s IL_0072
IL_0017: ldloc.0
IL_0018: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CodeLib.xf9b7e3b2c792f083::get_x6bc866479ed84b6d()
IL_001d: ldstr bytearray (AF 06 CD 0D CF 14 78 1B A7 22 B7 29 B6 30 B5 37 // ......x..".).0.7
BC 3E BA 45 A8 4C B2 53 A2 5A 9E 61 58 68 ) // .>.E.L.S.Z.aXh
IL_0022: ldc.i4 0x5c4c0661
//解码后的信息
//Not Registered!
IL_0027: call string xed3d7768b7546353.x7846ebd83ad1c299::_bc22ed13a5229081(string,
int32)
IL_002c: call string [mscorlib]System.String::Intern(string)
IL_0031: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
IL_0036: ldloc.0
IL_0037: callvirt instance class [System.Windows.Forms]System.Windows.Forms.Label CodeLib.xf9b7e3b2c792f083::get_x6bc866479ed84b6d()
IL_003c: call valuetype [System.Drawing]System.Drawing.Color [System.Drawing]System.Drawing.Color::get_Red()
IL_0041: br.s IL_006d
IL_0043: ldstr "Licensed to "
IL_0048: ldsfld string CodeLib.x83a1ac2c48984168::xa07a59ecbea7b301
IL_004d: ldstr "Settings"
IL_0052: ldstr "User"
IL_0057: ldstr ""
IL_005c: call string [Microsoft.VisualBasic]Microsoft.VisualBasic.Interaction::GetSetting(string,
string,
string,
string)
IL_0061: call string [mscorlib]System.String::Concat(string,
string)
IL_0066: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
IL_006b: br.s IL_0015
……