本程序分为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 *)¶ms[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<stdio.h>
int main(int argc,char **argv)
{
static unsigned int i =0;
while(1){
sleep(1000);
printf("i am target program %d",i++);
}
return 0;
}
注意:
- 此处的进程名为./target而不是target或者/data/local/tmp/target,也就说,/proc/pid/cmdline中的进程名跟执行时的输入路径是一样的
- 日志中显示的是__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日志显示如下:
可见被注入的so已经执行了。
(二)so加载执行apk方法
inject64工程中的loadapk模块实现了so中加载执行apk的实现过程。
inject64中的MainActivity类代码调用了loadLibrary,然后进入loadApk.cpp中的JNI_OnLoad函数,然后在jniLoadApk函数中实现了apk文件的加载。
具体细节看代码,此处不再赘述。
编写一个测试程序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, "<init>", "()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/.<init>
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加载以及执行的日志输出如下: