首页
论坛
课程
招聘
[原创]Android Hook 系列教程(二) 自己写APK实现Hook Java层函数
2018-9-28 15:30 9291

[原创]Android Hook 系列教程(二) 自己写APK实现Hook Java层函数

2018-9-28 15:30
9291

章节内容

一. Android Hook 系列教程(一) Xposed Hook 原理分析
二. Android Hook 系列教程(二) 自己写APK实现Hook Java层函数
三. Android Hook 系列教程(三) Cydia Hook Native 原理分析
四. Android Hook 系列教程(四) 自己写APK实现Hook Native层函数
五. Android Hook 系列教程(五) 更多Hook方法
六. Andoird Hook 系列教程(六) Hook的总结

 

根据上一节课的内容现在我们写一个简单的DEMO,既然是DEMO,我认为代码要尽量的少,能尽量的说明问题,不需要太多的兼容性和扩展性,所以并不打算实现多么复杂的框架.

 

话不多说,建议先下载附件查看代码(附件在后面).

具体步骤

  1. 使用Android新建一个NDK工程
  2. 编写java代码
  3. 编写c++代码
  4. 结果验证

1.使用Android新建一个NDK工程

我使用的是最新版的Android Studio

 

 

因为目前该例子只支持32位,所以需要更改app/build.gradle只生成32位的so文件,不然编译会出错(指针转换).

android {
    compileSdkVersion 28

    defaultConfig {
...
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'armeabi-v7a'
            }
        }
    }
...
}

2.编写java代码

编写HookModule.java

 

该类非常简单,就是获取方法的DeclaringClass和slot字段,然后传入Native层进行Hook.
主要方法:HookJavaMethod

import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;

public class HookModule {
    public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        try {
            Method method=clazz.getDeclaredMethod(methodName,parameterTypes);
            method.setAccessible(true);
            return method;
        } catch (Exception e) {
            return null;
        }
    }
    public static Field findFieLd(Class<?> clazz,String fieldName)
    {
        Field fid=null;
        try {
            fid=clazz.getDeclaredField(fieldName);
        }catch (Exception e){
            while (true) {
                clazz = clazz.getSuperclass();
                if (clazz == null || clazz.equals(Object.class))
                    break;

                try {
                    fid=clazz.getDeclaredField(fieldName);

                } catch (Exception ignored) {}
            }
        }
        return  fid;
    }

    public static void HookJavaMethod(Class<?> clazz,String methodName,Class<?>... parameterTypes)
    {
        int slot;
        Method hookMethod=findMethodExact(clazz,methodName,parameterTypes);//获取反射方法
        Class<?> declaringClass=hookMethod.getDeclaringClass();
        try{
            Field fid=findFieLd(hookMethod.getClass(),"slot");//获取slot字段
            fid.setAccessible(true);
            slot=fid.getInt(hookMethod);
        }catch (Exception e)
        {
            String msg=e.getMessage();
            return;
        }
        hookMethodNative(declaringClass,slot);//进入Native层Hook
        int a=0;
    }

    private native static void hookMethodNative(Class<?> declaringClass, int slot);
    //public void hookJavaMethodNative();
}
  • 在MainActivity.java中

我们写add函数用于测试Hook
并在onCreate时进行hook

public class MainActivity extends AppCompatActivity {
    public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

        add(7,7,"Add Result:");//调用

    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    //要Hook的函数
    public void add(int a,int b,String Tip)
    {
        Log.d("HookJava", Tip+a+"+"+b+"="+(a+b));
    }
}

3.编写c++代码

前置说明

 

由于dvm函数在Android Studio里面不能直接调用,也没有相应的头文件.

 

所以我通过如下方法使用:

  1. 导入Dalivk虚拟机源代码的头文件,这些我已经做好,并打包在项目中(过程中遇到一些无法识别的符号,我直接注销掉,对使用没有任何影响).

  2. dvm函数实在系统的/system/lib/libdvm.so文件中我们通过dlsym动态获取其函数地址

    动态获取函数地址时,使用的是导出名称,而不是原来的名称.
    我们把libdvm.so从/system/lib/libdvm.so拖到电脑.

    adb pull /system/lib/libdvm.so

    用IDA查看:EXPOST 后面才是真正的函数名称

      ![](upload/attach/201809/793792_4HD59VY2G5B7Z83.png)
    

我们在Init函数中动态获取所有需要的dvm函数

//-------------------------------在其他头文件中
Object* dvmDecodeIndirectRef(Thread* self, jobject jobj);
typedef Object* (*PdvmDecodeIndirectRef)(Thread* self, jobject jobj);
PdvmDecodeIndirectRef _dvmDecodeIndirectRef;

Thread* dvmThreadSelf(void);
typedef Thread* (*pdvmThreadSelf)(void);
pdvmThreadSelf _dvmThreadSelf;

Method* dvmSlotToMethod(ClassObject* clazz, int slot);
typedef  Method* (*pdvmSlotToMethod)(ClassObject* clazz, int slot);
pdvmSlotToMethod _dvmSlotToMethod;
//--------------------------------
bool Init()
{
    void *libdvm=dlopen("system/lib/libdvm.so",0);//加载模块地址或获取模块地址
    if(NULL==libdvm)
        return false;
    //由于这些函不能直接得到调用,所以需要动态获取
    _dvmDecodeIndirectRef=(PdvmDecodeIndirectRef)dlsym(libdvm,"_Z20dvmDecodeIndirectRefP6ThreadP8_jobject");
    _dvmThreadSelf=(pdvmThreadSelf)dlsym(libdvm,"_Z13dvmThreadSelfv");
    _dvmSlotToMethod=(pdvmSlotToMethod)dlsym(libdvm,"_Z15dvmSlotToMethodP11ClassObjecti");

    return true;
}

在JNI_Onload动态注册hookMethodNative函数

JNINativeMethod methods[]={
        {"hookMethodNative","(Ljava/lang/Class;I)V",(void *)hookMethodNative},
        {0,0,0}
};
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm,void *r)
{
    Init();
    JNIEnv *env;
    jvm->GetEnv((void **)&env,JNI_VERSION_1_6);
    jclass  jclass1=env->FindClass("com/example/chp/hookjava/HookModule");
    jint i=env->RegisterNatives(jclass1,methods,1);
    LOGD("JNI_OnLoad:%d",i);
    return JNI_VERSION_1_6;

}

前面的一些都是在做铺垫,接下来我们看最主要的函数:hookMethodNative

 

非常的简单,就是

  • 获取ClassObject,
  • 根据slot获取Method,
  • 替换Method的nativeFunc为hookedMethodCallback,-
  • 设置其FLAG为ACC_NATIVE.就完成了Hook
extern "C"
JNIEXPORT void JNICALL
hookMethodNative(JNIEnv *env, jclass type, jobject declaringClass, jint slot) {

    // TODO
    ClassObject *declaredClass=(ClassObject*)_dvmDecodeIndirectRef(_dvmThreadSelf(),declaringClass);
    Method *method=_dvmSlotToMethod(declaredClass,slot);
    SET_METHOD_FLAG(method,ACC_NATIVE);
    method->nativeFunc = &hookedMethodCallback;
    LOGD("Hook 函数 %s 完成",method->name);
}

然后我们看一下hookedMethodCallback

 

由于我们做的是demo,我并不想让问题变得复杂,所以要实现什么功能,就请尽情的发挥你的想象力吧.

void hookedMethodCallback(const u4* args, JValue* pResult, const Method* method, ::Thread* self)
{
    //这是一个参数数组,下标从0开始
    //在这里你可以调用上层函数,想干嘛都是可以的.
    JNIEnv *jniEnv=self->jniEnv;//获取JNIEnv
    //作为演示例子,完成Hook就已经完成了目的
    LOGD("大家好,我是Hook函数");
}

结果验证

我使用的手机是nexus 5.Android 4.4.4 系统.

 

也可以使用模拟器测试.

 

接下来我们看一下运行结果.

 

我点击一下add按钮.

 

 

这个例子是不需要root的.
附件在后面.


[培训] 优秀毕业生寄语:恭喜id咸鱼炒白菜拿到远超3W月薪的offer,《安卓高级研修班》火热招生!!!

最后于 2018-10-29 21:28 被chpeagle编辑 ,原因: 图片放错
上传的附件:
收藏
点赞2
打赏
分享
最新回复 (4)
雪    币: 7162
活跃值: 活跃值 (441)
能力值: ( LV5,RANK:71 )
在线值:
发帖
回帖
粉丝
joker陈 活跃值 2018-9-29 17:28
2
0
支持楼主,先标记下,待楼主写完后可以好好跟着学习下。
雪    币: 99
活跃值: 活跃值 (161)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
飞翔的肥皂 活跃值 2018-10-12 23:22
3
0
mark一下,感谢楼主
雪    币: 32
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
airdian 活跃值 2018-10-13 12:56
4
0
3,4,5,6呢?
雪    币: 35
活跃值: 活跃值 (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wyhacket 活跃值 2019-1-2 13:05
5
0
楼主,你好!我问一下:我怎么在这个回调函数中调用原来的方法呢?
hookedMethodCallback当中?
游客
登录 | 注册 方可回帖
返回