前两篇主要说了一些基本内容,如pid的获取,都是查询后手动输入的,本片介绍注入第三方so到目标进程并执行so的方法。
前两篇的地址:android inject(一)ptrace基础 android inject (二) 跨进程注入
/*
* 注入so到进程
* 1.获取目标进程pid
* 2.附加目标进程
* 3.获取/保存目标进程寄存器的内容
* 4.计算mmap方法的地址offset
* 5.调用mmap方法,映射一块内存(更改本地寄存器的值,更改后赋值到目标寄存器,其目的是装注入so的路径,便于dlopen调用)
* 6.计算 dlopen、dlsym、dlclose方法的地址
* 7.分别调用上三个方法来加载so/获取so方法地址/释放so
* 8.恢复上下文
*/
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/ptrace.h>
//#include <asm/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <android/log.h>
#define ENABLE_DEBUG 0
#define PTRACE_PEEKTEXT 1
#define PTRACE_POKETEXT 4
#define PTRACE_ATTACH 16
#define PTRACE_GETREGS 12
#define PTRACE_CONT 7
#define PTRACE_DETACH 17
#define PTRACE_SYSCALL 24
#define CPSR_T_MASK ( 1u << 5 )
#if defined(__aarch64__)
const char *PATH_LIBC_SO = "/system/lib64/libc.so";
const char *PATH_LINKER = "/system/bin/linker64";
#else
const char *PATH_LIBC_SO = "/system/lib/libc.so";
const char *PATH_LINKER = "/system/bin/linker";
#endif
//const char *PATH_LINKER = "/system/bin/linker";
//const char *PATH_LIBC_SO = "/system/lib/libc.so";
const char* PATH_INJECT_SO = "/data/local/tmp/libso.so";
// 1.获取目标进程pid
int getRemotePid(const char* pack) {
int remotePid = 0;
int resultRemotePid = 0;
struct dirent* entry = NULL;
DIR* dir = opendir("/proc");
char filename[1024];
char cmdline[1024];
FILE* fp = NULL;
if (NULL == dir) {
return -1;
}
while (NULL != (entry = readdir(dir))) {
remotePid = atoi(entry->d_name);
if (0 != remotePid) {
snprintf(filename, 1024, "/proc/%d/cmdline", remotePid);
fp = fopen(filename, "r");
if (fp) {
fgets(cmdline, sizeof(cmdline), fp);
fclose(fp);
if (0 == (strncmp(pack, cmdline, strlen(pack)))
&& strlen(pack) == strlen(cmdline)) {
printf(" cmdline:%s\n", cmdline);
resultRemotePid = remotePid;
break;
}
}
}
}
closedir(dir);
return resultRemotePid;
}
// 2.附加目标进程
int ptrace_attach(int pid) {
int status;
//附加 pid 进程
if (0 > ptrace(PTRACE_ATTACH, pid, NULL, 0)) {
return -1;
}
//等待附加进程停止
waitpid((pid_t) pid, &status, WUNTRACED);
return 0;
}
// 3.获取目标进程寄存器的值
int ptrace_getregs(int pid, struct pt_regs* regs) {
if (0 > ptrace(PTRACE_GETREGS, pid, NULL, regs)) {
printf(" ***************ptrace_getregs error!");
return -1;
}
return 0;
}
// 4.计算mmap地址offset
// 计算 pid 进程中 methodAdr 的偏移
void* getRemoteFuncAddr(int pid, const char* soName, void* selfMethodAddr) {
int copyPid = -1;
char mapsFile[1024] = { 0 };
char mapsLineData[1024] = { 0 };
unsigned long soAddr = 0;
unsigned long selfSoAddr = 0;
unsigned long remoteSoAddr = 0;
FILE* fp = NULL;
for (;;) {
if (-1 == copyPid) {
snprintf(mapsFile, sizeof(mapsFile), "/proc/self/maps");
} else {
snprintf(mapsFile, sizeof(mapsFile), "/proc/%d/maps", copyPid);
}
fp = fopen(mapsFile, "r");
if (NULL != fp) {
while (fgets(mapsLineData, sizeof(mapsLineData), fp)) {
if (strstr(mapsLineData, soName)) {
char* addr = strtok(mapsLineData, "-");
soAddr = strtoul(addr, NULL, 16);
printf(
" getRemoteFuncAddr_ mapsFile:%s ,mapsLineData:%s ,soName:%s ,soAddr:%ld\n",
mapsFile, mapsLineData, soName, soAddr);
if (0x8000 == soAddr) {
soAddr = 0;
}
break;
}
}
}
fclose(fp);
if (-1 == copyPid) {
printf(" getRemoteFuncAddr_ pid:%d , soAddr:%p\n", copyPid,
(void*) soAddr);
copyPid = pid;
selfSoAddr = soAddr;
} else {
printf(" getRemoteFuncAddr_ pid:%d , soAddr:%p\n", copyPid,
(void*) soAddr);
remoteSoAddr = soAddr;
break;
}
}
long resultAddr = ((long) selfMethodAddr - (long) selfSoAddr
+ (long) remoteSoAddr);
printf(" getRemoteFuncAddr_ resultAddr:%p\n\n", (void*) resultAddr);
return (void*) resultAddr;
}
// 更改addr地址的值为data
int ptrace_poketext(int pid, void* addr, void* data, int paramLen) {
printf(" ptrace_poketext paramLen:%d\n", paramLen);
int writeNum, i, remain;
int writeLen = sizeof(long);
printf(" ptrace_poketext writeLen:%d\n", writeLen);
writeNum = paramLen / sizeof(long);
printf(" ptrace_poketext writeNum:%d\n", writeNum);
remain = paramLen % writeLen;
printf(" ptrace_poketext remain:%d\n", remain);
if (0 != remain) {
writeNum += 1;
}
// uint8_t* ldata = (uint8_t*) data;
// uint8_t* ldest = (uint8_t*) addr;
union u {
long val;
char chars[sizeof(long)];
} d;
uint8_t *laddr;
for (i = 0; i < writeNum; i++) {
if (remain && i == writeNum - 1) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, addr, 0);
for (i = 0; i < remain; i++) {
// laddr = (uint8_t*) data;
// d.chars[i] = *laddr++;
d.chars[i] = ((*(uint8_t*) data))++;
// d.chars[i] = *((uint8_t*) data) + 1;
}
ptrace(PTRACE_POKETEXT, pid, addr, d.val);
printf(" ptrace_poketext write>4:%d\n", i);
break;
//此处可能有bug,如 memcpy拷贝一个字节,poketext拷贝4个字节,后三个字节是多少? 不变 还是 清零 还是d内存后面的三个字节?
// memcpy(d.chars, data, remain);
// ptrace(PTRACE_POKETEXT, pid, addr, d.val);
} else {
memcpy(d.chars, data, writeLen);
//printf(" ptrace_poketext data:%s\n", &data);
ptrace(PTRACE_POKETEXT, pid, addr, d.val);
// dest += writeLen;
addr = ((uint8_t*) addr) + writeLen;
data = ((uint8_t*) data) + writeLen;
printf(" ptrace_poketext addr:%p\n", addr);
printf(" ptrace_poketext data:%p\n", data);
}
}
return 0;
}
//修改pid进程寄存器
int ptrace_setregs(int pid, struct pt_regs* regs) {
if (0 > ptrace(PTRACE_SETREGS, pid, NULL, regs)) {
return -1;
}
return 0;
}
//激活目标进程
int ptrace_cont(int pid) {
if (0 > ptrace(PTRACE_CONT, pid, NULL, 0)) {
printf(" ***************ptrace_cont error");
return -1;
}
return 0;
}
// 5.调用pid进程的methodAddr方法,参数是params
int ptrace_call(int pid, void* methodAddr, long* params, long paramNum,
struct pt_regs* regs) {
int i;
for (i = 0; i < paramNum; i++) {
if (i < 4) {
printf(" ptrace_call params[%d]:%p\n", i, (void*) params[i]);
regs->uregs[i] = params[i];
} else {
//sp开辟空间
regs->ARM_sp-= sizeof(long) * (paramNum - i);
ptrace_poketext(pid, (void *) regs->ARM_sp, ¶ms[i],
sizeof(long) * (paramNum - i));
break;
}
}
//设置pc
regs->ARM_pc= (long) methodAddr;
//pc指令集判断32 and 16
if (regs->ARM_pc& 1) {
//thumb
regs->ARM_pc &= (~1u);
regs->ARM_cpsr |= CPSR_T_MASK;
} else {
//arm
regs->ARM_cpsr &= ~CPSR_T_MASK;
}
regs->ARM_lr= 0;
//以上是修改regs的内容
if (0 > ptrace_setregs(pid, regs) || 0 > ptrace_cont(pid)) {
return -1;
}
int status = 0;
//ptrace_cont激活了附加进程, 这里等待附加进程停止
waitpid(pid, &status, WUNTRACED);
while (status != 0xb7f) {
if (ptrace_cont(pid) == -1) {
printf(" ***************ptrace_call error\n");
return -1;
}
waitpid(pid, &status, WUNTRACED);
}
return 0;
}
int ptrace_getResult(struct pt_regs * regs) {
return regs->ARM_r0;
}
// 1.调用method方法并获取返回值
int ptrace_callMethod(int pid, void* methodAddr, long* params, long paramNum,
struct pt_regs* regs) {
// 调用methodAddr
if (0 == ptrace_call(pid, methodAddr, params, paramNum, regs)) {
printf(" ptrace_callMethod ptrace_call ok\n");
} else {
printf(" **********************ptrace_callMethod ptrace_call error\n");
return -1;
}
// 获取调用methodAddr后的寄存器
if (0 == ptrace_getregs(pid, regs)) {
printf(" ptrace_callMethod ptrace_getregs ok\n");
} else {
printf(
" **********************ptrace_callMethod ptrace_getregs error\n");
return -1;
}
return 0;
}
void* ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size) {
long i, j, remain;
uint8_t *laddr;
size_t bytes_width = sizeof(long);
union u {
long val;
char chars[sizeof(long)]; //[bytes_width];
} d;
j = size / bytes_width;
remain = size % bytes_width;
laddr = buf;
for (i = 0; i < j; i++) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
memcpy(laddr, d.chars, bytes_width);
src += bytes_width;
laddr += bytes_width;
}
if (remain > 0) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
memcpy(laddr, d.chars, remain);
}
return laddr;
}
//去掉附加
int ptrace_detach(pid_t pid) {
if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
perror("ptrace_detach");
return -1;
}
return 0;
}
void printfRemoteStr(int pid, void* addr, int len) {
union u {
long val;
char chars[sizeof(long)];
} d;
char* chars = (char*) malloc(len);
int i = 0;
int j = len / sizeof(long);
int x = len % sizeof(long);
printf("****************printfRemoteStr_ addr:%p ; len:%d\n", addr, len);
for (i = 0; i < j; i++) {
d.val = ptrace(PTRACE_PEEKTEXT, pid, addr, d.chars);
memcpy(chars, d.chars, 4);
chars += sizeof(long);
addr = ((uint8_t*) addr) + sizeof(long);
}
printf("****************printfRemoteStr_ addr:%p ; chars:%s\n", addr,
chars - len + x);
}
//=============================================
int main() {
// com.example.test_android_camera
// com.tencent.mm
// com.wuba.zhuanzhuan
// com.taobao.taobao
const char* pack = "com.wuba.zhuanzhuan";
const char* injectSoMethodName = "InjectInterface";
const char* injectSoMethodParams = "InjectInterface";
struct pt_regs regs, OriginalRegs;
// 1.get remote pid
int pid = getRemotePid(pack);
printf("1 getRemotePid pack:%s , pid:%d ok\n", pack, pid);
// 2.attach pc
if (0 == ptrace_attach(pid)) {
printf("2 ptrace_attach %d ok\n", pid);
}
// 3.get remote regs
if (0 == ptrace_getregs(pid, ®s)) {
printf("3 ptrace_getregs %d ok\n", pid);
}
// 3.1.save regs
memcpy(&OriginalRegs, ®s, sizeof(regs));
// 4.get remote mmap method addr
void* remoteMmapAddr = getRemoteFuncAddr(pid, PATH_LIBC_SO, (void *) mmap);
printf("4 getRemoteFuncAddr mmap: %p ok\n", remoteMmapAddr);
//set mmap method params
long params[6];
params[0] = 0; // addr
params[1] = 0x4000; // size
params[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
params[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
params[4] = 0; //fd
params[5] = 0; //offset
// 5.call mmap method, new memory
if (0 == ptrace_callMethod(pid, remoteMmapAddr, params, 6, ®s)) {
printf("5 ptrace_callMethod mmap ok\n");
}
void* newMemBase;
//get new memory Addr
if (!(newMemBase = (void*) (®s)->ARM_r0))
return -1;
printf("mmap newMemAddr:%p\n", newMemBase);
//init newMem
// if (0 > ptrace_poketext(pid, newMemBase, (void*) "0", 0x400))
// return -1;
// printf("init newMem");
//write soPath to mem
if (0
> ptrace_poketext(pid, newMemBase, (void*) PATH_INJECT_SO,
strlen(PATH_INJECT_SO)))
return -1;
printf("ptrace_poketext PATH_INJECT_SO data write ok\n");
printf("mmap newMemAddr:%p\n", newMemBase);
printfRemoteStr(pid, newMemBase, strlen(PATH_INJECT_SO) + 1);
printf("mmap newMemAddr:%p\n", newMemBase);
//6.get remote dlopen、dlsym、dlclose method addr
void* remoteDlopenAddr = getRemoteFuncAddr(pid, PATH_LINKER,
(void *) dlopen);
void* remoteDlsymAddr = getRemoteFuncAddr(pid, PATH_LINKER, (void *) dlsym);
void* remoteDlcloseAddr = getRemoteFuncAddr(pid, PATH_LINKER,
(void *) dlclose);
void* remoteDlerror_addr = getRemoteFuncAddr(pid, PATH_LINKER,
(void *) dlerror);
printf(
"get remote methodList: dlopenAddr:%p, dlsymAddr:%p, dlcloseAddr:%p, dlerrorAddr:%p\n",
remoteDlopenAddr, remoteDlsymAddr, remoteDlcloseAddr,
remoteDlerror_addr);
params[0] = (long) newMemBase;
params[1] = RTLD_NOW | RTLD_GLOBAL;
// 6.1.call dlopen load so
printf("library path = %s\n", PATH_INJECT_SO);
if (0 == ptrace_callMethod(pid, remoteDlopenAddr, params, 2, ®s)) {
printf("6.1 ptrace_callMethod dlopen\n");
}
// 6.1.get injectSoHandle
void* injectSoHandle = (void*) (®s)->ARM_r0;
if (injectSoHandle) {
printf("6.1 dlopen inject SO injectSoHandle:%p\n", injectSoHandle);
} else {
//get error
if (0 > ptrace_callMethod(pid, remoteDlerror_addr, 0, 0, ®s)) {
return -1;
}
uint8_t *errret = (uint8_t *) (®s)->ARM_r0;
uint8_t errbuf[100];
void* errbufData = ptrace_readdata(pid, errret, errbuf, 100);
printf("6.1 *****dlopen sohandle errret:%p; error:%s\n", errret,
errbuf);
ptrace_cont(pid);
return -1;
}
#define FUNCTION_NAME_ADDR_OFFSET 0x100
// 6.2.write so.methodName to mem
if (0
== ptrace_poketext(pid,
((uint8_t *) newMemBase) + FUNCTION_NAME_ADDR_OFFSET,
(void*) injectSoMethodName,
strlen(injectSoMethodName) + 1)) {
printf("ptrace_poketext injectSo.MethodName write ok\n");
printfRemoteStr(pid,
((uint8_t *) newMemBase) + FUNCTION_NAME_ADDR_OFFSET,
strlen(injectSoMethodName) + 1);
}
params[0] = (long) injectSoHandle;
params[1] = (long) ((uint8_t*) newMemBase + FUNCTION_NAME_ADDR_OFFSET);
// 6.2.call dlsym
if (0 == ptrace_callMethod(pid, remoteDlsymAddr, params, 2, ®s)) {
printf("6.2 ptrace_callMethod dlsym\n");
}
// 6.2.get so.method addr
void* injectSoMethodAddr = (void*) (®s)->ARM_r0;
if (injectSoMethodAddr) {
printf("6.2 dlsym get SO.method addr ok\n");
} else {
//get error
if (0 > ptrace_callMethod(pid, remoteDlerror_addr, 0, 0, ®s)) {
return -1;
}
uint8_t *errret = (uint8_t *) (®s)->ARM_r0;
uint8_t errbuf[100];
void* errbufData = ptrace_readdata(pid, errret, errbuf, 100);
printf("6.2 *****dlsym getSoMethodAddr errret:%p; error:%s\n", errret,
errbuf);
ptrace_cont(pid);
return -1;
}
#define FUNCTION_PARAM_ADDR_OFFSET 0x200
// 6.3.write so.method param to mem
ptrace_poketext(pid, ((uint8_t *) newMemBase) + FUNCTION_PARAM_ADDR_OFFSET,
(void*) injectSoMethodParams, strlen(injectSoMethodParams) + 1);
printfRemoteStr(pid, ((uint8_t *) newMemBase) + FUNCTION_PARAM_ADDR_OFFSET,
strlen(injectSoMethodParams));
params[0] = (long) (((uint8_t *) newMemBase) + FUNCTION_PARAM_ADDR_OFFSET);
// 6.3.call so.mehtod
if (0 == ptrace_callMethod(pid, injectSoMethodAddr, params, 1, ®s)) {
printf("6.3 call so.method\n");
}
if (0 == ((void*) (®s)->ARM_r0)) {
printf("6.3 call so.method ok\n");
}
//6.3 dlclose so
params[0] = (long) injectSoHandle;
if (0 == ptrace_callMethod(pid, remoteDlcloseAddr, params, 1, ®s)) {
printf("6.4 ptrace_callMethod call dlclose\n");
}
if (0 == ((void*) (®s)->ARM_r0)) {
printf("6.4 ptrace_callMethod dlclose so ok\n");
}
ptrace_setregs(pid, &OriginalRegs);
ptrace_detach(pid);
printf("pid:%d\n", pid);
printf("cat /proc/%d/maps |grep libso\n", pid);
return 0;
}
上代码在真机android6.0里执行并注入成功,时间是2018年11月27日。
如何知道是否懂了?
回答自己一个问题,调用mmap方法创建内存的目的是什么?
至于其他的基本都属于调用api,没难度的。O(∩_∩)O
x86的没有写,应只是寄存器的差别。自行完善。
创建了一个qq群,主要研究android逆向 安全 系统开发(逆向android系统)方面,欢迎各位加入。群号:492788365