【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取 远程 目标进程 中的 /system/lib/libc.so 动态库中的 mmap 函数地址 )

简介: 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取 远程 目标进程 中的 /system/lib/libc.so 动态库中的 mmap 函数地址 )

文章目录

一、获取 远程 目标进程 中的 /system/lib/libc.so 动态库中的 mmap 函数地址

二、从 /proc/pid/maps 文件中获取 指定 进程 中的 /system/lib/libc.so 动态库地址

三、获取 本地进程 中的 /system/lib/libc.so 动态库的 mmap 函数地址

四、获取 远程进程 中的 /system/lib/libc.so 动态库的 mmap 函数地址





一、获取 远程 目标进程 中的 /system/lib/libc.so 动态库中的 mmap 函数地址


获取 远程 目标进程 中的 /system/lib/libc.so 动态库中的 mmap 函数地址流程 :


① 获取 本地进程 /system/lib/libc.so 动态库 地址 ;


② 获取 远程进程 /system/lib/libc.so 动态库 地址 ;


③ 计算 本地进程 与 远程进程 的 /system/lib/libc.so 动态库 地址 偏移量 ;


④ 获取 本地进程 mmap 函数地址 ;


⑤ 根据 本地进程 mmap 函数地址 + 本地进程 与 远程进程 的 /system/lib/libc.so 动态库 地址 偏移量 , 计算出 远程进程 /system/lib/libc.so 动态库 的 mmap 函数地址 ;






二、从 /proc/pid/maps 文件中获取 指定 进程 中的 /system/lib/libc.so 动态库地址


查看 /proc/2223/maps 进程对应的内存信息 :

image.png



其中涉及到 /system/lib/libc.so 动态库的地址的行 :


b758c000-b758f000 r--p 000e6000 08:02 848        /system/lib/libc.so



首先 , 要获取到 maps 文件地址 ,


获取本进程的 maps 文件地址 , 直接使用 "/proc/self/maps" 字符串作为地址 ;

获取远程进程 maps 文件地址 , 需要 "/proc/%d/maps", pid 将 pid 拼接到

“/proc/%d/maps” 字符串中 ;
  char filename[32];  // maps 文件路径
  /* 获取 maps 文件路径 */
  if (pid < 0) {
  /* self process 本进程 */
  snprintf(filename, sizeof(filename), "/proc/self/maps");
  }
  else {
  /* 远程进程 */
  snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
  }


然后 , 使用只读方式 , 打开文件 ;


FILE* fp; // 文件描述符
  /* 打开 maps 文件 */
  fp = fopen(filename, "r");


最后 , 解析文件中的内容 , 按照 b758c000-b758f000 r--p 000e6000 08:02 848 /system/lib/libc.so 格式解析即可 ;


逐行遍历文件 , fgets(line, sizeof(line), fp) ;

读取一行之后 , 查看该行是否包含 "/system/lib/libc.so" 字符串子串 , strstr(line, module_name) ;

如果包含 , 则根据 - 字符 , 将其分割成字符串数组 , pch = strtok(line, "-") ;

该数组的第一个字符串就是地址值对应的字符串 ,

将字符串地址转为 int 类型地址 , 该地址就是 远程 目标进程 中的 /system/lib/libc.so 动态库地址 ; addr = strtoul(pch, NULL, 16)

解析文件代码如下 :


if (fp != NULL) {
  /* 逐行遍历 maps 文件 */
  while (fgets(line, sizeof(line), fp)) {
    /* 下面是数据行示例 */
    /* b758c000-b758f000 r--p 000e6000 08:02 848        /system/lib/libc.so */
    /* 找到查找的 module_name 行 , 如果 module_name 是 line 的子串 , 
    返回 module_name 首次出现的地址 */
    if (strstr(line, module_name)) {
    /* 按照 - 字符 , 分割字符串 , 返回第一个字符串 */
    pch = strtok(line, "-");
    /* 将 "b758c000" 字符串转为 b758c000 整型 */
    addr = strtoul(pch, NULL, 16);
    if (addr == 0x8000)
      addr = 0;
    break;
    }
  }
  /* 关闭文件 */
  fclose(fp);
  }



从 /proc/pid/maps 文件中获取 指定 进程 中的 /system/lib/libc.so 动态库地址代码 :


/* 从 /proc/pid/maps 文件中获取 */
void* get_module_base(pid_t pid, const char* module_name)
{
  FILE* fp;
  long addr = 0;
  char* pch;  // 字符串
  char filename[32];
  char line[1024];
  /* 获取 maps 文件路径 */
  if (pid < 0) {
  /* self process 本进程 */
  snprintf(filename, sizeof(filename), "/proc/self/maps");
  }
  else {
  /* 远程进程 */
  snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
  }
  /* 打开 maps 文件 */
  fp = fopen(filename, "r");
  if (fp != NULL) {
  /* 逐行遍历 maps 文件 */
  while (fgets(line, sizeof(line), fp)) {
    /* 下面是数据行示例 */
    /* b758c000-b758f000 r--p 000e6000 08:02 848        /system/lib/libc.so */
    /* 找到查找的 module_name 行 , 如果 module_name 是 line 的子串 , 
    返回 module_name 首次出现的地址 */
    if (strstr(line, module_name)) {
    /* 按照 - 字符 , 分割字符串 , 返回第一个字符串 */
    pch = strtok(line, "-");
    /* 将 "b758c000" 字符串转为 b758c000 整型 */
    addr = strtoul(pch, NULL, 16);
    if (addr == 0x8000)
      addr = 0;
    break;
    }
  }
  /* 关闭文件 */
  fclose(fp);
  }
  /* 返回指定的 module_name 动态库地址 */
  return (void*)addr;
}






三、获取 本地进程 中的 /system/lib/libc.so 动态库的 mmap 函数地址


获取本地进程的函数地址 , 函数名就是函数地址 ; (void*)mmap 就是 mmap 函数对应的函数指针 ;


/* 获取 目标进程中的 /system/lib/libc.so 动态库中的 mmap 函数地址 
  (void*)mmap 是本进程中 mmap 函数的地址 
  计算出 本进程 与 远程目标进程 libc.so 的偏移量 
  使用本进程的 mmap 函数地址 + 偏移量 , 就可以得到目标进程 mmap 函数地址*/
  mmap_addr = get_remote_addr(target_pid, libc_path, (void*)mmap);





四、获取 远程进程 中的 /system/lib/libc.so 动态库的 mmap 函数地址


分别调用 get_module_base 方法 , 获取本地进程 和 特定 PID 进程号对应的远程目标进程 的 /system/lib/libc.so 动态库 首地址 , 计算出这两个首地址之间的偏移量 (uint32_t)remote_handle - (uint32_t)local_handle ;


本进程的 mmap 函数的地址是已知的 , 直接使用 (void*)mmap 就可以获取 ;


使用 本进程的 mmap 函数地址 + 偏移量 , 就可以得到目标进程 mmap 函数地址 ;


char* ret_addr = (char*)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle)



完整代码示例 :


/* 获取 target_pid 进程的 module_name 动态库中的 local_addr 函数地址 */
void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
{
  void* local_handle, * remote_handle;
  /* 获取本进程 module_name 动态库地址 */
  local_handle = get_module_base(-1, module_name);
  /* 获取远程目标进程 module_name 动态库地址 */
  remote_handle = get_module_base(target_pid, module_name);
  char* ret_addr = (char*)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);
  LOGW("[+] get_remote_addr[%s]: local[%p], remote[%p], ret_addr[%p], local_addr[%p]\n", module_name, local_handle, remote_handle, ret_addr, local_addr);
#if defined(__i386__)
  if (!strcmp(module_name, libc_path)) {
  //ret_addr += 2;
  }
#endif  
  return ret_addr;
}



目录
相关文章
|
11月前
|
Web App开发 JavaScript 前端开发
Android端使用WebView注入一段js代码实现js调用android
Android端使用WebView注入一段js代码实现js调用android
245 0
|
编解码 开发工具 Android开发
Android平台如何实现外部RTSP|RTMP流注入轻量级RTSP服务模块(内网RTSP网关)
今天分享的是外部RTSP或RTMP流,拉取后注入到本地轻量级RTSP服务模块,供内网小并发场景下使用,这里我们叫做内网RTSP网关模块。
191 0
|
12月前
|
Java Android开发
程序与技术分享:Android使用Dagger注入的方式初始化对象的简单使用
程序与技术分享:Android使用Dagger注入的方式初始化对象的简单使用
220 0
|
安全 网络安全 API
kotlin安卓开发JetPack Compose 如何使用webview 打开网页时给webview注入cookie
在Jetpack Compose中使用WebView需借助AndroidView。要注入Cookie,首先在`build.gradle`添加WebView依赖,如`androidx.webkit:webkit:1.4.0`。接着创建自定义`ComposableWebView`,通过`CookieManager`设置接受第三方Cookie并注入Cookie字符串。最后在Compose界面使用这个自定义组件加载URL。注意Android 9及以上版本可能需要在网络安全配置中允许第三方Cookie。
1190 0
|
Android开发
android|Magisk注入Zygisk的过程
android|Magisk注入Zygisk的过程
1213 1
 android|Magisk注入Zygisk的过程
|
监控 Android开发
【Android 逆向】函数拦截 ( GOT 表拦截 与 插桩拦截 | 插桩拦截简介 | 插桩拦截涉及的 ARM 和 x86 中的跳转指令 )
【Android 逆向】函数拦截 ( GOT 表拦截 与 插桩拦截 | 插桩拦截简介 | 插桩拦截涉及的 ARM 和 x86 中的跳转指令 )
201 0
【Android 逆向】函数拦截 ( GOT 表拦截 与 插桩拦截 | 插桩拦截简介 | 插桩拦截涉及的 ARM 和 x86 中的跳转指令 )
|
存储 Android开发
【Android 逆向】函数拦截 ( GOT 表数据结构分析 | 函数根据 GOT 表进行跳转的流程 )
【Android 逆向】函数拦截 ( GOT 表数据结构分析 | 函数根据 GOT 表进行跳转的流程 )
191 0
【Android 逆向】函数拦截 ( GOT 表数据结构分析 | 函数根据 GOT 表进行跳转的流程 )
|
存储 缓存 Java
【Android 逆向】函数拦截 ( 使用 cache_flush 系统函数刷新 CPU 高速缓存 | 刷新 CPU 高速缓存弊端 | 函数拦截推荐时机 )
【Android 逆向】函数拦截 ( 使用 cache_flush 系统函数刷新 CPU 高速缓存 | 刷新 CPU 高速缓存弊端 | 函数拦截推荐时机 )
210 0
【Android 逆向】函数拦截 ( 使用 cache_flush 系统函数刷新 CPU 高速缓存 | 刷新 CPU 高速缓存弊端 | 函数拦截推荐时机 )
|
Android开发
【Android 逆向】函数拦截原理 ( 通过修改 GOT 全局偏移表拦截函数 | 通过在实际被调用的函数中添加跳转代码实现函数拦截 )
【Android 逆向】函数拦截原理 ( 通过修改 GOT 全局偏移表拦截函数 | 通过在实际被调用的函数中添加跳转代码实现函数拦截 )
259 0
【Android 逆向】函数拦截原理 ( 通过修改 GOT 全局偏移表拦截函数 | 通过在实际被调用的函数中添加跳转代码实现函数拦截 )
|
Java Linux Android开发
【Android 逆向】函数拦截原理 ( 可执行程序基本结构 | GOT 全局偏移表 | 可执行程序函数调用步骤 )
【Android 逆向】函数拦截原理 ( 可执行程序基本结构 | GOT 全局偏移表 | 可执行程序函数调用步骤 )
286 0
【Android 逆向】函数拦截原理 ( 可执行程序基本结构 | GOT 全局偏移表 | 可执行程序函数调用步骤 )

相关实验场景

更多