一步一步教你在空闲空间中插入程序

在空闲空间中插入程序


        目标:将一个程序的代码段、数据段、导入表、重定位表等信息插入到另一个程序中,保证先运行插入的程序,再正常运行插入前的程序。

        两个程序都是使用汇编语言编写编译,程序体积较小,但程序较大时的原理与插入步骤一样。实验中涉及的两个软件如下:


插入程序打开时,先弹出一个对话框,再打开网址“http://www.baidu.com”,如下图所示:





目标程序打开时只弹出一个对话框,如下图所示:




我们要做的是将代码和数据插入到目标程序的空闲空间中,这样插入后。目标程序的大小不会改变。

1、  分析程序的结构大小

1)       用DIE等工具打开目标程序,得到各节的信息如下:



a)      代码节占用大小为26H,占用块大小为200H,即剩余1DAH的空间

b)      数据节占用大小为14H,占用块大小为200H,即剩余1ECH的空间

c)      导入表占用大小为92H,占用块大小为200H,即剩余16EH的空间

d)      重定位节占用大小为10H,占用块大小为200H,即剩余1F0H的空间


2)       打开插入程序得到各节信息如下:



       代码占用大小为36H,满足插入要求。导入表占用92H,满足插入要求。数据占用3DH满足插入要求,重定位信息占用14H满足插入要求。

2、  插入数据

       目标程序的数据存储在虚拟地址0x3000开始的地方,转换成文件地址就是0x800H,而数据占用14H字节,因此将数据插入到文件地址0x814开始的地方。

       同理插入程序的数据存储在文件地址0x800开始的地方,占用大小为3DH,因此需要把插入程序0x800 – 0x83C的数据插入到目标程序0x814开始的地址。插入数据的字节码如下:



CC E1 CA BE 21 00 D5 E2 CA C7 B2 E5 C8 EB B5 C4 B3 CC D0 F2 A3 AC BD AB D2 AA B4 F2 BF AA B0 D9 B6 C8 21 00 6F 70 65 6E 00 68 74 74 70 3A 2F 2F 77 77 77 2E 62 61 69 64 75 2E 63 6F 6D


       但是通过分析发现,目标程序与插入程序的提示框标题相同,即全部插入会导致数据重复,虽然不影响程序运行,但是不必要,即我们可以去掉前5个字节CC E1 CA BE 21 00(字符为“提示!”,最后的00表示字符串的结束),因此只需要把如下数据插入到目标程序文件偏移0x814开始的地方:


D5 E2 CA C7 B2 E5 C8 EB B5 C4 B3 CC D0 F2 A3 AC BD AB D2 AA B4 F2 BF AA B0 D9 B6 C8 21 00 6F 70 65 6E 00 68 74 74 70 3A 2F 2F 77 77 77 2E 62 61 69 64 75 2E 63 6F 6D



        红色表示新插入的值,插入的字符串最后一个字符串末尾应该也是00结束,由于空闲空间本身全是00,因此不用更改。

       更新之后,字符串“提示!”的文件地址为0x800,虚拟偏移为3000H,由于默认装载地址是400000,因此该字符串的虚拟地址是0x403000。

       同理字符串“这是插入的程序,将要打开百度!”的虚拟地址为0x403014。字符串“open”的虚拟地址为0x403032。字符串“http://www.baidu.com”的虚拟地址为0x403037


3、  插入导入表

       在数据目录表的第二个元素有指向导入表的虚拟偏移,第二项元素的文件偏移为148H,找到目标程序的文件偏移148H处,存储的虚拟偏移为2010H。偏移2010H转换为文件偏移为610H。通过分析目标程序有两个导入表,每个导入表都只调用了一个函数:






        分析发现在.idata这个节中,第一个部分存储的是导入地址表,第二部分存储的才是导入表结构体,由于第一个部分不能移动(代码中会引用这里的值,如果IAT较大,则需要较大幅度改动代码)。第三个部分为导入名字表、第四个部分为名字存储字符串(最好不要移动,较多时同样改动较大)。

       因此这里的一种做法是:将导入表结构体加上要插入的值后移动到最后面(预留一定空间给字符串插入),在原导入地址表最后面插入导入地址表,在原导入名称表前插入导入名称表,再修改相应字段的值。由于两者都导入了user32.dll因此这个导入表不需要插入。


1)       移动并插入导入表结构体

目标程序含有两个导入表结构体,且最后全00结构体表示结束,一个结构体占用20个字节,因此先复制文件偏移 610H – 637H之间的字节码:



54 20 00 00 00 00 00 00 00 00 00 00 6A 20 00 00 08 2000 00 4C 20 00 00 00 00 00 00 00 00 00 00 84 20 00 00 00 20 00 00

并在后面加入插入程序的导入表shell32.dll字节码,通过分析在文件偏移624H – 637H :



字节码为:4C 20 00 00 00 00 00 00 00 00 00 00 86 20 00 0000 20 00 00

         这里有几个字段需要改变,第一个是INT字段,由于导入名字表将被插在原导入名字表的前面,且只调用了一个函数,因此要插入的导入名称表在目标函数导入名称表地址的前8个字节处。

        根据目标程序的导入表结构体,得出其INT的起始字段在文件偏移64CH,因此插入后导入名字表的起始地址应该为644H,转成虚拟偏移为:2044H。

        第二个是Name RVA字段,由于字符串是插在了目标程序导入表字符串紧接着的最后面,根据目标程序导入表四个部分的排放结构,由于idata的虚拟大小为92H,因此新插入的字符串放在文件偏移692H开始的地方,由于前面存放的是函数名,后面存放的才是dll名,因此dll名存放在文件偏移6A2处,转换成虚拟地址就是20A2H。

        第三个是IAT字段,由于新的导入地址表插在目标程序导入地址表的最后面,且目标程序的导入地址表的文件偏移为600H,大小为16个字节,因此新的导入地址表在610H开始的地方,虚拟偏移为2010。

        因此更新后,需要插入的导入表字节码为:

44 20 00 00 00 00 00 00 00 00 00 00 A2 20 00 00 10 20 00 00

由于需要预留的空间大小为1CH字节,如下图所示:



因此合并后的导入表结构插入在文件偏移6AEH处,插入的合并数据为:

54 20 00 00 00 00 00 00 00 00 00 00 6A 20 00 00 08 20 00 00 4C 20 00 00 00 00 00 00 00 00 00 00 84 20 00 00 00 20 00 00 44 20 00 00 00 00 00 00 00 00 00 00 A2 20 00 00 10 20 00 00

插入结果如下:


        由于更改了导入地址表,因此需要更改原数据目录表第二项的值,虚拟偏移改为20AEH,大小改为50H。如下图所示:


2)       插入字符串

由前面的分析可知,插入的字符串字节码为:

D9 00 53 68 65 6C 6C 45 78 65 63 7574 65 41 00 73 68 65 6C 6C 33 32 2E 64 6C 6C 00

从文件偏移692H开始处插入字符串:



插入后shell32.dll的文件偏移为6A2H,虚拟偏移为20A2H


3)       插入导入地址表

        按照前面的分析,我们是在原导入表的最后面插入了新的导入地址表,由于在没有导入程序时,导入地址表中存放的实质上还是一个函数名的RVA,所以这里存放的是ShellExecuteA函数名字符串的RVA,而这里的结构实际上是IMAGE_IMPORT_BY_NAME,字符串前面有两个字节表示序号,因此开始时导入地址表存的就是这个结构的地址,即文件偏移692H,转换成虚拟偏移就是2092H。所以需要插入的字节为 92 20 00 00 00 00 00 00。

插入地址紧接着原来的导入地址表,文件偏移为610H:



4)       插入导入名字表

        由前面的分析可知,倒入名字表插在文件偏移644H开始的地方,开始时,导入名字表与导入地址表的内容一样,因此同样为:

       92 20 00 00 00 00 00 00,插入过程如下:


4、  插入代码

        从最开始的分析可知,代码所在节的文件偏移为400H,且代码的虚拟长度为26H,即插入程序的代码插入到文件地址426H开始的后面,要插入的代码如下:


00AC1000 > $  6A 00              push 0x0                                

00AC1002   .  68 0030AC00   push 插入程序.00AC3000                     

00AC1007   .  68 0630AC00   push 插入程序.00AC3006                      

00AC100C   .  6A 00               push 0x0                                

00AC100E   .  E8 17000000   call <jmp.&user32.MessageBoxA>         

00AC1013   .  6A 01               push 0x1                                

00AC1015   .  6A 00               push 0x0                                 

00AC1017   .  6A 00               push 0x0                               

00AC1019   .  68 2930AC00   push 插入程序.00AC3029                      

00AC101E   .  68 2430AC00   push 插入程序.00AC3024                      

00AC1023   .  6A 00                push 0x0                               

00AC1025   .  E8 06000000    call <jmp.&shell32.ShellExecuteA>      

00AC102A   $- FF25 0820AC00 jmp dword ptr ds:[<&user32.MessageBoxA>] ;

00AC1030   $- FF25 0020AC00 jmp dword ptr ds:[<&shell32.ShellExecute>;



上述代码中,红色的部分需该改动,因为插入后数据的地址发生了改变。

         地址0030AC00是函数MessageBox第三个参数的地址,这里的地址进行了重定位,这里偏移是3000,不用改动。

         地址0630AC00的偏移为3060,需要改成3014,即地址为0x00403014。

         地址2930AC00的偏移为3029,需要改成3037,即地址为0x00403037

         地址2430AC00的偏移为3024,需要改成3032,即地址为0x00403032

         地址0820AC00的偏移为2008,是函数MessageBox对应的导入表地址,在目标程序中,它的偏移仍然是2008,因此不用改动。

         地址0020AC00的偏移为2000,是函数ShellExecute对应的导入表地址,在目标程序中,它被插入到了偏移2010处,因此虚拟地址为:0x00402010

         还有两个call指令对应的地址也要改动,但由于指令后的四字节是偏移地址,因此我们先插入转移指令后,再计算偏移地址

       由于程序执行完后需要转到原来的入口点去执行,我们在文件偏移0XF0处查看到入口点是1000H,因此我们需要将入口点的值改成1026H:



        然后在call指令后加一个跳转指令转向原入口点:0x1000。这里的跳转指令使用E9,后面跟一个四字节的偏移,由于这条跳转指令的偏移地址是1050,入口地址是1000,因此偏移=1000-(1050+5)=FFFFFFAB(补码表示)。因此加入的指令字节吗为:E9ABFFFFFF。

       由于中间加入了五个字节,因此字节17000000改成1C000000,四字节06000000改成0B000000,即最后要插入的字节码为:


6A 00 68 00 30 40 00 68 14 30 40 00 6A 00 E8 1C 00 00 00 6A 01 6A 00 6A 00 68 37 30 40 00 68 32 30 40 00 6A 00 E8 0B 00 00 00 E9 AB FF FF FF FF 25 08 20 40 00 FF 25 10 20 40 00




5、  插入重定位表

       从插入的代码可以看到,上述标红的地址中,除了有两个不需要重定位,其余的都需要重定位,这些需要重定位的偏移分别是:

1029、102E、1040、1045、1057、105D

目标程序重定位节点信息如下:



        现在需要插入12字节的内容,用于表示上述需要重定位的地址信息,同时更改数据目录表中重定位表的大小和上图中文件偏移A04处的大小为1C。

        这里两字节的表示方法为前4位表示定位方式,在Windows中一般为3,后12位表示与当前页的偏移。

        因此上述偏移转换得到:3029、302E、3040、3045、3057、305D,转换为字节码为:29 30 2E 30 40 30 45 30 5730 5D 30。存储在文件偏移A10开始后的地方,如下:



修改文件偏移A04处的值为1C(表示当前重定位表块的大小):




修改数据目录表中重定位表(文件偏移16C)的大小为1C:



6、  完善字段值

          数据、代码、导入表、重定位表插入完成后,修改相应的表头节点字段,如修改目标数据段虚拟大小为3BH(14H+37H)等,但是不修改也不影响程序运行,因为程序运行时不会以头节点设置的大小为准。









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值