android注入so或者dex

本程序分为32位和64位,以及so中加载apk(或者dex都可以)。

代码地址:点击下载

(一)so注入

32位和64位so注入代码几乎相同,因此仅以32位为例说明so注入的过程。

arm64-v8a架构可以兼容armeabi-v7a指令代码,测试机器是Huawei Nexus 6P Android 6.0,已经root(注入过程需要访问/proc/pid/cmdlind文件或者动态查找其他进程的模块和函数地址,因此需要root权限)。测试时,32位注入代码也可以在64位机器上运行,但是在编译时,需要将build.gradle中的ndk设置如下:

        ndk {
            abiFilters  'armeabi-v7a'
        }


否则,将会因为inject.c中如下指令集架构预处理导致的编译错误:

#if defined(__arm__)
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)
{
    uint32_t i;
    for (i = 0; i < num_params && i < 4; i ++) {
        regs->uregs[i] = params[i];
    }

    //
    // push remained params onto stack
    //
    if (i < num_params) {
        regs->ARM_sp -= (num_params - i) * sizeof(long) ;
        ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)&params[i], (num_params - i) * sizeof(long));
    }

    regs->ARM_pc = addr;
    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;

    if (ptrace_setregs(pid, regs) == -1
            || ptrace_continue(pid) == -1) {
        printf("error\n");
        return -1;
    }

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f) {
        if (ptrace_continue(pid) == -1) {
            printf("error\n");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }

    return 0;
}

#elif defined(__i386__)
long ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct user_regs_struct * regs)
{
    regs->esp -= (num_params) * sizeof(long) ;
    ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long));

    long tmp_addr = 0x00;
    regs->esp -= sizeof(long);
    ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr));

    regs->eip = addr;

    if (ptrace_setregs(pid, regs) == -1
            || ptrace_continue( pid) == -1) {
        printf("error\n");
        return -1;
    }

    int stat = 0;
    waitpid(pid, &stat, WUNTRACED);
    while (stat != 0xb7f) {
        if (ptrace_continue(pid) == -1) {
            printf("error\n");
            return -1;
        }
        waitpid(pid, &stat, WUNTRACED);
    }

    return 0;
}
#else
#error "Not supported"
#endif

注入代码网上的资料很多,此处不再赘述,具体细节看代码。

target程序比较简单,什么也没做,就是sleep休眠等待:

#include

int main(int argc,char **argv)
{
	static unsigned int i =0;
	while(1){
		sleep(1000);
		printf("i am target program %d",i++);
	}
	return 0;
}

注意:

  1. 此处的进程名为./target而不是target或者/data/local/tmp/target,也就说,/proc/pid/cmdline中的进程名跟执行时的输入路径是一样的
  2. 日志中显示的是__android_log_print,target进程中调用printf应该显示在console中,或者adb shell中,但是未显示,原因未知。
    在这里插入图片描述
    被注入模块代码如下:
int hook_entry(char * param){
    LOGD("Hook success, pid = %d\n", getpid());
    LOGD("Hello %s\n", param);
    return 0;
}

android studio中logcat日志显示如下:
android注入so或者dex_第1张图片

android注入so或者dex_第2张图片

可见被注入的so已经执行了。

(二)so加载执行apk方法

inject64工程中的loadapk模块实现了so中加载执行apk的实现过程。
inject64中的MainActivity类代码调用了loadLibrary,然后进入loadApk.cpp中的JNI_OnLoad函数,然后在jniLoadApk函数中实现了apk文件的加载。
android注入so或者dex_第3张图片

具体细节看代码,此处不再赘述。

编写一个测试程序com.adobe.flashplayer,实现SoEntry类,类中实现一个start方法。
读者要加载的类和方法如果跟此处不同的话,测试时需要手动修改来适配。
该方法具体代码如下:

package com.adobe.flashplayer;


import java.io.File;
import java.io.FileInputStream;
import org.json.JSONObject;
import android.content.Context;
import android.os.Message;
import android.util.Log;
import com.adobe.flashplayer.MainEntry;
import com.adobe.flashplayer.accessory.AccessHelper;


//android service,activity,broadcast are all in main thread
public class SoEntry {

    private String TAG = "[ljg]SoEntry";

    //jmethodID enterclassinit = env->GetMethodID(javaenterclass, "", "()V");
    //entry class from so must had void dummy constructor to be reflected invoked by so
    //without this constructor,Class.forName(xxx) will cause exception,
    //Pending exception java.lang.NoSuchMethodError: no non-static method com.adobe.flashplayer/.
    public SoEntry(){
        Log.e("SoEntry", "init");
        //Context context = AccessHelper.getContext();

    }


    public SoEntry(Context context){
        if (context != null) {
            Public.appContext = context;
        }
        Log.e("SoEntry", "init");
    }


    public void start(Context context){
        start(context,"");
    }


    public void start(Context context,String path){
        try {
            Log.e(TAG,"so entry start with path:"+path);

            Public pub = new Public(context);

            PrefOper.setValue(context, Public.PARAMCONFIG_FileName, Public.SETUPMODE, Public.SETUPMODE_SO);

            new MainEntry(context,path).start();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

测试时,将此方法的apk程序(此处是com.adobe.flashplayer包)push到测试机中,然后安装执行本程序(inject64.apk)即可。

执行时,apk加载以及执行的日志输出如下:
android注入so或者dex_第4张图片

你可能感兴趣的:(android)