作者:Fly2015

Android平台的so库的注入是有Linux平台的进程注入移植来的。由于Android系统的底层实现是基于Linux系统的源码修改而来,因此很多Linux下的应用可以移植到Android平台上来比如漏洞。由于Linux平台的注入需要权限,相比较于Windows平台的进程的注入没有被玩的那么火热。但是到了,Android平台以后,很多Android的安全手机软件,都是从这里做文章。至此,跟风学习一下,Android下的进程的So库的注入。Android平台的基本注入框架,最先我是在看雪论坛上看到的,有古河大神提供的代码:http://bbs.pediy.com/showthread.php?t=141355,下面就来学习这份代码,相似的注入框架还有很多如ddi等。

Android平台的so库注入的基本思路和windows平台的注入思路是一致的,android平台要想注入so库成功必须先取得设备的Root权限。古河大神这份注入代码是基于shellcode实现,这里称要被注入的进程为目标进程,大致的实现思路是:

1.让目标进程调用其mmap函数在其进程内存中申请一段内存空间

2.将要注入的so库的名称字符串和so库中要调用的函数名称字符串写入到目标进程的内存(上面申请的内存)中

3.将编写好的ShellCode汇编代码写入到到目标进程的内存(上面申请的内存)中

4.修改目标进程的PC寄存器的值,让其跳到注入的ShellCode代码中执行,实现so库的注入,然后调用注入的so库中的函数。

具体的实现代码如下,已经做好了完全的注释,并修改了代码中多余的和有误的地方。古河大神的这份代码其实是Android实现so库注入的工具,注入的so库就是我们可以发挥的地方,既可以是对Android的函数的Hook也可以是其他的操作代码。

Android的so库注入代码的头文件inject.h:

  1. #pragma once
  2. #include <sys/types.h>
  3. #ifdef __cplusplus
  4. extern "C"
  5. {
  6. #endif
  7. //远程进程注入
  8. int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size);
  9. //根据进程的名字获取进程的PID
  10. int find_pid_of(const char *process_name);
  11. //获取进程加载的模块的基址
  12. void* get_module_base(pid_t pid, const char* module_name);
  13. #ifdef __cplusplus
  14. }
  15. #endif
  16. //进程注入的参数-根据Hook的函数需要自定义该结构体
  17. struct inject_param_t
  18. {
  19. //进程的PID
  20. pid_t from_pid;
  21. } ;

Android的so库注入代码的实现文件inject.c:

  1. /*
  2. ============================================================================
  3. Name : libinject.c
  4. Author :
  5. Version :
  6. Copyright :
  7. Description : 对古河大神所提供的Android LibInject代码的学习和修改
  8. ============================================================================
  9. */
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <asm/ptrace.h>
  13. #include <asm/user.h>
  14. #include <asm/ptrace.h>
  15. #include <sys/wait.h>
  16. #include <sys/mman.h>
  17. #include <dlfcn.h>
  18. #include <dirent.h>
  19. #include <unistd.h>
  20. #include <string.h>
  21. #include <utils/Log.h>
  22. #define ENABLE_DEBUG 1
  23. #define PTRACE_PEEKTEXT 1
  24. #define PTRACE_POKETEXT 4
  25. #define PTRACE_ATTACH 16
  26. #define PTRACE_CONT 7
  27. #define PTRACE_DETACH 17
  28. #define PTRACE_SYSCALL 24
  29. #define CPSR_T_MASK (1u << 5)
  30. #define MAX_PATH 0x100
  31. //本地ShellCode的指令或者数据的内存地址到远程目标进程的内存地址的重定位映射
  32. #define REMOTE_ADDR(addr, local_base, remote_base) ((uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base))
  33. //系统调用函数mmap所在的模块
  34. const char *libc_path = "/system/lib/libc.so";
  35. //系统调用函数dlopn、dlsym、dlclose所在的模块
  36. const char *linker_path = "/system/bin/linker";
  37. //显示调试的信息
  38. #if ENABLE_DEBUG
  39. #define DEBUG_PRINT(format,args...) \
  40. LOGD(format, ##args)
  41. #else
  42. #define DEBUG_PRINT(format,args...)
  43. #endif
  44. //#########################################################################################################
  45. //查找要注入的目标进程的PID
  46. //process_name为要查找的进程名字
  47. int find_pid_of(const char *process_name)
  48. {
  49. int id;
  50. DIR* dir;
  51. FILE *fp;
  52. //保存进程的PID
  53. pid_t pid = -1;
  54. //保存进程的名称
  55. char filename[32];
  56. //保存运行进程的命令行
  57. char cmdline[256];
  58. struct dirent * entry;
  59. //进程的名字不能为NULL
  60. if (process_name == NULL)
  61. return -1;
  62. //打开文件目录"/proc"
  63. dir = opendir("/proc");
  64. //文件目录"/proc"的句柄不能为NULL
  65. if (dir == NULL)
  66. return -1;
  67. /*
  68. * 函数struct dirent* readdir(DIR* dir_handle); //读取目录(循环遍历)
  69. * struct dirent
  70. * {
  71. * long d_ino; //inode number 索引节点号
  72. * off_t d_off; //offset to this dirent 在目录文件中的偏移
  73. * unsigned short d_reclen; //length of this d_name 文件名长
  74. * unsigned char d_type; //the type of d_name 文件类型
  75. * char d_name [NAME_MAX+1]; //file name (null-terminated) 文件名,最长255字符
  76. * }
  77. */
  78. //循环读取文件目录"/proc"里的文件
  79. while((entry = readdir(dir)) != NULL)
  80. {
  81. //将文件名字符串转整型得到进程的PID
  82. id = atoi(entry->d_name);
  83. if (id != 0)
  84. {
  85. //格式化字符串得到"/proc/pid/cmdline"
  86. sprintf(filename, "/proc/%d/cmdline", id);
  87. //打开文件"/proc/pid/cmdline"
  88. fp = fopen(filename, "r");
  89. if (fp)
  90. {
  91. //读取运行进程的命令行中的arg[0]即进程的名称
  92. fgets(cmdline, sizeof(cmdline), fp);
  93. fclose(fp);
  94. //判断获取到进程的名字是否与要查找的目标进程名字process_name相等
  95. if (strcmp(process_name, cmdline) == 0)
  96. {
  97. //保存目标进程的PID
  98. pid = id;
  99. break;
  100. }
  101. }
  102. }
  103. }
  104. closedir(dir);
  105. //返回查找到目标进程的PID
  106. return pid;
  107. }
  108. //#########################################################################################################
  109. /*
  110. * 对远程目标进程进行LibInject和函数的Hook
  111. * library_path------------------自定义的Hook函数所在的模块(libHook.so库)的路径
  112. * function_name-----------------Hook函数在libHook.so库中名称Hook_Api
  113. * param-------------------------Hook函数调用所需要的参数
  114. * param_size--------------------Hook函数调用所需要的参数的大小
  115. */
  116. int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size)
  117. {
  118. int ret = -1;
  119. void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr;
  120. void *local_handle, *remote_handle, *dlhandle;
  121. uint8_t *map_base;
  122. uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;
  123. struct pt_regs regs, original_regs;
  124. //导出全局变量
  125. extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
  126. _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
  127. _saved_cpsr_s, _saved_r0_pc_s;
  128. uint32_t code_length;
  129. long parameters[10];
  130. DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);
  131. //附加远程目标进程
  132. if (ptrace_attach(target_pid) == -1)
  133. return EXIT_SUCCESS;
  134. //获取附加远程目标进程此时寄存器的状态值
  135. if (ptrace_getregs(target_pid, &regs) == -1)
  136. goto exit;
  137. //保存获取到的附加远程目标进程的寄存器的状态值
  138. memcpy(&original_regs, &regs, sizeof(regs));
  139. //获取附加远程目标进程"/system/lib/libc.so"模块中函数mmap的调用地址
  140. mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap); //"/system/lib/libc.so"
  141. DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);
  142. //格式化函数mmap的调用参数
  143. parameters[0] = 0; // addr
  144. parameters[1] = 0x4000; // size 申请内存空间的大小
  145. parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot 可读可写可执行
  146. parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
  147. parameters[4] = 0; // fd
  148. parameters[5] = 0; // offset
  149. DEBUG_PRINT("[+] Calling mmap in target process.\n");
  150. //在附加远程目标进程中调用函数mmmap申请内存空间
  151. if (ptrace_call(target_pid, (uint32_t)mmap_addr, parameters, 6, &regs) == -1)
  152. goto exit;
  153. //读取附加远程目标进程中此时寄存器的状态值,获取函数mmap调用返回的申请内存空间的地址
  154. if (ptrace_getregs(target_pid, &regs ) == -1)
  155. goto exit;
  156. DEBUG_PRINT("[+] Target process returned from mmap, return value=%x, pc=%x \n", regs.ARM_r0, regs.ARM_pc );
  157. //保存在附加远程目标进程中申请到内存空间的地址map_base = r0
  158. map_base = (uint8_t *)regs.ARM_r0;
  159. //获取附加远程目标进程中函数dlopen的调用地址
  160. dlopen_addr = get_remote_addr(target_pid, linker_path, (void *)dlopen); //"/system/bin/linker"
  161. //获取附加远程目标进程中函数dlsym的调用地址
  162. dlsym_addr = get_remote_addr(target_pid, linker_path, (void *)dlsym); //"/system/bin/linker"
  163. //获取附加远程目标进程中函数dlclose的调用地址
  164. dlclose_addr = get_remote_addr(target_pid, linker_path, (void *)dlclose); //"/system/bin/linker"
  165. DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x\n", dlopen_addr, dlsym_addr, dlclose_addr );
  166. //附加远程目标进程注入代码ShellCode的起始地址,并预留0x3C00的内存空间空间
  167. remote_code_ptr = map_base + 0x3C00;
  168. //注入ShellCode的本地起始地址
  169. local_code_ptr = (uint8_t *)&_inject_start_s;
  170. //保存函数dlopen的调用地址到全局变量_dlopen_addr_s中
  171. _dlopen_addr_s = (uint32_t)dlopen_addr;
  172. //保存函数dlsym的调用地址到全局变量_dlsym_addr_s中
  173. _dlsym_addr_s = (uint32_t)dlsym_addr;
  174. //保存函数dlclose的调用地址到全局变量_dlclose_addr_s中
  175. _dlclose_addr_s = (uint32_t)dlclose_addr;
  176. DEBUG_PRINT("[+] Inject code start: %x, end: %x\n", local_code_ptr, &_inject_end_s);
  177. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  178. //获取注入ShellCode代码指令的长度
  179. code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s;
  180. //本地为函数dlopen的第1个参数pathname变量申请内存空间
  181. //void * dlopen(const char* pathname, int mode);
  182. dlopen_param1_ptr = local_code_ptr + code_length + 0x20;
  183. //本地为函数dlsym的第2个参数symbol变量申请内存空间
  184. //void*dlsym(void* handle, constchar* symbol);
  185. dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH;
  186. //本地为附加远程目标进程的寄存器状态值r0-r15(pc)的保存申请内存空间
  187. saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH;
  188. //本地为附加远程目标进程的Hook函数的参数inject_param_ptr申请内存空间
  189. inject_param_ptr = saved_r0_pc_ptr + MAX_PATH;
  190. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  191. //拷贝函数dlopen的第1个参数到本地内存空间dlopen_param1_ptr中
  192. //函数dlopen的第1个参数也就是附加远程目标中要调用的Hook函数所在的模块
  193. strcpy(dlopen_param1_ptr, library_path);
  194. //获取函数dlopen的第1个参数从本地内存地址到附加远程目标进程内存映射的重定位地址
  195. _dlopen_param1_s = REMOTE_ADDR(dlopen_param1_ptr, local_code_ptr, remote_code_ptr);
  196. DEBUG_PRINT("[+] _dlopen_param1_s: %x\n", _dlopen_param1_s);
  197. //拷贝函数dlsym的第2个参数到本地内存空间dlsym_param2_ptr中
  198. //函数dlsym的第2个参数也就是附加远程目标中要调用的Hook函数的名称
  199. strcpy(dlsym_param2_ptr, function_name);
  200. //获取函数dlsym的第2个参数从本地内存地址到附加远程目标进程内存映射的重定位地址
  201. _dlsym_param2_s = REMOTE_ADDR(dlsym_param2_ptr, local_code_ptr, remote_code_ptr);
  202. DEBUG_PRINT("[+] _dlsym_param2_s: %x\n", _dlsym_param2_s);
  203. //保存附加远程目标进程的cpsr寄存器的值(cpsr寄存器在ARM的模式切换的时候会使用)
  204. _saved_cpsr_s = original_regs.ARM_cpsr;
  205. //保存附加远程目标进程的寄存器r0-r15(pc)的状态值
  206. memcpy(saved_r0_pc_ptr, &(original_regs.ARM_r0), 16*4); // r0 ~ r15
  207. //获取附加远程目标进程的寄存器r0-r15(pc)的状态值从本地内存保存地址到附加远程目标进程内存映射的重定位地址
  208. _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr);
  209. DEBUG_PRINT("[+] _saved_r0_pc_s: %x\n", _saved_r0_pc_s);
  210. //拷贝附加远程目标进程的Hook函数的参数到本地内存空间inject_param_ptr中
  211. memcpy(inject_param_ptr, param, param_size);
  212. //获取附加远程目标进程的Hook函数的参数从本地内存地址到附加远程目标进程内存映射的重定位地址
  213. _inject_function_param_s = REMOTE_ADDR(inject_param_ptr, local_code_ptr, remote_code_ptr);
  214. DEBUG_PRINT("[+] _inject_function_param_s: %x\n", _inject_function_param_s);
  215. //显示附加远程目标进程的ShellCode注入的内存地址
  216. DEBUG_PRINT("[+] Remote shellcode address: %x\n", remote_code_ptr);
  217. //向附加远程目标进程的内存空间中写入0x400大小的本地ShellCode指令代码
  218. ptrace_writedata(target_pid, remote_code_ptr, local_code_ptr, 0x400);
  219. //拷贝附加远程目标进程被附加时寄存器的状态值到临时变量regs中
  220. memcpy(&regs, &original_regs, sizeof(regs));
  221. //修改附加远程目标进程的sp寄存器的值为ShellCode的注入地址
  222. regs.ARM_sp = (long)remote_code_ptr;
  223. //修改附加远程目标进程的pc寄存器的值为ShellCode的注入地址
  224. regs.ARM_pc = (long)remote_code_ptr;
  225. //设置附加远程目标进程的寄存器的状态值即让附加远程目标进程执行注入的ShellCode指令代码
  226. ptrace_setregs(target_pid, &regs);
  227. //结束目标进程的附加
  228. ptrace_detach(target_pid);
  229. //进程注入成功
  230. ret = 0;
  231. exit:
  232. return ret;
  233. }
  234. //读取被附加调试目标进程内存中的数据
  235. //读取的数据保存在buf缓冲区中
  236. int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size)
  237. {
  238. uint32_t i, j, remain;
  239. uint8_t *laddr;
  240. //联合体
  241. union u{
  242. long val;
  243. char chars[sizeof(long)];
  244. } d;
  245. //4字节的整数倍
  246. j = size / 4;
  247. //剩余的字节数
  248. remain = size % 4;
  249. //src为要读取数据的目标进程的内存地址
  250. //buf保存读取到目标进程中的数据
  251. laddr = buf;
  252. //在目标进程中读取4字节的整数倍的数据
  253. for (i = 0; i < j; i++)
  254. {
  255. //在目标进程中读取数据
  256. d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
  257. //拷贝读取的数据到临时缓冲区中
  258. memcpy(laddr, d.chars, 4);
  259. src += 4;
  260. laddr += 4;
  261. }
  262. //在目标进程中读取剩余的数据
  263. if (remain > 0)
  264. {
  265. //在目标进程中读取数据
  266. d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
  267. //拷贝读取的数据到临时缓冲区中
  268. memcpy(laddr, d.chars, remain);
  269. }
  270. return 0;
  271. }
  272. //向附加调试的目标进程内存中写入数据
  273. int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size)
  274. {
  275. uint32_t i, j, remain;
  276. uint8_t *laddr;
  277. //联合体
  278. union u {
  279. long val;
  280. char chars[sizeof(long)];
  281. } d;
  282. //4字节整数倍
  283. j = size / 4;
  284. //剩余的字节数
  285. remain = size % 4;
  286. //data中存放的是要写入目标进程的数据
  287. laddr = data;
  288. //向目标进程中写入4字节的整数倍的数据
  289. for (i = 0; i < j; i ++)
  290. {
  291. memcpy(d.chars, laddr, 4);
  292. //向目标中写入1个字的数据
  293. ptrace(PTRACE_POKETEXT, pid, dest, d.val);
  294. dest += 4;
  295. laddr += 4;
  296. }
  297. //向目标进程中写入剩余的数据
  298. if (remain > 0)
  299. {
  300. //d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0); //原来的代码中有,感觉是多余的
  301. for ( i = 0; i < remain; i++)
  302. {
  303. d.chars[i] = *laddr++;
  304. }
  305. //向目标进程中写入剩余的数据
  306. ptrace(PTRACE_POKETEXT, pid, dest, d.val);
  307. }
  308. return 0;
  309. }
  310. //向附加调试的目标进程内存中写入字符串数据
  311. int ptrace_writestring(pid_t pid, uint8_t *dest, char *str)
  312. {
  313. //调用函数向附加目标进程内存中写入数据
  314. return ptrace_writedata(pid, dest, str, strlen(str)+1);
  315. }
  316. /*
  317. * 在其他进程(远程目标进程)中调用系统函数mmap申请内存空间
  318. * void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
  319. * params是已经格式化的mmap函数的参数,num_params是mmap函数的参数的个数
  320. * regs是远程目标进程的寄存器的数据,addr为远程目标进程中函数mmap的调用地址
  321. */
  322. int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)
  323. {
  324. uint32_t i;
  325. /*
  326. struct user_regs_struct
  327. {
  328. long int ebx;
  329. long int ecx;
  330. long int edx;
  331. long int esi;
  332. long int edi;
  333. long int ebp;
  334. long int eax;
  335. long int xds;
  336. long int xes;
  337. long int xfs;
  338. long int xgs;
  339. long int orig_eax;
  340. long int eip;
  341. long int xcs;
  342. long int eflags;
  343. long int esp;
  344. long int xss;
  345. };
  346. */
  347. //ARM中函数mmap的前4个参数通过r0-r3来传入
  348. for (i = 0; i < num_params && i < 4; i ++)
  349. {
  350. regs->uregs[i] = params[i];
  351. }
  352. //ARM中函数mmap的剩余2个参数通过栈来传入
  353. if (i < num_params)
  354. {
  355. //在目标进程的ARM栈中为剩余的2个参数申请内存空间
  356. regs->ARM_sp -= (num_params - i)*sizeof(long);
  357. //向目标进程的ARM栈中写入剩余的2个参数的数据
  358. ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i)*sizeof(long));
  359. }
  360. //设置远程目标进程的的PC寄存器的值(修改目标进程的执行)
  361. regs->ARM_pc = addr; //addr为远程目标进程中函数mmap的调用地址
  362. //根据远程目标进程的运行模式,设置目标进程的CPSR寄存器的值
  363. if (regs->ARM_pc & 1)
  364. {
  365. //thumb模式
  366. regs->ARM_pc &= (~1u);
  367. regs->ARM_cpsr |= CPSR_T_MASK;
  368. }
  369. else
  370. {
  371. //arm模式
  372. regs->ARM_cpsr &= ~CPSR_T_MASK;
  373. }
  374. //设置远程目标进程的LR寄存器的值为0,触发地址0异常回到当前进程中
  375. regs->ARM_lr = 0;
  376. //设置远程目标进程各寄存器的值然后在远程目标进程中调用mmap函数申请内存空间
  377. if (ptrace_setregs(pid, reg) == -1
  378. || ptrace_continue(pid) == -1)
  379. {
  380. return -1;
  381. }
  382. //等待在远程目标进程中申请内存空间操作的完成
  383. //申请到的内存空间的地址保存在返回值寄存器r0中
  384. waitpid(pid, NULL, WUNTRACED);
  385. return 0;
  386. }
  387. //获取被附加调试进程的寄存器的值
  388. int ptrace_getregs( pid_t pid, struct pt_regs* regs )
  389. {
  390. if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0)
  391. {
  392. perror( "ptrace_getregs: Can not get register values");
  393. return -1;
  394. }
  395. return 0;
  396. }
  397. //设置被附加调试进程的寄存器的值
  398. int ptrace_setregs(pid_t pid, struct pt_regs* regs)
  399. {
  400. if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0)
  401. {
  402. perror("ptrace_setregs: Can not set register values");
  403. return -1;
  404. }
  405. return 0;
  406. }
  407. //附加的目标进程继续执行
  408. int ptrace_continue(pid_t pid)
  409. {
  410. if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0)
  411. {
  412. perror("ptrace_cont");
  413. return -1;
  414. }
  415. return 0;
  416. }
  417. //附加目标进程
  418. int ptrace_attach(pid_t pid)
  419. {
  420. //附加目标进程
  421. if (ptrace(PTRACE_ATTACH, pid, NULL, 0 ) < 0)
  422. {
  423. perror("ptrace_attach");
  424. return -1;
  425. }
  426. //等待目标进程附加完成
  427. waitpid(pid, NULL, WUNTRACED);
  428. //DEBUG_PRINT("attached\n");
  429. //目标进程继续执行,让目标进程在下次进/出系统调用时被调试
  430. if (ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0)
  431. {
  432. perror("ptrace_syscall");
  433. return -1;
  434. }
  435. //等待目标进程的此设置的完成
  436. waitpid(pid, NULL, WUNTRACED);
  437. return 0;
  438. }
  439. //结束目标进程的附加
  440. int ptrace_detach(pid_t pid)
  441. {
  442. if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0)
  443. {
  444. perror("ptrace_detach");
  445. return -1;
  446. }
  447. return 0;
  448. }
  449. //获取进程加载模块的基址
  450. void* get_module_base(pid_t pid, const char* module_name)
  451. {
  452. FILE *fp;
  453. long addr = 0;
  454. char *pch;
  455. //保存模块的名称
  456. char filename[32];
  457. //保存读取的信息
  458. char line[1024];
  459. if (pid < 0)
  460. {
  461. //获取当前进程的模块的基址
  462. snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
  463. }
  464. else
  465. {
  466. //获取其他进程的模块的基址
  467. snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
  468. }
  469. //打开"/proc/pid/maps"文件
  470. fp = fopen(filename, "r");
  471. if (fp != NULL)
  472. {
  473. //循环读取"/proc/pid/maps"文件的信息,每次一行
  474. while (fgets(line, sizeof(line), fp))
  475. {
  476. //判断读取的信息line中是否包含要查找的模块名称
  477. if (strstr(line, module_name))
  478. {
  479. //以"-"为标记拆分字符串
  480. pch = strtok(line, "-");
  481. //字符串转无符号长整型的模块基址
  482. addr = strtoul(pch, NULL, 16 );
  483. //排除特殊情况
  484. if (addr == 0x8000)
  485. addr = 0;
  486. break;
  487. }
  488. }
  489. fclose( fp );
  490. }
  491. //返回获取到的模块的基址
  492. return (void *)addr;
  493. }
  494. //获取其他进程的某加载模块中某系统函数的调用地址
  495. /*
  496. * Once we know the base address of a given library both in our process and in the target process,
  497. * what we can do to resolve the remote function address is:
  498. * REMOTE_ADDRESS = LOCAL_ADDRESS + (REMOTE_BASE - LOCAL_BASE)
  499. *
  500. */
  501. void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
  502. {
  503. void* local_handle, *remote_handle;
  504. //获取某系统模块在当前进程中的加载基址
  505. local_handle = get_module_base(-1, module_name);
  506. //获取其他进程(目标进程)中某系统模块的加载基址
  507. remote_handle = get_module_base(target_pid, module_name);
  508. DEBUG_PRINT("[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle);
  509. //REMOTE_ADDRESS = LOCAL_ADDRESS + (REMOTE_BASE - LOCAL_BASE)
  510. //获取其他进程(目标进程)某系统模块中某系统函数的调用地址并返回
  511. return (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);
  512. }
  513. //******************************************************************************************************
  514. //main函数
  515. int main(int argc, char** argv)
  516. {
  517. //要注入的进程的PID
  518. pid_t target_pid;
  519. //查找要注入的目标进程"/system/bin/servicemanager"的PID
  520. target_pid = find_pid_of("/system/bin/surfaceflinger");
  521. //对目标进程servicemanager进行LibInject和函数的Hook
  522. //"/data/local/tmp/libhookdll.so"为要注入到目标进程中的so库
  523. //"hook_entry"为注入要调用的so库中的函数
  524. inject_remote_process(target_pid, "/data/local/tmp/libhookdll.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!"));
  525. }

Android的so库注入代码的SellCode代码shellcode.s的编写:

  1. .global _dlopen_addr_s @全局变量_dlopen_addr_s保存dlopen函数的调用地址
  2. .global _dlopen_param1_s @全局变量_dlopen_param1_s保存函数dlopen的第一个参数-加载库文件的路径
  3. .global _dlopen_param2_s @全局变量_dlopen_param2_s保存函数dlopen的第二个参数-库文件的打开模式
  4. .global _dlsym_addr_s @全局变量_dlsym_addr_s保存函数dlsym的调用地址
  5. .global _dlsym_param2_s @全局变量_dlsym_param2_s保存函数dlsym的第二个参数-获取调用地址的函数的名称
  6. .global _dlclose_addr_s @全局变量_dlclose_addr_s保存函数dlclose的调用地址
  7. .global _inject_start_s @全局变量_inject_start_s保存注入代码的起始地址
  8. .global _inject_end_s @全局变量_inject_end_s保存注入代码的结束地址
  9. .global _inject_function_param_s @全局变量_inject_function_param_s保存Hook函数的参数
  10. .global _saved_cpsr_s @全局变量_saved_cpsr_s保存当前程序状态寄存器CPSR的值
  11. .global _saved_r0_pc_s @全局变量_saved_r0_pc_s保存寄存器环境R0-R15(PC)的值起始地址
  12. @定义数据段.data
  13. .data
  14. @注入代码的起始地址
  15. _inject_start_s:
  16. @ debug loop
  17. 3:
  18. @sub r1, r1, #0
  19. @B 3b
  20. @调用dlopen函数
  21. ldr r1, _dlopen_param2_s @库文件的打开模式
  22. ldr r0, _dlopen_param1_s @加载库文件的路径字符串即Hook函数所在的模块
  23. ldr r3, _dlopen_addr_s @dlopen函数的调用地址
  24. blx r3 @调用函数dlopen加载并打开动态库文件
  25. subs r4, r0, #0 @判断函数返回值r0-是否打开动态库文件成功
  26. beq 2f @打开动态库文件失败跳转标签2的地方执行
  27. @r0保存加载库的引用pHandle
  28. @调用dlsym函数
  29. ldr r1, _dlsym_param2_s @获取调用的地址的函数名称字符串
  30. ldr r3, _dlsym_addr_s @dlsym函数的调用地址
  31. blx r3 @调用函数dlsym获取目标函数的调用地址
  32. subs r3, r0, #0 @判断函数的返回值r0
  33. beq 1f @不成功跳转到标签1的地方执行
  34. @r3保存获取到的函数的调用地址
  35. @调用Hook_Api函数
  36. ldr r0, _inject_function_param_s @给Hook函数传入参数r0
  37. blx r3 @调用Hook函数Hook远程目标进程的某系统调用函数
  38. subs r0, r0, #0 @判断函数的返回值r0
  39. beq 2f @r0=0跳转到标签2的地方执行 ??
  40. 1:
  41. @调用dlclose函数
  42. mov r0, r4 @参数r0动态库的应用
  43. ldr r3, _dlclose_addr_s @赋值r3dlclose函数的调用地址
  44. blx r3 @调用dlclose函数关闭库文件的引用pHandle
  45. 2:
  46. @恢复目标进程的原来状态
  47. ldr r1, _saved_cpsr_s
  48. msr cpsr_cf, r1 @恢复目标进程寄存器CPSR的值
  49. ldr sp, _saved_r0_pc_s
  50. ldmfd sp, {r0-pc} @恢复目标进程寄存器环境R0-R15(PC)的值且sp不改变
  51. _dlopen_addr_s:
  52. .word 0x11111111 @初始化word型全局变量_dlopen_addr_s
  53. _dlopen_param1_s:
  54. .word 0x11111111 @初始化word型全局变量_dlopen_param1_s
  55. _dlopen_param2_s: @初始化word型全局变量_dlopen_param2_s = 0x2
  56. .word 0x2
  57. _dlsym_addr_s:
  58. .word 0x11111111 @初始化word型全局变量_dlsym_addr_s
  59. _dlsym_param2_s:
  60. .word 0x11111111 @初始化word型全局变量_dlsym_param2_s
  61. _dlclose_addr_s:
  62. .word 0x11111111 @初始化word型全局变量_dlclose_addr_s
  63. _inject_function_param_s:
  64. .word 0x11111111 @初始化word型全局变量_inject_function_param_s
  65. _saved_cpsr_s:
  66. .word 0x11111111 @初始化word型全局变量_saved_cpsr_s
  67. _saved_r0_pc_s:
  68. .word 0x11111111 @初始化word型全局变量_saved_r0_pc_s
  69. @注入代码的结束地址
  70. _inject_end_s:
  71. .space 0x400, 0 @申请的代码段内存空间大小
  72. @数据段.data的结束位置
  73. .end

Android的so库注入代码的Android.mk文件的编写:

  1. LOCAL_PATH := $(call my-dir)
  2. #
  3. #注入程序LibInject
  4. #
  5. #清除变量
  6. include $(CLEAR_VARS)
  7. #生成的模块的名称LibInject
  8. LOCAL_MODULE := LibInject
  9. #需要编译的源码并包含注入的shellcode代码
  10. LOCAL_SRC_FILES := inject.c shellcode.s
  11. #使用Android的Log日志系统
  12. LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
  13. #LOCAL_FORCE_STATIC_EXECUTABLE := true
  14. #编译生成可执行程序
  15. include $(BUILD_EXECUTABLE)

Android的so库注入代码的Application.mk文件的编写:

  1. #最终编译运行支持的平台
  2. APP_ABI := armeabi armeabi-v7a

OK,Android的so库的注入工具LibInject已经写好了。下面就可以开始编写注入到Android目标进程中的so库的代码了。

Android的so库注入的更多相关文章

  1. Android平台的so注入--LibInject

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53890315 大牛古河在看雪论坛分享的Android平台的注入代码,相信很多搞An ...

  2. Android进程的so注入--Poison(稳定注入版)

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53869796 Android进程的so注入已经是老技术了,网上能用的Android ...

  3. dlmalloc(Android bionic C库的malloc实现)简介

    欢迎转载opendevkit文章, 文章原始地址: http://www.opendevkit.com/?e=56 Dlmalloc是目前一个十分流行的内存分配器,其由Doug Lea从1987年开始 ...

  4. Android 第三方开源库收集整理(转)

    原文地址:http://blog.csdn.net/caoyouxing/article/details/42418591 Android开源库 自己一直很喜欢Android开发,就如博客签名一样,  ...

  5. Android开源图表库介绍

    XCL-Charts XCL-Charts V1.8     Android开源图表库(XCL-Charts is a free charting library for Android platfo ...

  6. Android的底层库libutils介绍

    第一部分 libutils概述 libutils是Android的底层库,这个库以C++实现,它提供的API也是C++的.Android的层次的C语言程序和库,大都基于libutils开发. libu ...

  7. Android JSON 解析库的使用 - Gson 和 fast-json

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族 ...

  8. Android进阶笔记02:Android 网络请求库的比较及实战(二)

    一.Volley        既然在android2.2之后不建议使用HttpClient,那么有没有一个库是android2.2及以下版本使用HttpClient,而android2.3及以上版本 ...

  9. Android中通过进程注入技术改动广播接收器的优先级

    前言 这个周末又没有吊事,在家研究了怎样通过进程的注入技术改动广播接收器的优先级.关于这个应用场景是非常多的.并且也非常重要.所以就非常急的去fixed了. Android中的四大组件中有一个广播:B ...

随机推荐

  1. mac 下如何轻松安装神器 Anaconda

    本文推荐使用homebrew 安装 1.打开终端执行 brew cask install anaconda3 然后就可以喝一杯咖啡了,终端会自动执行安装好 如果终端卡在update homebrew ...

  2. 报错问题: AtrributeError:module ‘allure’ has no attribute ‘’severity_level’

    问题:执行命令报错:pytest -s -q --alluredir report 报错问题: AtrributeError:module 'allure' has no attribute ''se ...

  3. POJ-2752(KMP算法+前缀数组的应用)

    Seek the Name, Seek the Fame POJ-2752 本题使用的算法还是KMP 最主要的片段就是前缀数组pi的理解,这里要求解的纸盒pi[n-1]有关,但是还是需要使用一个循环来 ...

  4. 什么是内存对齐,go中内存对齐分析

    内存对齐 什么是内存对齐 为什么需要内存对齐 减少次数 保障原子性 对齐系数 对齐规则 总结 参考 内存对齐 什么是内存对齐 弄明白什么是内存对齐的时候,先来看一个demo type s struct ...

  5. rest framework Genericview

    通用视图 Django的通用视图...被开发为普通使用模式的快捷方式......他们采取某些共同的习惯和模式的发展观和抽象,从而使您可以快速地将数据写入的共同看法,而不必重复自己发现的. - Djan ...

  6. SpringCloud里面切换数据源无效的问题

    问题描述: 调用链:controller1的接口A->service1的方法A->service2的方法B 方法A开启了事务,且指定了数据库A的数据源 方法B也开启了事务,使用了默认的事务 ...

  7. Linux目录,rpm及top,vi命令简记

    一次简单的Linux常用操作记录 一.一些Linux目录结构 /bin 存放二进制可执行文件(ls.cat.mkdir等),一些常用的命令一般都在这里. /etc 存放系统管理和配置文件 /home ...

  8. python 集合详解

    字符串 一个个字符组成的有序的序列,时字符的集合 使用单引,双引,三引 引住的字符序列 字符时不可变的对象 bytes定义 bytes不可变字节序列 使用b前缀定义 只允许基本ASCII使用字符形式 ...

  9. FIL怎么获得?FIL在哪里购买?

    从一些交易网站可以看到,FIL 这几天有一个比较大的涨幅,这让许多投资 FIL 的朋友大松一口气:FIL,你终于不装睡了.估计许多关注区块链的小伙伴看到消息又要问了:FIL 怎么获得?FIL 在哪里购 ...

  10. Java字符串==和equals的区别

    首先我们来了解一下String类,Java的字符串是一旦被赋值之后无法更改的(这里的无法更改是指不能将字符串中单个或一段字符重新赋值),这也是Java虚拟机为了减少内存开销,避免字符串的重复创建设立的 ...