IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> HUST网络攻防实践|5_二进制文件补丁技术|实验三 热补丁 -> 正文阅读

[系统运维]HUST网络攻防实践|5_二进制文件补丁技术|实验三 热补丁

实验要求

打开IDArepeat程序,可以看到先调用了puts,再调用sleep函数。所以实验要求就是将puts函数改成自己的。

要求最终做到,在不打断程序运行的同时,将输出的字符串替换成"My name is 姓名\n"。

实验过程

1. 64位Ubuntu下先安装32位库

我的实验环境是Ubuntu 20.04 x86_64,运行不了repeat程序,显示没有这个程序。

在群里问了一下同学,他们告诉我用readelf -h repeat查看一下文件的属性。

原来repeat是32位的程序,在64位的Ubuntu中运行需要提前安装32位的库。

添加方法如下,首先添加i32架构,然后更新镜像源,再安装相关库就可以了:

sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 -y
sudo apt install lib32z1 -y

2. 利用Preload Hook实现热补丁修补

我不清楚怎么在不使用stdio.h的情况下,输出字符串(因为putsprintf都在stdio.h里)。而且内联汇编也比较麻烦,所以我直接编写了AT&T的32位汇编程序,作为热补丁。

汇编程序patch.s如下,参考AT&T汇编语言初步_farthjun的博客-CSDN博客

.section .data
	output:
		.ascii "My name is shandianchengzi.\n"

.section .text
.globl puts
puts:
	movl $4, %eax
	movl $1, %ebx
	movl $output,%ecx
	movl $26,%edx
	int  $0x80

意思是先声明一个output字符串,内容是"My name is shandianchengzi.\n",长度是26。然后重写puts函数,其中使用第4号系统调用write,构造write(1,output,26),从而输出字符串。

首先将其编译成静态链接库.o文件:

as --32 -o patch.o patch.s

然后用gcc编译成.so文件:

gcc -shared -m32 -o patch.so patch.o

利用Preload Hook直接传入环境变量,来实现热补丁修补:

LD_PRELOAD=./patch.so ./repeat

运行结果如下图所示:

在这里插入图片描述

可以看到,补丁是生效的,原来的puts函数被替换了。

3. 利用系统调用ptrace对运行状态的程序进行hook

参考实验指导书和博客:linux下实现在程序运行时的函数替换(热补丁) - __sipl - 博客园 (cnblogs.com)

因为ptrace允许做两个不同名字的函数的替换,因此完全可以再写一个更方便的patch.c程序,也不用顾忌头文件了。

3.1 编写补丁程序

补丁程序patch.c如下:

#include<stdio.h>

int newputs()
{
  printf("My name is ShenShandian.\n");
  return 0;
}

补丁文件编译:

gcc -m32 -fPIC --shared patch.c -o patch.so

编译报错了fatal error: bits/libc-header-start.h: No such file or directory,百度后发现还是32位系统的问题,再安装一个依赖,就可以编译了:

sudo apt-get install gcc-multilib

再次运行补丁编译指令完成编译。

3.2 编写hook程序

hook程序ptrace_patch.c如下:

#include <stdio.h>
#include <string.h>
#include <elf.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/user.h>
#include <link.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bits/dlfcn.h>

#define IMAGE_ADDR 0x08048000

int mode = 2;

struct user_regs_struct oldregs;
Elf32_Addr phdr_addr;
Elf32_Addr dyn_addr;
Elf32_Addr map_addr;
Elf32_Addr symtab;
Elf32_Addr strtab;
Elf32_Addr jmprel;
Elf32_Addr reldyn;
Elf32_Word reldynsz;
Elf32_Word totalrelsize;
Elf32_Word relsize;
unsigned long link_addr;
int nrels;
int nreldyns;
int modifyflag = 0;

// 读寄存器
void ptrace_readreg(int pid, struct user_regs_struct *regs)
{
    if(ptrace(PTRACE_GETREGS, pid, NULL, regs))
        printf("*** ptrace_readreg error ***\n");
}

// 写寄存器
void ptrace_writereg(int pid, struct user_regs_struct *regs)
{
    if(ptrace(PTRACE_SETREGS, pid, NULL, regs))
        printf("*** ptrace_writereg error ***\n");
}

// 关联到进程
void ptrace_attach(int pid)
{
    if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
        perror("ptrace_attach");
        exit(-1);
    }
    waitpid(pid, NULL, 0);  
    ptrace_readreg(pid, &oldregs);
}

// 让进程继续
void ptrace_cont(int pid)
{
    int stat;
    if(ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
        perror("ptrace_cont");
        exit(-1);
    }
}

// 脱离进程
void ptrace_detach(int pid)
{
    ptrace_writereg(pid, &oldregs);
    if(ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
        perror("ptrace_detach");
        exit(-1);
    }
}

// 写指定进程地址
void ptrace_write(int pid, unsigned long addr, void *vptr, int len)
{
    int count;
    long word;
    count = 0;
    while(count < len) {
        memcpy(&word, vptr + count, sizeof(word));
        word = ptrace(PTRACE_POKETEXT, pid, addr + count, word);
        count += 4;
        if(errno != 0)
            printf("ptrace_write failed\t %ld\n", addr + count);
    }
}

// 读指定进程
int ptrace_read(int pid, unsigned long addr, void *vptr, int len)
{
    int i,count;
    long word;
    unsigned long *ptr = (unsigned long *)vptr;
    i = count = 0;
    while (count < len) {
        word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
        while(word < 0)
        {
            if(errno == 0)
                break;
            perror("ptrace_read failed");
            return 2;
        }
        count += 4;
        ptr[i++] = word;
    }
    return 0;
}

// 在进程指定地址读一个字符串
char * ptrace_readstr(int pid, unsigned long addr)
{
    char *str = (char *) malloc(64);
    int i,count;
    long word;
    char *pa;
    i = count = 0;
    pa = (char *)&word;
    while(i <= 60) {
        word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
        count += 4;
        if (pa[0] == 0) {
            str[i] = 0;
        break;
        }
        else
            str[i++] = pa[0];
        if (pa[1] == 0) {
            str[i] = 0;
            break;
        }
        else
            str[i++] = pa[1];
        if (pa[2] ==0) {
            str[i] = 0;
            break;
        }
        else
            str[i++] = pa[2];
        if (pa[3] ==0) {
            str[i] = 0;
            break;
        }
        else
            str[i++] = pa[3];
    }
    return str;
}

// 将指定数据压入进程堆栈并返回堆栈指针
void * ptrace_push(int pid, void *paddr, int size)
{
    unsigned long esp;
    struct user_regs_struct regs;
    ptrace_readreg(pid, &regs);
    esp = regs.esp;
    esp -= size;
    esp = esp - esp % 4;
    regs.esp = esp;
    ptrace_writereg(pid, &regs);
    ptrace_write(pid, esp, paddr, size);
    return (void *)esp;
}

// 在进程内调用指定地址的函数
void ptrace_call(int pid, unsigned long addr)
{
    void *pc;
    struct user_regs_struct regs;
    int stat;
    void *pra;
    pc = (void *) 0x41414140;
    pra = ptrace_push(pid, &pc, sizeof(pc));
    ptrace_readreg(pid, &regs);
    regs.eip = addr;
    ptrace_writereg(pid, &regs);
    ptrace_cont(pid);
}

// 得到指向link_map链表首项的指针
struct link_map * get_linkmap(int pid)
{
    Elf32_Ehdr *ehdr = (Elf32_Ehdr *) malloc(sizeof(Elf32_Ehdr));      
    Elf32_Phdr *phdr = (Elf32_Phdr *) malloc(sizeof(Elf32_Phdr));
    Elf32_Dyn  *dyn =  (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
    Elf32_Word got;
    struct link_map *map = (struct link_map *)malloc(sizeof(struct link_map));
    int i = 1;
    unsigned long tmpaddr;
    ptrace_read(pid, IMAGE_ADDR, ehdr, sizeof(Elf32_Ehdr));
    phdr_addr = IMAGE_ADDR + ehdr->e_phoff;
    printf("phdr_addr\t %p\n", phdr_addr);
    ptrace_read(pid, phdr_addr, phdr, sizeof(Elf32_Phdr));
    while(phdr->p_type != PT_DYNAMIC)
        ptrace_read(pid, phdr_addr += sizeof(Elf32_Phdr), phdr,sizeof(Elf32_Phdr));
    dyn_addr = phdr->p_vaddr;
    printf("dyn_addr\t %p\n", dyn_addr);
    ptrace_read(pid, dyn_addr, dyn, sizeof(Elf32_Dyn));
    while(dyn->d_tag != DT_PLTGOT) {
        tmpaddr = dyn_addr + i * sizeof(Elf32_Dyn);
        ptrace_read(pid,tmpaddr, dyn, sizeof(Elf32_Dyn));
        i++;
    }
    got = (Elf32_Word)dyn->d_un.d_ptr;
    got += 4;
    ptrace_read(pid, got, &map_addr, 4);
    printf("map_addr\t %p\n", map_addr);
    map = map_addr;
    free(ehdr);
    free(phdr);
    free(dyn);
    return map;
}

// 取得给定link_map指向的SYMTAB、STRTAB、HASH、JMPREL、PLTRELSZ、RELAENT、RELENT信息
// 这些地址信息将被保存到全局变量中,以方便使用
void get_sym_info(int pid, struct link_map *lm)
{
    Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
    unsigned long dyn_addr;
    ptrace_read(pid,&(lm->l_ld) , &dyn_addr, sizeof(dyn_addr));
    ptrace_read(pid,&(lm->l_addr) , &link_addr, sizeof(dyn_addr));
    ptrace_read(pid, dyn_addr, dyn, sizeof(Elf32_Dyn));
    while(dyn->d_tag != DT_NULL){
        switch(dyn->d_tag)
        {
        case DT_SYMTAB:
            symtab = dyn->d_un.d_ptr;
            break;
        case DT_STRTAB:
            strtab = dyn->d_un.d_ptr;
            break;
        case DT_JMPREL:
            jmprel = dyn->d_un.d_ptr;
            break;
        case DT_PLTRELSZ:
            totalrelsize = dyn->d_un.d_val;
            break;
        case DT_RELAENT:
            relsize = dyn->d_un.d_val;
            break;
        case DT_RELENT:
            relsize = dyn->d_un.d_val;
            break;
        case DT_REL:
            reldyn = dyn->d_un.d_ptr;       
            break;
        case DT_RELSZ:
            reldynsz = dyn->d_un.d_val;
            break;
        }
        ptrace_read(pid, dyn_addr += sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
    }
    nrels = totalrelsize / relsize;
    nreldyns = reldynsz/relsize;
    free(dyn);
    printf("get_sym_info exit\n");
}

// 在指定的link_map指向的符号表查找符号,它仅仅是被上面的find_symbol使用
unsigned long  find_symbol_in_linkmap(int pid, struct link_map *lm, char *sym_name)
{
    Elf32_Sym *sym = (Elf32_Sym *) malloc(sizeof(Elf32_Sym));
    int i = 0;
    char *str;
    unsigned long ret;
    int flags = 0;
    get_sym_info(pid, lm);
    do{
        if(ptrace_read(pid, symtab + i * sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym)))
            return 0;
        i++;
        if (!sym->st_name && !sym->st_size && !sym->st_value)
            continue;
        str = (char *) ptrace_readstr(pid, strtab + sym->st_name);
        if (strcmp(str, sym_name) == 0) {
            printf("\nfind_symbol_in_linkmap str = %s\n",str);
            printf("\nfind_symbol_in_linkmap sym->st_value = %x\n",sym->st_value);
            free(str);
            if(sym->st_value == 0)
                continue;
            flags = 1;
            break;
        }
        free(str);
    }while(1);
    if (flags != 1)
        ret = 0;
    else
        ret =  link_addr + sym->st_value;
    free(sym);
    return ret;
}

// 解析指定符号
unsigned long  find_symbol(int pid, struct link_map *map, char *sym_name)
{
    struct link_map *lm = map;
    unsigned long sym_addr;
    char *str;
    unsigned long tmp;
    sym_addr = find_symbol_in_linkmap(pid, lm, sym_name);
    while(!sym_addr ) {
        ptrace_read(pid, (char *)lm+12, &tmp, 4);
        if(tmp == 0)
            return 0;
        lm = tmp;
        if ((sym_addr = find_symbol_in_linkmap(pid, lm, sym_name)))
            break;
    }
    return sym_addr;
}

// 查找符号的重定位地址
unsigned long  find_sym_in_rel(int pid, char *sym_name)
{
    Elf32_Rel *rel = (Elf32_Rel *) malloc(sizeof(Elf32_Rel));
    Elf32_Sym *sym = (Elf32_Sym *) malloc(sizeof(Elf32_Sym));
    int i;
    char *str;
    unsigned long ret;
    struct link_map *lm;
    lm = map_addr;
    do{
        get_sym_info(pid,lm);
        ptrace_read(pid, (char *)lm+12, &lm, 4);
        for(i = 0; i< nrels ;i++) {
            ptrace_read(pid, (unsigned long)(jmprel + i * sizeof(Elf32_Rel)),
                                                                     rel, sizeof(Elf32_Rel));
            if(ELF32_R_SYM(rel->r_info)) {
                ptrace_read(pid, symtab + ELF32_R_SYM(rel->r_info) *
                                                   sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym));
                str = ptrace_readstr(pid, strtab + sym->st_name);
                if (strcmp(str, sym_name) == 0) {
                    if(sym->st_value != 0){
                        free(str);
                        continue;
                    }
                    modifyflag = 1;
                    free(str);
                    break;
                }
                free(str);
            }
        }
        if(modifyflag == 1)
            break;
        for(i = 0; i< nreldyns;i++) {
            ptrace_read(pid, (unsigned long)(reldyn+ i * sizeof(Elf32_Rel)),
                                                                     rel, sizeof(Elf32_Rel));
            if(ELF32_R_SYM(rel->r_info)) {
                ptrace_read(pid, symtab + ELF32_R_SYM(rel->r_info) *
                                                   sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym));
                str = ptrace_readstr(pid, strtab + sym->st_name);
                if (strcmp(str, sym_name) == 0) {
                    if(sym->st_value != 0){
                        free(str);
                        continue;
                    }
                    modifyflag = 2;
                    free(str);
                    break;
                }
                free(str);
            }
        }
        if(modifyflag == 2)
            break;
    }while(lm);
    if (modifyflag == 0)
        ret = 0;
    else
        ret =  link_addr + rel->r_offset;
    free(rel);
    free(sym);
    return ret;
}

void call__libc_dlopen_mode(int pid, unsigned long addr, char *libname)
{
    void *plibnameaddr;
    plibnameaddr = ptrace_push(pid, libname, strlen(libname) + 1);
    ptrace_push(pid,&mode,sizeof(int));
    ptrace_push(pid,&plibnameaddr,sizeof(plibnameaddr));
    ptrace_call(pid, addr);
}

void call_puts(int pid, unsigned long addr, char *string)
{
    void *paddr;
    paddr = ptrace_push(pid, string, strlen(string) + 1);
    ptrace_push(pid,&paddr,sizeof(paddr));
    ptrace_call(pid, addr);
}

int main(int argc, char *argv[])
{
    int pid;
    struct link_map *map;
    char sym_name[256];
    unsigned long sym_addr;
    unsigned long new_addr,old_addr,rel_addr;
    int status = 0;
    char libpath[1024];
    char oldfunname[128];
    char newfunname[128];
    if(argc < 5){
        printf("usage : ./ptrace_patch pid libpath oldfunname newfunname\n");
        exit(-1);
    }
    
    pid = atoi(argv[1]);
    memset(libpath,0,sizeof(libpath));
    memcpy(libpath,argv[2],strlen(argv[2]));
    memset(oldfunname,0,sizeof(oldfunname));
    memcpy(oldfunname,argv[3],strlen(argv[3]));
    memset(newfunname,0,sizeof(newfunname));
    memcpy(newfunname,argv[4],strlen(argv[4]));
    printf("main pid = %d\n",pid);
    printf("main libpath : %s\n",libpath);
    printf("main oldfunname : %s\n",oldfunname);
    printf("main newfunname : %s\n",newfunname);
    
    ptrace_attach(pid);
    
    map = get_linkmap(pid);                    
    
    sym_addr = find_symbol(pid, map, "puts");      
    printf("found puts at addr %p\n", sym_addr); 
    if(sym_addr == 0)
        goto detach;
    call_puts(pid,sym_addr,"patch successed\n");
    waitpid(pid,&status,0);
    printf("status = %x\n",status);
    
    sym_addr = find_symbol(pid, map, "__libc_dlopen_mode");        
    printf("found __libc_dlopen_mode at addr %p\n", sym_addr); 
    if(sym_addr == 0)
        goto detach;
    call__libc_dlopen_mode(pid, sym_addr,libpath);      
    waitpid(pid,&status,0);
    
    strcpy(sym_name, newfunname);                
    sym_addr = find_symbol(pid, map, sym_name);
    printf("%s addr\t %p\n", sym_name, sym_addr);
    if(sym_addr == 0)
        goto detach;
    strcpy(sym_name, oldfunname);              
    rel_addr = find_sym_in_rel(pid, sym_name);
    printf("%s rel addr\t %p\n", sym_name, rel_addr);
    if(rel_addr == 0)
        goto detach;
        
    puts("intercept...");                    
    if(modifyflag == 2)
        sym_addr = sym_addr - rel_addr - 4;
    printf("main modify sym_addr = %x\n",sym_addr);
    ptrace_write(pid, rel_addr, &sym_addr, sizeof(sym_addr));
    
    puts("patch ok");
detach:
    printf("prepare to detach\n");
    ptrace_detach(pid);
    return 0;
}

上述代码基本上是实验指导书给出的代码,还有博客的代码。不过本作业需要Hook的函数是puts,不是printf,所以尝试通过查找putsymbol、调用call_puts来实现patch successed的输出。

编译ptrace_patch.c

gcc -m32 ptrace_patch.c -o ptrace_patch

3.3 热补丁的结果

完成编译后,先在一个终端运行repeat

./repeat

再另开一个终端,先ps -a查看repeat对应的pid,再运行:

sudo ./ptrace_patch <pid> ./patch.so puts newputs

结果如下图所示:

在这里插入图片描述

可以看到,上图左边终端运行着repeat程序,中途被修改成我的输出;右边终端运行着打热补丁的程序。

显然,热补丁打成功了,不过有一个小缺陷,call_puts没有调用成功,在ptrace_read的时候返回了错误。

我查找了原因,应该还是因为64位系统问题:

当您使用 64 位操作系统时,您必须使用兼容 64 位的寄存器。即,RAX,ORIG_EAX,EBX,EBX …如果不是,您将获得垃圾值。

——ptrace 寄存器中返回的异常值(ptrace abnormal values returned in the registers)答案 - 爱码网 (likecs.com)

我认为不需要修改这个问题,因为热补丁已经打入成功,满足实验要求。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 19:40:14  更:2022-08-19 19:40:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 18:35:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码