ptrace的解析和注入

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: ptrace

1.ptrace基础
函数原型
long ptrace(enum __ptrace_request request, pid_t pid, void addr,void data);
参数详解
主要是参数一
PTRACE_ATTACH,表示附加到指定远程进程
PTRACE_DETACH,表示从指定远程进程分离
PTRACE_GETREGS,表示读取远程进程当前寄存器环境
PTRACE_SETREGS,表示设置远程进程的寄存器环境
PTRACE_CONT,表示使远程进程继续运行
PTRACE_PEEKTEXT,从远程进程指定内存地址读取一个word大小的数据
PTRACE_POKETEXT,往远程进程指定内存地址写入一个word大小的数据
在注入需求下都是应用这两个参数的PTRACE_ATTACH、PTRACE_DETACH
参数二
需要附加注入的pid数据
参数三、参数四
都直接用NULL填充就可以
2.ptrace注入的步骤
1.调用ptrace系统函数进行附加到远程进程
2.保存寄存器的环境数据
3.通过调mmap系统函数进行分配内存空间
4.想附加的进程写入模块名称和要执行的函数名称
5.调用dlopen系统函数进行打开注入的模块
6.调用dlsym系统函数获取需调用函数的地址信息
7.远程调用被注入模块的函数
8.恢复寄存器的环境数据
9.调用ptrace系统函数进行和附加进程进行剥离
3.ptrace注入源码实现
3.1附加进程
/*
函数功能n: 使用ptrace函数 Attach到指定远程进程
参数: pid表示远程进程的ID
返回值: 返回0表示attach成功,返回-1表示失败
/
int ptrace_attach(pid_t pid)
{

int status = 0;

if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {    
    LOGD("attach process error, pid:%d", pid);    
    return -1;    
}    

 LOGD("attach process pid:%d", pid);          
waitpid(pid, &status , WUNTRACED);           

return 0;    

}

3.2剥离进程
/**
函数功能n: 使用ptrace函数 DETACH到剥离进程
参数: pid表示远程进程的ID
返回值: 返回0表示DETACH成功,返回-1表示失败
**/
int ptrace_detach(pid_t pid)
{

if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {    
    LOGD("detach process error, pid:%d", pid);     
    return -1;    
}    

LOGD("detach process pid:%d", pid);  
return 0;    

}

3.2ptrace注入so功能
/
函数功能: 通过远程直接调用dlopen\dlsym的方法ptrace注入so模块到远程进程中
参数1: pid表示远程进程的pid,
参数2:LibPath为被远程注入的so模块路径,
参数3:FunctionName为远程注入的模块后调用的函数
参数4::FuncParameter指向被远程调用函数的参数(若传递字符串,需要先将字符串写入到远程进程空间中),NumParameter为参数的个数
Return: 返回0表示注入成功,返回-1表示失败

/
int inject_remote_process(pid_t pid, char LibPath, char FunctionName, long *FuncParameter, long NumParameter)
{

int iRet = -1;
struct pt_regs CurrentRegs, OriginalRegs;  // CurrentRegs表示远程进程中当前的寄存器值,OriginalRegs存储注入前的寄存器值,方便恢复
void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;   // 远程进程中需要调用函数的地址
void *RemoteMapMemoryAddr, *RemoteModuleAddr, *RemoteModuleFuncAddr; // RemoteMapMemoryAddr为远程进程空间中映射的内存基址,RemoteModuleAddr为远程注入的so模块加载基址,RemoteModuleFuncAddr为注入模块中需要调用的函数地址
long parameters[6];  

// Attach远程进程
if (ptrace_attach(pid) == -1)
    return iRet;

// 获取远程进程的寄存器值
if (ptrace_getregs(pid, &CurrentRegs) == -1)
{
    ptrace_detach(pid);
    return iRet;
}


// 保存远程进程空间中当前的上下文寄存器环境
memcpy(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs)); 

// 获取mmap函数在远程进程中的地址
mmap_addr = GetRemoteFuncAddr(pid, libc_path, (void *)mmap);
LOGD("mmap RemoteFuncAddr:0x%lx", (long)mmap_addr);

// 设置mmap的参数
// void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);
parameters[0] = 0;  // 设置为NULL表示让系统自动选择分配内存的地址    
parameters[1] = 0x1000; // 映射内存的大小    
parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC;  // 表示映射内存区域可读可写可执行   
parameters[3] =  MAP_ANONYMOUS | MAP_PRIVATE; // 建立匿名映射    
parameters[4] = 0; //  若需要映射文件到内存中,则为文件的fd  
parameters[5] = 0; //文件映射偏移量     

// 调用远程进程的mmap函数,建立远程进程的内存映射
if (ptrace_call(pid, (long)mmap_addr, parameters, 6, &CurrentRegs) == -1)
{
    LOGD("Call Remote mmap Func Failed");
    ptrace_detach(pid);
    return iRet;
}

// 获取mmap函数执行后的返回值,也就是内存映射的起始地址
RemoteMapMemoryAddr = (void *)ptrace_getret(&CurrentRegs);
LOGD("Remote Process Map Memory Addr:0x%lx", (long)RemoteMapMemoryAddr);

// 分别获取dlopen、dlsym、dlclose等函数的地址
dlopen_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlopen);
dlsym_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlsym);
dlclose_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlclose);
dlerror_addr = GetRemoteFuncAddr(pid, linker_path, (void *)dlerror);



// 将要加载的so库路径写入到远程进程内存空间中
if (ptrace_writedata(pid, RemoteMapMemoryAddr, LibPath, strlen(LibPath) + 1) == -1)
{
    
    ptrace_detach(pid);
    return iRet;
}

// 设置dlopen的参数,返回值为模块加载的地址
// void *dlopen(const char *filename, int flag);
parameters[0] = (long)RemoteMapMemoryAddr;
parameters[1] = RTLD_NOW| RTLD_GLOBAL;

if (ptrace_call(pid, (long)dlopen_addr, parameters, 2, &CurrentRegs) == -1)
{
    
    ptrace_detach(pid);
    return iRet;
}

// RemoteModuleAddr为远程进程加载注入模块的地址
RemoteModuleAddr = (void *)ptrace_getret(&CurrentRegs);


if ((long)RemoteModuleAddr == 0x0)   // dlopen 错误
{
    LOGD("dlopen error");
    if (ptrace_call(pid, (long)dlerror_addr, parameters, 0, &CurrentRegs) == -1)
    {
        LOGD("Call Remote dlerror Func Failed");
        ptrace_detach(pid);
        return iRet;
    }
    char *Error = (void *)ptrace_getret(&CurrentRegs);
    char LocalErrorInfo[1024] = {0};
    ptrace_readdata(pid, Error, LocalErrorInfo, 1024);
    ptrace_detach(pid);
    return iRet;
}    

// 将so库中需要调用的函数名称写入到远程进程内存空间中
if (ptrace_writedata(pid, RemoteMapMemoryAddr + strlen(LibPath) + 2, FunctionName, strlen(FunctionName) + 1) == -1)
{
    
    ptrace_detach(pid);
    return iRet;
}

// 设置dlsym的参数,返回值为远程进程内函数的地址
// void *dlsym(void *handle, const char *symbol);
parameters[0] = (long)RemoteModuleAddr;
parameters[1] = (long)(RemoteMapMemoryAddr + strlen(LibPath) + 2);

if (ptrace_call(pid, (long)dlsym_addr, parameters, 2, &CurrentRegs) == -1)
{
    LOGD("Call Remote dlsym Func Failed");
    ptrace_detach(pid);
    return iRet;
}    

// RemoteModuleFuncAddr为远程进程空间内获取的函数地址
RemoteModuleFuncAddr = (void *)ptrace_getret(&CurrentRegs);


if (ptrace_call(pid, (long)RemoteModuleFuncAddr, FuncParameter, NumParameter, &CurrentRegs) == -1)
{
    LOGD("Call Remote injected Func Failed");
    ptrace_detach(pid);
    return iRet;
}    

if (ptrace_setregs(pid, &OriginalRegs) == -1)
{
    LOGD("Recover reges failed");
    ptrace_detach(pid);
    return iRet;        
}

ptrace_getregs(pid, &CurrentRegs);
if (memcmp(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs)) != 0)
{
    LOGD("Set Regs Error");
}

//剥离
if (ptrace_detach(pid) == -1)
{
    LOGD("ptrace detach failed");
    return iRet;
}

return 0;

}

相关文章
|
6月前
|
SQL 安全 PHP
|
4月前
|
SQL 安全 数据库
Python Web开发者必看!SQL注入、XSS、CSRF全面解析,守护你的网站安全!
【7月更文挑战第27天】在 Python Web 开发中, 安全至关重要。
64 0
|
6月前
|
SQL XML 安全
Pikachu SQL 注入通关解析
Pikachu SQL 注入通关解析
|
6月前
|
SQL 安全 前端开发
Go语言Gin框架安全加固:全面解析SQL注入、XSS与CSRF的解决方案
Go语言Gin框架安全加固:全面解析SQL注入、XSS与CSRF的解决方案
|
XML Java 数据格式
深入探索Spring的Bean注入:四种方式解析与循环依赖探讨
深入探索Spring的Bean注入:四种方式解析与循环依赖探讨
165 0
|
6月前
|
Java 数据库连接 API
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
SpringBoot【问题 01】借助@PostConstruct解决使用@Component注解的类用@Resource注入Mapper接口为null的问题(原因解析+解决方法)
725 0
|
Java 数据库 Android开发
Android注入框架ButterKnife使用解析
Android注入框架ButterKnife使用解析
212 0
|
缓存 Java Spring
spring aop 注入源码解析
spring aop 注入源码解析
2177 0
|
SQL Web App开发 数据库

推荐镜像

更多