X86应用程序热补丁(一)

事例

// mymain.c

#include <stdio.h>
#include <time.h>
#include <unistd.h>

void mytest2()
{
    printf("[%s:%05d] %s enter!\n", __FILE__, __LINE__, __FUNCTION__);
}

void mytest()
{
    printf("[%s:%05d] %s enter!\n", __FILE__, __LINE__, __FUNCTION__);
}

int main(int argc, const char *argv[])
{
    while (1) {
        sleep(1);

        mytest();
    }

    return 0;
}

编译

gcc -m32 -g mymain.c

背景

  • 在上述事例中,main调用mytest函数,mytest和mytest2为可执行文件中的函数
  • 利用mytest2替换main函数中mytest的调用

原理

  • mytest和mytest2均属于可执行文件中的函数,则一旦编译器连接成功后,它们之间的相对位置是固定的
  • 利用相对位置固定的原理,则它们的调用原理是通过相对地址进行调用的,即:main调用mytest函数,则从main函数跳转到mytest函数,CPU利用它们之间的相对位置进行寻址

main调用mytest原理解析

Dump of assembler code for function main:
   0x0804847f <+0>:	    8d 4c 24 04	    lea    0x4(%esp),%ecx
   0x08048483 <+4>:	    83 e4 f0	    and    $0xfffffff0,%esp
   0x08048486 <+7>:	    ff 71 fc	    push   -0x4(%ecx)
   0x08048489 <+10>:	55	            push   %ebp
   0x0804848a <+11>:	89 e5	        mov    %esp,%ebp
   0x0804848c <+13>:	51	            push   %ecx
   0x0804848d <+14>:	83 ec 04	    sub    $0x4,%esp
   0x08048490 <+17>:	83 ec 0c	    sub    $0xc,%esp
   0x08048493 <+20>:	6a 01	        push   $0x1
   0x08048495 <+22>:	e8 76 fe ff ff	call   0x8048310 <sleep@plt>
   0x0804849a <+27>:	83 c4 10	    add    $0x10,%esp
   0x0804849d <+30>:	e8 bb ff ff ff	call   0x804845d <mytest>
   0x080484a2 <+35>:	eb ec	        jmp    0x8048490 <main+17>
End of assembler dump.
  • 从上图可知调用mytest对应的内存地址为:0x0804849d,而该地址处对应的e8为CPU指令码,后面0xffffffbb(此处为小端字节序)为操作数。
  • 该操作数为call指令的下一条指令地址0x080484a2相对于mytest函数地址的相对偏移量(该偏移量用补码表示)
  • 0xffffffbb的由来:对0x080484a2 - 0x804845d的值求补码即可得到操作数(偏移量0xffffffbb)
  • 因此,想要使用mytest2替换mytest函数,则只修改其操作数就可达到要求。

操作过程

(gdb) print mytest2
$2 = {void ()} 0x804843b <mytest2>

(gdb) print /x 0x080484a2 - 0x804843b //获得相对位置
$3 = 0x67

(gdb) print /x ~0x67 + 1 //获得补码
$6 = 0xffffff99

(gdb) set *(0x0804849d + 1) = 0xffffff99
(gdb) continue

Dump of assembler code for function main:
   0x0804847f <+0>:	    8d 4c 24 04	    lea    0x4(%esp),%ecx
   0x08048483 <+4>:	    83 e4 f0	    and    $0xfffffff0,%esp
   0x08048486 <+7>:	    ff 71 fc	    push   -0x4(%ecx)
   0x08048489 <+10>:	55	            push   %ebp
   0x0804848a <+11>:	89 e5	        mov    %esp,%ebp
   0x0804848c <+13>:	51	            push   %ecx
   0x0804848d <+14>:	83 ec 04	    sub    $0x4,%esp
   0x08048490 <+17>:	83 ec 0c	    sub    $0xc,%esp
   0x08048493 <+20>:	6a 01	        push   $0x1
   0x08048495 <+22>:	e8 76 fe ff ff	call   0x8048310 <sleep@plt>
   0x0804849a <+27>:	83 c4 10	    add    $0x10,%esp
   0x0804849d <+30>:	e8 99 ff ff ff	call   0x804843b <mytest2>
   0x080484a2 <+35>:	eb ec	        jmp    0x8048490 <main+17>
End of assembler dump.
  • 从上图可知,已经将0x0804849d地址出对mytest的调用修改为mytest2的调用

结果展示

修改前

[mymain.c:00012] mytest enter!
[mymain.c:00012] mytest enter!
[mymain.c:00012] mytest enter!
[mymain.c:00012] mytest enter!
[mymain.c:00012] mytest enter!

修改后

[mymain.c:00007] mytest2 enter!
[mymain.c:00007] mytest2 enter!
[mymain.c:00007] mytest2 enter!
[mymain.c:00007] mytest2 enter!
[mymain.c:00007] mytest2 enter!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值