如何使用ILAsm与ILDasm修改

一、背景

最近项目组新上项目,交付的时间比较急迫,原本好的分支管理习惯没有遵守好,于是出现下面状况:

  1. 多个小伙伴在不同的分支上开发。
  2. 原本QA环境也存在一个阻碍性的bug A
  3. 一位同事在QA环境发布了新的代码,引入了新bug B
  4. 回滚QA能修改bug B,但是对于bugA却无能为力
  5. 同时,混乱的代码管理已经导致无法确定原始发布包对应的代码版本。

最终陷入了两难的地步,既不能发布新包,回滚也无法解决问题。
好在之前了解到如何使用微软官方工具ILAsm与ILDasm对dll文件进行修改,于是开始动手实现。下面将会用示例代码讲解如何修改已经的.exe文件。

  本文主要介绍IL代码,内容大部分来自网上,进行整理合并的。

二、ILAsm与ILDasm

我们知道,.net是一个跨平台的的开发平台,其跨平台则是由其编译的中间语言(Intermediate
Language,
简称IL或MSIL)实现,无论我们使用的是C#、VB.Net、还是F#或者C++,
最终都会被编译成IL,由JIT(Just In Time)编译成目标机器语言,在CLR(Commen
Language Runtime, 公用语言运行时)上运行。

因此,理论上,我们可以跳过过平时使用的C#代码,直接修改IL,然后生成相应的dll或者exe文件。

那么如何查看与修改IL呢,这就是ILAsm与ILDasm的工作了。ILAsm (MSIL
Assembler),用来从IL语言生成PE(Portable
Executable),也就是.net中我们使用的.exe、.dll文件。ILDasm (MSIL
Disassembler),则与ILAsm相反,从PE文件,生成.IL文件。那么我们可以猜到,要修改dll,我们需要先用ILDasm反编译.dll生成.il文件,再用ILAsm编译修改后的.il文件生成.dll,最终替换.dll文件。

一、IL简介

 为什么要了解IL代码?

  如果想学好.NET,IL是必须的基础,IL代码是.NET运行的基础,当我们对运行结果有异议的时候,可以通过IL代码透过表面看本质;IL也是更好理解、认识CLR的基础;大量的实例分析是以IL为基础的,所以了解IL,是读懂他人代码的必备基础,同时自己也可以获得潜移默化的提高;

三、使用ILDasm生成IL

先看下示例代码:

    class Program
    {
        static void Main(string[] args)
        {
            var loginResult = Login("foo", "111111");
            if (loginResult)
            {
                Console.WriteLine("登录成功");
            }
            else
            {
                Console.WriteLine("登录失败,请重试");
            }
            Console.ReadLine();
        }
        private static bool Login(string userName, string password)
        {
            if (userName.Equals("johnny") && password.Equals("123456"))
            {
                return false;
            }
            return false;
        }
    }

显然,上述代码针对Login(string userName, string password)的调用会返回false,导致最后Console中会输出“登录失败,请重试”,
我们的目的是通过直接修改.exe文件,让它返回true,
Console里面输出“登录成功”
ILDasm与ILAsm已经包含在Visual
Studio发行包中中,无需另外下载安装。按如下步骤执行即可:

  1. Developer Command Prompt for VS 2017,在里面输入命令ILDasm
  2. 在打开的IL Dasm窗口中找到需要修改的.exe文件。
  3. 选择菜单 File >
    Dump
    ,弹出的新窗口中点击确认,保存生成的.il文件。

整个过程如下面gif所示:
图片 1

最后会生成相应的.il与.res文件。
图片 2

 什么是IL?

  IL是.NET框架中中间语言(Intermediate
Language)
的缩写。使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直接执行的机器代码,而是一种中间语言IL(Intermediate
Language)的代码(来源百度)。

   IL指令大全  点这里

   IL代码编译器 ILDasm   点这里

四、修改IL

打开.il文件,会看到如下代码(节选)

  .method private hidebysig static bool  Login(string userName,
                                               string password) cil managed
  {
    // Code size       43 (0x2b)
    .maxstack  2
    .locals init ([0] bool V_0,
             [1] bool V_1)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldstr      "johnny"
    IL_0007:  callvirt   instance bool [mscorlib]System.String::Equals(string)
    IL_000c:  brfalse.s  IL_001b

    IL_000e:  ldarg.1
    IL_000f:  ldstr      "123456"
    IL_0014:  callvirt   instance bool [mscorlib]System.String::Equals(string)
    IL_0019:  br.s       IL_001c

    IL_001b:  ldc.i4.0
    IL_001c:  stloc.0
    IL_001d:  ldloc.0
    IL_001e:  brfalse.s  IL_0025

    IL_0020:  nop
    IL_0021:  ldc.i4.0
    IL_0022:  stloc.1
    IL_0023:  br.s       IL_0029

    IL_0025:  ldc.i4.0
    IL_0026:  stloc.1
    IL_0027:  br.s       IL_0029

    IL_0029:  ldloc.1
    IL_002a:  ret
  } // end of method Program::Login

这个就是Login(string userName, string password)所对应的IL代码了。如果你了解IL语言,可以直接对其修改。
如果不想直接修改IL,我们可以重写一个小的示例方法,直接return true,如下:

private static bool Login()
{
    return true;
}

然后使用ILDasm生成相应的IL代码,替换我们想修改的方法。最终的IL如下:

.method private hidebysig static bool  Login(string userName,
                                               string password) cil managed
  {
    .maxstack 8

    IL_0000: ldc.i4.1     
    IL_0001: ret          

  } // end of method Program::Login

 C#代码编译过程?

  C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令;JIT编译器把IL代码转为机器识别的机器代码。如下图

图片 3

  语言编译器:无论是VB code还是C# code都会被Language
Compiler转换为MSIL

  MSIL的作用:MSIL包含一些元数据和中间语言指令

  JIT编译器的作用:根据系统环境将MSIL中间语言指令转换为机器码

  为什么ASP.NET网站第一次运行时会较慢,而后面的执行速度则会相对快很多?

  当你第一次运行.NET开发的站点时,CLR会将MSIL通过JIT进行编译,最终转换为执行速度非常快的Native
Code。这可以解释。

  关于这一部分,我也不大熟悉,以后再学习。

五、使用ILAsm生成exe

修改保存完.il文件以后,接下来的工作就是利用 ILAsm
让.il文件生成重新生成.exe可执行文件了,在Console中执行如下命令

ilasm ILAsmAndILDasmDemo.il /output:ILAsmAndILDasmDemo_1.exe
// 如果修改的是dll文件,需要加上参数 /dll

图片 4

成功以后会生成一个ILAsmAndILDasmDemo_1.exe文件,执行这个文件,我们可以看到,现在已经显示“登录成功”了。
图片 5
图片 6

使用反编译工具
dotPeekI查看新生成的ILAsmAndILDasmDemo_1.exe文件,我们能够看到,Login(string userName, string password)已经直接return true了。如下图,
图片 7

二、怎么查看IL代码

六、总结

其它,上面所做的事情其实也是《CLR via C#》中提到强名能够
防止代码被不怀好意的人篡改
的一个反面教材了。通过这个例子,大家应该对代码被篡改的风险也有一定的认识了,所以如果大家需要将自己的.dll(.exe)文件露给别人,最好还是打上强名,防止别人恶意篡改你的代码,导致不必要的损失。

经过这次事件,也证明了多了解下底层还是很有必要的,说不定哪天就用上了。平时再忙,也不能只限于只业务和编程语言层面,还需要对底层有一定的了解,这样才能知其然与知其所以然。

虽然此次在不修改c#代码的情况下完美解决QA的环境问题,但是这种方法也只限于小范围的改动,只试用于救急。所以给我的教训是分支管理规范才是王道,要能做到随时可发布,随时可回滚才行,这样才能完全避免再次出现这样尴尬的情况了。

  (一)工欲善其事,必先利其器

   现在可以查看IL的工具其实很多。

   (1)ILDasm

    打开vs,在工具 – > 外部工具
,把ILDasm导入到VS工具中,具体如下:

    图片 8

    位置:C:\Program Files (x86)\Microsoft
SDKs\Windows\v7.0A\bin\ildasm.exe,

    Microsoft
SDKs\Windows下的文件夹下有多个ildasm,包括的版本有.NET 4.0
,4.5.1,4.6,4.6.1,4.6.2等

    在VS的工具下这时就可以看到,打开ILDasm,然后打开想看的dll或exe就可以了,IL代码通过ILDasm反编译后,如下:

    图片 9

    符号解释:

图片 10

如果想查看IL,通过文件-转储保存下来就可以查看

    (2)ILSpy

     使用简单,注意:默认选择的是c#,改为IL即可,下载地址:

     (3)Reflector 

      这是也是很出名的,不介绍了,使用教材:

     (4)LINQPad

      它实际是个对linq方面的工具,但也可以看IL

      图片 11

 

    (5)Resharper

      打开方法:vs的Resharper菜单栏-window-IL
Viewer,随时都可以查看

      有条件的,建议使用,非常方便

  (二)实例解析

       待续

 

 

 

参考:

 

相关文章