首页
社区
课程
招聘
[原创]给.net程序打内存补丁(2)(含操作录像)
发表于: 2006-8-25 20:10 17506

[原创]给.net程序打内存补丁(2)(含操作录像)

2006-8-25 20:10
17506

给.net程序打内存补丁(2)
tankaiha[NE365][FCG]
2006-8-25

    接上文。上次讲了个最简单的动态修改代码的方法,讲得比较简约。今天介绍个复杂点的代码修改,顺便多介绍一些基本概念。

一、修改目标
    先看今天修改的目标。这一次tmp.cs的代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace tmp
{
    //新增的类
    public class userClass1
    {
        public static void showMsg()
        {
            MessageBox.Show("You get it", "^_^");
        }
    };

    public partial class Form1 : Form
    {
        bool bRetVal;
        
        //定义一个变量,表示是否输入正确的字符串
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string cmpText = textBox1.Text;
            if(cmpText=="tankaiha")
            {
                this.bRetVal = true;
            }
            else
            {
                this.bRetVal = false;
            }            
            
        }

        //永远不会执行这个
        private void neverUsed()
        {
            MessageBox.Show("You never get this");
        }
    }
}
    我们在代码新增了一个userClass1,里面有一个静态方法showMsg()。我们要在button1_Click返回前执行这个方法。先对比一下这次的修

改和上次的不同

                上次       本次
代码块大小      不变       改变
代码块类型    小型(tiny)   大型(fat)
   
其实这次难度也不大,更难的在以后介绍。来看一下反汇编代码(部分):

……..
    IL_0000:  /* 02   |                  */ ldarg.0
    IL_0001:  /* 7B   | (04)000003       */ ldfld      class [System.Windows.Forms/*23000001*/]

System.Windows.Forms.TextBox/*01000006*/ tmp.Form1/*02000002*/::textBox1 /* 04000003 */
    IL_0006:  /* 6F   | (0A)000028       */ callvirt   instance string [System.Windows.Forms/*23000001*/]System.Windows.Forms.Control/*0100001B*/::get_Text() /* 0A000028 */
    IL_000b:  /* 0A   |                  */ stloc.0
    IL_000c:  /* 06   |                  */ ldloc.0
    IL_000d:  /* 72   | (70)00003B       */ ldstr      "tankaiha" /* 7000003B */
    IL_0012:  /* 28   | (0A)000029       */ call       bool [mscorlib/*23000002*/]System.String/*01000024*/::op_Equality(string,string) /* 0A000029 */
    IL_0017:  /* 2C   | 08               */ brfalse.s  IL_0021

    IL_0019:  /* 02   |                  */ ldarg.0
    IL_001a:  /* 17   |                  */ ldc.i4.1
    IL_001b:  /* 7D   | (04)000004       */ stfld      bool tmp.Form1/*02000002*/::bRetVal /* 04000004 */
    IL_0020:  /* 2A   |                  */ ret

    IL_0021:  /* 02   |                  */ ldarg.0
    IL_0022:  /* 16   |                  */ ldc.i4.0
    IL_0023:  /* 7D   | (04)000004       */ stfld      bool tmp.Form1/*02000002*/::bRetVal /* 04000004 */
    IL_0028:  /* 2A   |                  */ ret
  } // end of method Form1::button1_Click

我们要在最后IL_0028 ret之前插入一个call。

二、相关基本概念
    先来看看.net里的call。如果你用ildasm比较多,会比较熟悉。MSIL里的call是0x28,后面接了个(XX)YYYYYY,这是它的操作数,合起来XXYYYYYY就是你要call的方法的token。Token就是一个代码该方法的唯一值。不只是方法,.net中的任何东东都有个token。反汇编后发现userClass1.showMsg()的token是0x06000006。XX是分类(06),YYYYYY(000006)是序号。用工具打开tmp.exe可以看得更清楚些。


    从上图中看到,00是module,01是TypeRef,02是TypeDef,当然,还有06代表Method。而Method中,000001是Form1::Dispose(),000002是Form1::InitializeComponent,还有我们要调用的userClass1::showMsg(),排在第6位。这些信息都是存储在#~流中,还有其它的流,如#Strings、#US和#Blog等。这些流分别存储不同的信息,具体见MSDN,与本文关系不大。
    .Net中的token还有个方法,就是在一个Module中他是不变的,不管是否在一个类里,都可以直接用token值调用。因此,我们只需要在ret前插入28 06 00 00 06。

    第二个要介绍的概念是tiny和fat的区别。下图是方法在内存中的布局。


说白了,方法体就是一块内存。很明显,tiny比fat少了SEH处理块。一般来说,有SEH肯定是fat的,二是代码超过64字节也是fat。我们这次处理的就是不含SEH的fat Method。(下次再说对SEH块的处理)
    基本概念先介绍这两个,下面看代码。

三、修改
    下面开始修改代码,仍然在JITCompilationStarted中,首先定义我们要插入的代码:
#pragma pack(1)
        struct
        {
                BYTE insertcall;
                DWORD method_token;
        } InsertCode;
#pragma pack()
        InsertCode.insertcall=0x28;
        InsertCode.method_token=0x06000006;
   
    这样,我们的代码就比原代码大了5字节,所以在分配空间时要加上:

        IMethodMalloc* pIMethodMalloc = NULL;
        IMAGE_COR_ILMETHOD* pNewMethod = NULL;
        hr = m_pICorProfilerInfo->GetILFunctionBodyAllocator(moduleId, &pIMethodMalloc);
        if (FAILED(hr))
        { goto exit; }

        pNewMethod = (IMAGE_COR_ILMETHOD*) pIMethodMalloc->Alloc(iMethodSize+sizeof(InsertCode)+1);//注意新空间

的size要改

        if (pNewMethod == NULL)
        { goto exit; }
        memcpy((void*)pNewMethod, (void*)pMethod, iMethodSize);
下面是对fat方法头的处理和修改
if(IsTinyHeader(pNewMethod))
        {
        ……
}
        else
        {
                COR_ILMETHOD_FAT* newfatImage = (COR_ILMETHOD_FAT*)&pNewMethod->Fat;
                codeBytes = newfatImage->GetCode();
                ULONG codeSize = newfatImage->GetCodeSize()+sizeof(InsertCode);

                //这里更改,注意位置的选择
                memcpy(codeBytes+codeSize-sizeof(InsertCode)-1,&InsertCode,sizeof(InsertCode));
                codeBytes[codeSize-1]=0x2A;
                newfatImage->SetCodeSize(codeSize);
        }

    最后是将修改过的代码分配给新的方法,并释放空间。
         hr = m_pICorProfilerInfo->SetILFunctionBody(moduleId, tkMethod, (LPCBYTE) pNewMethod);
         if (FAILED(hr))
         { goto exit; }

         pIMethodMalloc->Release();

四、测试
    测试方法不变,不过这次给新手做了个动画。下面是前后结果对比,注意看红体字的codeSize前后对比和从第40个字节开始的更改。

1        17:59:04:078        516: tmp        funcitonId is a75930
2        17:59:04:078        516: tmp        JITCompilationStarted: ::tmp.Form1.button1_Click
3        17:59:04:078        516: tmp        target string is: tmp.Form1.button1_Click
4        17:59:04:078        516: tmp        enter fat code
5        17:59:04:078        516: tmp        Flags: 13
6        17:59:04:093        516: tmp        MaxStack: 2
7        17:59:04:093        516: tmp        CodeSize: 29
8        17:59:04:093        516: tmp        LocalVarSigTok: 11000001
……
44        19:44:59:062        3984: tmp        codeBytes[35] = 0x7D;
45        19:44:59:062        3984: tmp        codeBytes[36] = 0x04;
46        19:44:59:062        3984: tmp        codeBytes[37] = 0x00;
47        19:44:59:062        3984: tmp        codeBytes[38] = 0x00;
48        19:44:59:062        3984: tmp        codeBytes[39] = 0x04;
49        19:44:59:078        3984: tmp        codeBytes[40] = 0x2A;

50        17:59:04:250        516: tmp        enter fat code again
51        17:59:04:265        516: tmp        Flags: 13
52        17:59:04:265        516: tmp        MaxStack: 2
53        17:59:04:281        516: tmp        NewCodeSize: 2E
54        17:59:04:281        516: tmp        LocalVarSigTok: 11000001
……
90        19:44:59:343        3984: tmp        codeBytes[35] = 0x7D;
91        19:44:59:359        3984: tmp        codeBytes[36] = 0x04;
92        19:44:59:359        3984: tmp        codeBytes[37] = 0x00;
93        19:44:59:375        3984: tmp        codeBytes[38] = 0x00;
94        19:44:59:375        3984: tmp        codeBytes[39] = 0x04;
95        19:44:59:390        3984: tmp        codeBytes[40] = 0x28;
96        19:44:59:390        3984: tmp        codeBytes[41] = 0x06;
97        19:44:59:390        3984: tmp        codeBytes[42] = 0x00;
98        19:44:59:406        3984: tmp        codeBytes[43] = 0x00;
99        19:44:59:406        3984: tmp        codeBytes[44] = 0x06;
100        19:44:59:421        3984: tmp        codeBytes[45] = 0x2A;


    打完收功,下一次会介绍更复杂更有实战性的修改。附件里是测试文件和测试的动画,专为新手准备。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (27)
雪    币: 5275
活跃值: (456)
能力值: (RANK:1170 )
在线值:
发帖
回帖
粉丝
2
操作录像待上传
2006-8-25 20:19
0
雪    币: 5275
活跃值: (456)
能力值: (RANK:1170 )
在线值:
发帖
回帖
粉丝
3
操作录像在这
上传的附件:
2006-8-25 20:26
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
4
看不懂,我来顶。
2006-8-25 20:29
0
雪    币: 97697
活跃值: (200834)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
5
sustain.
2006-8-25 20:31
0
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
6
这是在海拔4000米写的文章,一定得收藏
2006-8-25 20:33
0
雪    币: 443
活跃值: (200)
能力值: ( LV9,RANK:1140 )
在线值:
发帖
回帖
粉丝
7
不能不顶的好帖子!

下了学习!
2006-8-25 20:46
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
看不懂,我来顶。
2006-8-25 21:16
0
雪    币: 338
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
超强!!膜拜!
2006-8-25 21:18
0
雪    币: 222
活跃值: (40)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
10
完全不懂说,只有顶的份了
2006-8-25 22:31
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
11
顶一下,太酷了~
2006-8-26 00:03
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
12
LZ写的一清二楚,可惜我还是糊涂,对net太不熟了
2006-8-26 00:53
0
雪    币: 89
活跃值: (171)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
不顶不行啊,,,学习中
2006-8-26 11:28
0
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
14
文章不错!!!
继续...
2006-8-26 21:03
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
最初由 kanxue 发布
这是在海拔4000米写的文章,一定得收藏

真的吗?
2006-8-27 16:55
0
雪    币: 1223
活跃值: (469)
能力值: (RANK:460 )
在线值:
发帖
回帖
粉丝
16
坐布达拉宫的屋檐上写的
2006-8-28 00:55
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
看不太懂,顶一下
2006-8-28 21:29
0
雪    币: 333
活跃值: (11)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
18
还有录像,要收藏
2006-8-28 22:29
0
雪    币: 175
活跃值: (2531)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
强,学习。
2006-8-30 21:29
0
雪    币: 158
活跃值: (43)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
20
下载学习.
2006-8-30 23:15
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
看不懂,我来学习一下。
2006-8-31 10:56
0
雪    币: 3149
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
又好又新的文章!
2006-9-6 14:30
0
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
23
要好好学习的,
2006-9-6 16:27
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
已经下载,并学习
2006-11-2 08:49
0
雪    币: 671
活跃值: (723)
能力值: ( LV9,RANK:1060 )
在线值:
发帖
回帖
粉丝
25
强!收下来慢慢看!
2006-11-2 09:50
0
游客
登录 | 注册 方可回帖
返回
//