写点什么

ptrace 注入分析

作者:小道安全
  • 2022 年 5 月 11 日
  • 本文字数:3343 字

    阅读完需:约 11 分钟

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.2 ptrace 注入 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;
复制代码


}


以上就是整个 ptrace 的注入流程和代码实现。

发布于: 刚刚阅读数: 2
用户头像

小道安全

关注

还未添加个人签名 2020.11.25 加入

还未添加个人简介

评论

发布
暂无评论
ptrace注入分析_小道安全_InfoQ写作社区