吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2747|回复: 3
收起左侧

[其他转载] Linux下使用ptrace注入教程

[复制链接]
TongShuai 发表于 2021-1-21 16:58

注入技术在外挂、热修复技术中非常常见,由于Android使用Linux内核,所以先实现Linux下ptrace注入,后面再逐步改为Android版本

被注入的程序

#include <stdio.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <unistd.h>

int main(){
    printf("HelloWorld\n");
    //mmap(0,0x1000,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANONYMOUS|MAP_PRIVATE,0,0);
    printf("pid:%d\n",getpid());
    printf("mmap:%p\ndlopen%p\ndlclose%p\ndlsym%p\ndlerror%p\n",mmap,dlopen,dlclose,dlsym,dlerror);
    int a;
    while(1){
        scanf("%d",&a);
        printf("%d\n",a);
        // sleep(1);
    }
    return 0;
}

这里打印各个函数的地址,便于出问题的时候debug

要注入的so文件

#include <stdio.h>

void testEntry(){
    FILE *f = fopen("/tmp/test.txt","w");
    fputs("Ok to load",f);
    fclose(f);

}

使用下面的命令编译为so文件

gcc -o libtest.so test.c -fPIC -shared

注入器

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <memory.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <dlfcn.h>
#include <sys/ptrace.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>
#define MAX_PATH 0x512
char libcPath[]="/usr/lib/libc-2.32.so";

void ptraceWriteData(pid_t pid,void*addr,const char*data,size_t len){
    size_t i=0;
    for(;i<len;i+=sizeof(long)){
        ptrace(PTRACE_POKETEXT,pid,addr+i,(void*)*(long*)&data[i]);
    }
    // 每次写sizeof(long)字节,直接对齐
}
void ptraceReadData(pid_t pid,void*addr,char*data,size_t len){
    size_t i=0;
    long rdata;
    for(;i<len;i+=sizeof(long)){
        rdata=ptrace(PTRACE_PEEKTEXT,pid,addr+i,NULL);
        *(long*)&data[i]=rdata;
    }
}
void ptraceAttach(pid_t pid){
    if(ptrace(PTRACE_ATTACH,pid,NULL,NULL)==-1){
        printf("[INJECT F]Failed to attach:%d\n",pid);
    }
    int stat=0;
    waitpid(pid,&stat,WUNTRACED);
}
void ptraceGetRegs(pid_t pid,struct user_regs_struct*regs_addr){
    if(ptrace(PTRACE_GETREGS,pid,NULL,(void*)regs_addr)==-1){
        printf("Get regs error\n");
    }
}
void ptraceSetRegs(pid_t pid,struct user_regs_struct*regs_addr){
    if(ptrace(PTRACE_SETREGS,pid,NULL,(void*)regs_addr)==-1){
        printf("Set regs error\n");
    }
}
void ptraceDetach(pid_t pid){
    ptrace(PTRACE_DETACH,pid,NULL,NULL);
}
void ptraceContinue(pid_t pid){
    if(ptrace(PTRACE_CONT,pid,NULL,NULL)==-1){
        printf("ptrace continue error\n");
    }
}
void *getModuleBaseAddr(pid_t pid,const char*moduleName){
    if(pid==-1)pid=getpid();
    // 通过解析/proc/pid/maps 获得基址
    char filepath[MAX_PATH];
    void*moduleBaseAddr=NULL;
    snprintf(filepath,MAX_PATH,"/proc/%d/maps",pid);
    FILE *f = fopen(filepath,"r");
    char line[MAX_PATH];
    char base[MAX_PATH],name[MAX_PATH];
    size_t cnt,start;
    while(!feof(f)){
        memset(line,0,MAX_PATH);
        memset(name,0,MAX_PATH);
        fgets(line,MAX_PATH,f);
        cnt=0;
        while(line[cnt]!='/')cnt++;
        start=cnt;
        while(line[cnt]){
            name[cnt-start]=line[cnt];
            cnt++;
        }
        name[cnt-start-1]=0;

        if(strncmp(name,moduleName,MAX_PATH))continue;
        memset(base,0,MAX_PATH);
        cnt=0;
        while(line[cnt]!='-'){
            base[cnt]=line[cnt];
            cnt++;
        }
        base[cnt]=0;
        sscanf(base,"%llx",(long long*)(&moduleBaseAddr));
        printf("[INJECT] GotBaseAddr %p of %s\n",moduleBaseAddr,moduleName);
        break;
    }
    fclose(f);
    return moduleBaseAddr;
}
void *getRemoteFuncAddr(pid_t pid,const char *moduleName,void *localFuncAddr){
    void *localModuleAddr,*remoteModuleAddr,*remoteFuncAddr;
    // 通过计算指定函数的偏移量获取目标函数地址
    localModuleAddr = getModuleBaseAddr(-1,moduleName);
    remoteModuleAddr = getModuleBaseAddr(pid,moduleName);
    remoteFuncAddr=localFuncAddr-localModuleAddr+remoteModuleAddr;
    printf("[INJECT] GotFuncAddr:%p\n",remoteFuncAddr);
    return remoteFuncAddr;
}
int ptraceCall(pid_t pid,void* funcAddr,long*paras,long paraLen,struct user_regs_struct *regs){

    // 多于6个参数通过栈传递
    regs->rsp-=sizeof(void*);
    size_t errRet=0;
    // 返回地址弄成0导致错误停止
    ptraceWriteData(pid,(void*)regs->rsp,(char*)&errRet,sizeof(void*));
    if(paraLen>6){
        regs->rsp-=(paraLen-6)*sizeof(long);
        ptraceWriteData(pid,(void*)regs->rsp,(char*)¶s[6],sizeof(long)*(paraLen-6));
    }
    // 前6个参数通过寄存器传递
    switch(paraLen){
        case 6:
            regs->r9=paras[5];
        case 5:
            regs->r8=paras[4];
        case 4:
            regs->rcx=paras[3];
        case 3:
            regs->rdx=paras[2];
        case 2:
            regs->rsi=paras[1];
        case 1:
            regs->rdi=paras[0];
            break;

    }
    // 调用函数
    regs->rip=(unsigned long long)funcAddr;
    ptraceSetRegs(pid,regs);
    int stat=0;
    while(stat!=0xb7f){
        ptraceContinue(pid);
        waitpid(pid,&stat,WUNTRACED);
        printf("[INJECT] substatus: %x\n",stat);
    }

    ptraceGetRegs(pid,regs);
    return 0;
}
void inject(pid_t pid,const char*libname,const char*funcName){
    struct user_regs_struct oldRegs;
    struct user_regs_struct regs;
    long paras[6];
    char realLibPath[MAX_PATH];
    realpath(libname,realLibPath);
    printf("[INJECT]Real path of lib found:%s\n",realLibPath);

    ptraceAttach(pid);
    // 保存寄存器环境
    ptraceGetRegs(pid,&oldRegs);
    memcpy(®s,&oldRegs,sizeof(struct user_regs_struct));
    // 获取mmap地址
    void *mmapAddr = getRemoteFuncAddr(pid,libcPath,mmap);
    // 调用mmap
    paras[0]=0;
    paras[1]=0x1000;
    paras[2]=PROT_READ|PROT_WRITE|PROT_EXEC;
    paras[3]=MAP_ANONYMOUS|MAP_PRIVATE;
    paras[4]=0;
    paras[5]=0;
    ptraceCall(pid,mmapAddr,paras,6,®s);
    void *remoteMemAddr=(void*)regs.rax;
    printf("[INJECT] remote mmaped addr:%p\n",remoteMemAddr);
    // 调用dlopen
    void *dlopenAddr=getRemoteFuncAddr(pid,libcPath,dlopen);
    void *dlcloseAddr=getRemoteFuncAddr(pid,libcPath,dlclose);
    void *dlErrorAddr=getRemoteFuncAddr(pid,libcPath,dlerror);
    ptraceWriteData(pid,remoteMemAddr,realLibPath,strlen(realLibPath)+1);   // 作为dlopen的参数
    //debug start
    char buf[MAX_PATH];
    memset(buf,0,MAX_PATH);
    ptraceReadData(pid,remoteMemAddr,buf,strlen(realLibPath)+3);
    printf("%s\n",buf);
    //debug end
    paras[0]=(long)remoteMemAddr;
    paras[1]=RTLD_NOW|RTLD_GLOBAL;
    ptraceCall(pid,dlopenAddr,paras,2,®s);
    void*libBaseAddr=(void*)regs.rax;
    printf("[INJECT]remote lib base:0x%llx of %s\n",(long long)libBaseAddr,realLibPath);
    // 调用dlsym
    void*dlsymAddr=getRemoteFuncAddr(pid,libcPath,dlsym);
    ptraceWriteData(pid,remoteMemAddr,funcName,strlen(funcName)+1);
    //debug start
    memset(buf,0,MAX_PATH);
    ptraceReadData(pid,remoteMemAddr,buf,strlen(funcName)+3);
    printf("%s\n",buf);
    //debug end
    paras[0]=(long)libBaseAddr;
    paras[1]=(long)remoteMemAddr;
    ptraceCall(pid,dlsymAddr,paras,2,®s);
    void*remoteFuncAddr = (void*)regs.rax;
    printf("[INJECT]addr:0x%llx of func %s\n",(long long)remoteFuncAddr,funcName);
    // 调用目标函数
    ptraceCall(pid,remoteFuncAddr,paras,0,®s);
    // 恢复寄存器环境
    ptraceSetRegs(pid,&oldRegs);
    ptraceDetach(pid);
}
pid_t findPIdByName(const char *name){
    pid_t pid = -1;
    int pDirId = 0; // process dir id
    FILE *f;
    char filename[MAX_PATH]; 
    char cmdline[MAX_PATH];
    struct dirent *entry=NULL;
    int cnt;
    if(name==NULL){
        return -1;
    }
    DIR *dir = opendir("/proc");
    if(dir==NULL){
        return -1;
    }
    while((entry=readdir(dir))!=NULL){
        pDirId=atoi(entry->d_name); 
        if(pDirId!=0){
            snprintf(filename,MAX_PATH,"/proc/%d/cmdline",pDirId);
            f = fopen(filename,"r");
            if(f){
                fgets(cmdline,sizeof(cmdline),f);
                cnt = (int)strlen(cmdline);
                while(cnt>=0&&cmdline[cnt]!='/')cnt--; // cmdline是完整路径,这里只找最后一个/之后的,就是可执行文件的文件名
                cnt++;
                if(!strncmp(name,&cmdline[cnt],strlen(name))){
                    pid=pDirId;
                    break;
                }
            }
            fclose(f);
        }
    }
    closedir(dir);
    return pid; 
}

int main(int argc,char **argv){
    if(argc!=4){
        printf("usage: inject processname libexample.so funcname\n");
        return 0;
    }
    pid_t pid = findPIdByName(argv[1]);
    inject(pid,argv[2],argv[3]);
    return 0;
}

参考

https://xz.aliyun.com/t/5361?spm=5176.12901015.0.i12901015.2ad3525c5zq8Kz 一直跟着这个教程学的

https://man7.org/linux/man-pages/ linux的手册真的挺详细的

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Eaglecad 发表于 2021-1-21 19:15
有图有真相,需要搞点图
白衣国度 发表于 2021-1-21 19:36
 楼主| TongShuai 发表于 2021-1-27 15:41
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-26 09:24

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表