简介
欢迎到我的写作。NET程序集反编译的旅程。首先,我将尝试开发一个理论来反编译的MSIL。我只是做我做任何MSIL指令要求。但我牢记,我反编译的MSIL。因此,当问我推了一个变量的值,我把该变量在栈上的名称。简单的案例
理解代码,它是必需的,你知道每个MSIL指令实际上没有什么参考。下面是一个示例程序来测试,如果我们的概念作品:namespace DisasmIL
{
class Math
{
public int add(int x, int y)
{
return x + y;
}
}
class Program
{
static void Main(string[] args)
{
Math m;
int a, b;
m = new Math();
a = 20;
b = 50;
int p = m.add(a, b);
}
}
}
我们只检查方法之一。主要方法。的主要方法是编译时,它的形式如下。{C}
我们解析一行行:.method private hidebysig static void Main(string[] args) cil managed
这是一个具有默认的起始大括号的方法声明。输出码:static void Main(string[] args)
{
堆栈:[空].entrypoint
// Code size 23 (0x17)
.maxstack 3
.locals init (
[0] class DisasmIL.Math m,
[1] int32 a,
[2] int32 b,
[3] int32 p
)
这里是无需解释。他们言自明。声明变量。输出码:DisasmIL.Math m;
int a;
int b;
int p;
堆栈:[空]IL_0000: nop:
无关(NOP)
输出代码:[无]
堆栈:[空]IL_0001: newobj instance void DisasmIL.Math::.ctor()
我们的堆栈上创建一个新的DisasmIL.Math例如,使用一个默认的构造函数,所以我们把"新DisasmIL.Math()quot;
,输出的代码:
没有堆栈:新DisasmIL.Math(。 )IL_0006: stloc.0
所以我们弹出堆栈的顶部和分配到一个局部变量0。输出码:m = new DisasmIL.Math();
堆栈:[空]IL_0007: ldc.i4.s 20
我们做的是在堆栈上推恒定20
输出代码:[无]
堆栈:20IL_0009: stloc.1
所以我们弹出的最高价值和它分配给局部变量1。输出码:
堆栈:[空]IL_000a: ldc.i4.s 50
我们在栈上推恒定50
输出代码:[无]
堆栈:50IL_000c: stloc.2
弹出顶部的价值,并将其分配到一个局部变量2
输出代码。b = 50;
堆栈:[空]
推局部变量0,tstack
输出代码1和2:无
堆栈:M,A,BIL_0010: callvirt instance int32 DisasmIL.Math::'add'(int32,int32)
IL_0015: stloc.3
我们调用add - 1,堆栈的顶部,例如顶级2值的方法。对于任何方法调用,如果它返回一个值,它是返回堆栈上。因此,检查的下一条指令。如果它是一个stloc,那么我们指定返回值。我们的返回值分配给局部变量3
输出代码。p=m.add(a,b);
堆栈:[空]IL_0016: ret
返回void。所以除了右大括号中的代码。输出码:}
堆栈:[空]
现在,如果你添加的输出码在一起,你会发现原来的C#代码生成。这简单的情况下。我们需要测试它是否为复杂的情况下。让我们现在做一些更有趣的事情。if结构
让我们来看看最基本和最有用的if - then结构。编译器会生成一个条件分支。我们创建了一个结构的指示块。块是分隔的分支指令(像brtrue)和分支标签(如IL_0019 - 其中的代码跳转)。我们的条件是在栈上。如果我们找到一个真正的条件分支,我们否定它,并把它作为一个if语句的条件。块是最初的MSIL块,我们将转换后的C#代码(我们可能需要递归这里吗?)。请注意,标签不存储在MSIL。它只是在一个方法的MSIL偏移字节。
我们举一个简单的方法来测试我们的理论与if结构。public int IfStructure(int a, int b)
{
if (a < b)
{
System.Console.Write("Condition is true");
}
return b;
}
这里是由Visual Studio 2005编译器生成的MSIL代码。.method public hidebysig instance int32 IfStructure(int32 a, int32 b) cil managed
{
// Code size 31 (0x1f)
.maxstack 2
.locals init ([0] int32 CS$1$0000,
[1] bool CS$4$0001)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: clt
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000a: brtrue.s IL_0019
IL_000c: nop
IL_000d: ldstr "Condition is true"
IL_0012: call void [mscorlib]System.Console::Write(string)
IL_0017: nop
IL_0018: nop
IL_0019: ldarg.2
IL_001a: stloc.0
IL_001b: br.s IL_001d
IL_001d: ldloc.0
IL_001e: ret
} // end of method ControlStructures::IfStructure
我们现在开始解析:.method public hidebysig instance int32 IfStructure(int32 a, int32 b) cil managed
{
// Code size 31 (0x1f)
.maxstack 2
.locals init ([0] int32 CS$1$0000,
[1] bool CS$4$0001)
这些行生成的输出,我们没有做任何处理的MSIL。有一个方法定义和局部变量。我们改变局部变量名到C#目前的名称,而不会发生冲突。为了简单起见,这里我们只需更换'$'用'_'。评估MSIL指令的一件事,我们必须保持一个局部变量与变量数的地图。在这个例子中,政务司司长$ 1 $ 0000是本地的int类型的变量0。为了清楚起见,我们不显示位置图。一个简单的STL地图应工作
输出。public int IfStructure(int a, int b)
{
int32 CS_1_0000;
bool CS_4_0001;
堆栈:[空]
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
因此,我们推堆栈方法的参数1和2:
输出:[无]
堆栈:A,BIL_0003: clt
这教导我们,如果堆栈的顶部是小于堆栈的顶部。这两个元素从堆栈中弹出,结果到堆栈。当然没有输出
输出:[无]
堆栈:
ALT,BIL_0005: ldc.i4.0
负载(指推)值为0的整数常量堆栈
输出:[无]
堆栈:ALT,B,0。IL_0006: ceq
检查堆栈顶部1等于堆栈的顶部。结果到堆栈
输出:[无]
堆栈:LT; B == 0IL_0008: stloc.1
IL_0009: ldloc.1
还有什么呢?存储在局部变量堆栈的顶部,并再次加载跟踪。我们决定以前,当我们在一个局部变量存储一定的价值,我们使用分配给该变量和输出代码。为了清楚起见,我添加了括号:
输出:CS_4_0001 = (a < b == 0)
堆栈:CS_4_0001IL_000a: brtrue.s IL_0019
我们有一个条件分支。我们创建了一个块的开始从这里到IL_0019。并把他们在大括号。我们的条件是在栈上。我们找到一个真正的条件分支,所以我们否定它,并把它作为一个结构如果我在开始时说
输出。if(!CS_4_0001)
{
IL_000c: nop
IL_000d: ldstr "Condition is true"
IL_0012: call void [mscorlib]System.Console::Write(string)
IL_0017: nop
IL_0018: nop
}
堆栈:[空]IL_0019: ldarg.2
IL_001a: stloc.0
IL_001b: br.s IL_001d
IL_001d: ldloc.0
IL_001e: ret
我们不分析它们在这里。他们是很简单的了解和使用我们在简单的例子中看到的方法,我们可以分析它们。
确定,我们现在可以稍微复杂一些的代码工作。这也将产生quot产生的代码; structurequot,但一个有趣的方式。如果我们添加多一点智力产生quot; gotoquot;输出的特殊分支,我们不能用,如果处理的代码,我们得到以下结果
这样的代码... ...。for(int i=0;i<10;i++)
{
...
}
...
...将被转换为:int i;
i=0;
label_1:
if(i<10)
{
---
i++;
goto label_1;
}
...
OK,但没有问题。稍后我们来看看循环。现在,你可能会发现,我们的理论产生一个有趣的代码块,如:CS$4$0001=((a<b)==0);
if(!CS$4$0001)
{
---
}
这里的优化场景。但是,我们现在跳过它。循环
我想,作为一个反编译器作家,就没有循环。程序员使用数以千计的goto语句与if语句。但因为它是没有的情况下,我必须了解如何解析MSIL循环生成的指令。
最常见的循环的三种类型(同时,同时)while循环的基础之一。从任何类型的这些循环产生的块通常有一个条件跳转(通常是一个brtrue.slt; labelgt;)作为最后一个指令块。从如果差分结构的指令跳转的偏移量小于当前指令的偏移量。 for和while循环有一个无条件分支(br.slt; labelgt;)偏移块的开始和结束之间。跳转的目标通常是在开始条件检查的指示。 DO - while循环缺乏的原因这个分支 - 块结束之前,它不会测试条件。
所以,我们得到类似下面的MSIL块的指令块:LT / labelgt; LT / labelgt;IL_0010: br.s IL_005a ;do-while loop does not have this line
IL_0012: nop
[any type and number of instructions]
IL_0059: nop
[condition check instruction- results boolean value on stack]
IL_0060: brtrue.s IL_0012
一边看着,如果结构,我们已经看到如何创建一个布尔条件,如果结构。东西都是类似的循环。按照说明 - 堆栈顶元素时,发现条件跳转 - 反向(添加只是一个!)brtrue.s跳,并把它作为循环语句条件语句。请注意,条件跳转的目标只是在或块的起始指令后的指令。
在这里,我们发现,我们不能有一个单一的通过反编译。我们必须确定在迭代前的最后一次迭代的代码块。到目前为止,我们可以识别的if块,同时,做结构,而通过使用条件跳转指令和他们的目的地。如果有目的地后偏置电流和其他目的地之前,当前偏移的偏移。并不能区分得很清楚,但做而没有跳的开头。当然,可以从嵌套循环生成的嵌套块
有一些复杂的循环变化 - 无限循环一样,foreach循环等,他们都没有太大的不同。但现在,让我保持未完成的,等待你的回应,修正错误,并继续再进一步。发表评论,投票支持它,并显示我的失误。工具
我已经使用Visual Studio 2005和MSIL反汇编程序自带的。NET平台的安装。历史2007年9月24日:战后初期