Android中进程注入(二)

前言

接下来我们就要实现一个向目标进程注入so的函数,并使用ndk交叉编译代码让他能在我们arm手机上运行

环境

nexus5
Android4.4
ndk11

所有文件我都放到git上了
https://github.com/bigGreenPeople/android_inject.git

代码实现

注入远程进程so函数

int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name,
                          const char *param, size_t param_size) {
    int ret = -1;
    void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;
    void *local_handle, *remote_handle, *dlhandle;
    uint8_t *map_base = 0;
    uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;

    struct pt_regs regs, original_regs;
    extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
 _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
 _saved_cpsr_s, _saved_r0_pc_s;

    uint32_t code_length;
    long parameters[10];

    DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);
    //1.首先挂载到目标进程
    if (ptrace_attach(target_pid) == -1)
        goto exit;
    //2.读取目标进程寄存器数据
    if (ptrace_getregs(target_pid, &regs) == -1)
        goto exit;

    /*3.保存原来的寄存器数据*/
    memcpy(&original_regs, &regs, sizeof(regs));
    //获取目标进程mmap函数的地址
    mmap_addr = get_remote_addr(target_pid, libc_path, (void *) mmap);
     //获取目标进程 dlopen 函数的地址
    dlopen_addr = get_remote_addr(target_pid, linker_path, (void *) dlopen);
    //获取目标进程 dlsym 函数的地址
    dlsym_addr = get_remote_addr(target_pid, linker_path, (void *) dlsym);
    //获取目标进程 dlclose 函数的地址
    dlclose_addr = get_remote_addr(target_pid, linker_path, (void *) dlclose);
    //获取目标进程 dlerror 函数的地址
    dlerror_addr = get_remote_addr(target_pid, linker_path, (void *) dlerror);

    DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
                dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);
    DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);

        //4.使用mmap函数分配字符串内存
    /* call mmap 准备mmap参数 这里分配0x4000大小的内存*/
    parameters[0] = 0;  // addr
    parameters[1] = 0x4000; // size
    parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC;  // prot
    parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
    parameters[4] = 0; //fd
    parameters[5] = 0; //offset

    //在目标进程中调用mmap函数
    if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, &regs) == -1)
        goto exit;

    map_base = ptrace_retval(&regs);
   

    printf("library path = %s\n", library_path);
    //5.往目标进程写入library_path中的字符串
    ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);

    parameters[0] = map_base;
    parameters[1] = RTLD_NOW | RTLD_GLOBAL;
    //6.让目标进程调用 dlopen 函数
    if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, &regs) == -1)
        goto exit;

    void *sohandle = ptrace_retval(&regs);
        //7.往目标进程写入function_name中的字符串 (这里分配到0x100后面 一般上面的字符串不会超过)
#define FUNCTION_NAME_ADDR_OFFSET       0x100
    ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name,
                     strlen(function_name) + 1);
    parameters[0] = sohandle;
    parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET;
    //8.让目标进程调用 dlsym 函数(得到我们需要调用的函数地址)
    if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, &regs) == -1)
        goto exit;

    void *hook_entry_addr = ptrace_retval(&regs);
    DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr);
        //9.为调用的函数参数,拷贝字符串 (这里分配到0x200后面 一般上面的字符串不会超过)
#define FUNCTION_PARAM_ADDR_OFFSET      0x200
    ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1);
    parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET;

    //10.让目标进程调用hook_entry 函数
    if (ptrace_call_wrapper(target_pid, function_name, hook_entry_addr, parameters, 1, &regs) == -1)
        goto exit;

    printf("Press enter to dlclose and detach\n");


    /* 11.恢复寄存器*/
    ptrace_setregs(target_pid, &original_regs);
    //12.卸载目标进程
    ptrace_detach(target_pid);
    ret = 0;

exit:
    return ret;
}

这些都是之前封装好的函数,这里就实现了远程加载so。大致的步骤如下:
1.首先挂载到目标进程
2.读取目标进程寄存器数据
3.保存原来的寄存器数据
4.使用mmap函数分配字符串内存
5.往目标进程写入library_path中的字符串
6.让目标进程调用 dlopen 函数
7.往目标进程写入function_name中的字符串
8.让目标进程调用 dlsym 函数
9.为调用的函数参数,拷贝字符串
10.让目标进程调用hook_entry 函数
11.恢复寄存器
12.卸载目标进程

最后写一个main函数调用,让目标进程加载我们自己写的so

int main(int argc, char **argv) {
    pid_t target_pid;
    target_pid = find_pid_of(
            "com.shark.initapp");
    if (-1 == target_pid) {
        printf("Can't find the process\n");
        return -1;
    }
    printf("target_pid=%d argc=%d\n ", target_pid, argc);
    char *sopath = "/data/local/tmp/inject_shark.so";
    if (argc > 1) {
        sopath = argv[1];
    }
    char *main_entry = "main_entry";
    if (argc > 2) {
        main_entry = argv[2];
    }
    char *parameter = "parameter";
    if (argc > 3) {
        parameter = argv[3];
    }

    printf("inject_remote_process start\n");
    inject_remote_process(target_pid, sopath, main_entry, parameter, strlen(parameter));
    return 0;
}

inject_shark.so的代码如下

#include <jni.h>
#include <string>
#include<android/log.h>

extern "C" JNIEXPORT void main_entry(char * parameter){
    __android_log_print(ANDROID_LOG_ERROR, "SharkChilli",
                        "main_entry");
}

使用as将其编译成so后拷贝到/data/local/tmp/inject_shark.so

ndk编译

操作步骤可以参考我以前的文章NDK 编译可执行程序( 独立编译)(一)

这里我就直接贴上mk文件了

Android.mk

# 一个Android.mk file首先必须定义好LOCAL_PATH变量。
# 它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’, 
# 由编译系统提供,用于返回当前路径(即包含Android.mk file文件的目录)。
LOCAL_PATH := $(call my-dir)
# CLEAR_VARS由编译系统提供,
# 指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。这是必要的,
# 因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
include $(CLEAR_VARS)
# LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。
# 注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。
LOCAL_MODULE    := shark_inject
# 加入日志库
LOCAL_LDLIBS    :=  -llog
# LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,
# 因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。
LOCAL_SRC_FILES := shark_inject.c
# BUILD_EXECUTABLE 表示以一个可执行程序的方式进行编译
# BUILD_SHARED_LIBRARY 表示动态链接库的方式进行编译
include $(BUILD_EXECUTABLE)

Application.mk

#### 0APP_ABI := arm64-v8a 后面接的是需要生成的.so平台文件,
#### 正常手机使用arm64处理器的即可。 all是全平台
#### APP_PLATFORM :=后面接的是使用SDK的最低等级
APP_ABI := armeabi-v7a

在文件的父目录使用ndk进行编译,警告可以不用理睬。
将libs/armeabi-v7a目录下的shark_inject文件拷贝到手机上

adb push .\shark_inject /data/local/tmp/

打开com.shark.initapp,给予shark_inject 权限。

运行

 # ./shark_inject
target_pid=26833 argc=1
 inject_remote_process start
library path = /data/local/tmp/inject_shark.so
Press enter to dlclose and detach

ddms中的日志信息


image.png

可以看到我们成功调用了so中的main_entry函数

使用objection查看目标进程加载的so库


image.png

可以看到我们的so确实被加载了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容