首页
论坛
课程
招聘
[原创]HoneyBadger实战:虚拟化在调试有反射的so中的使用实例
2023-1-22 17:07 15145

[原创]HoneyBadger实战:虚拟化在调试有反射的so中的使用实例

2023-1-22 17:07
15145

概述

本文演示如何使用HoneyBadger调试so中反射调用Java函数的场景,基本流程不熟悉的小伙伴请移步 虚拟化在调试常规so中的使用及插件开发教程

 

废话不多说,直接开始

正文

Android部分测试Demo源码已上传至github,点我跳转

 

将要测试的反射函数是NativeLib1.java中的TestReflect函数:

1
2
3
4
5
6
7
8
9
10
11
public class NativeLib1 {
    ......
 
    // 测试反射
    public static String TestReflect(String strArg) {
        Log.d("NativeLib1", "TestReflect 函数调用  参数是:" + strArg);
        return "TestReflect结果返回";
    }
    // 将在此native函数调用上面的 TestReflect 函数
    public static native void DoTestReflect();
}

DoTestReflect函数的实现为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_honeybadger_nativelib_NativeLib1_DoTestReflect(JNIEnv *env, jclass clazz) {
    jclass jcTarget = env->FindClass("com/honeybadger/nativelib/NativeLib1");
    if (jcTarget == nullptr) {
        return JNI_FALSE;
    }
    jmethodID mtdTarget = env->GetStaticMethodID(jcTarget, "TestReflect",
                                                 "(Ljava/lang/String;)Ljava/lang/String;");
    if (mtdTarget == nullptr) {
        return JNI_FALSE;
    }
    jstring jsArg = env->NewStringUTF("来自于native的参数");
    jstring jsResult = (jstring) env->CallStaticObjectMethod(jcTarget, mtdTarget, jsArg);
    const char *szResult = env->GetStringUTFChars(jsResult, JNI_FALSE);
    bool bVerify = strcmp(szResult, "ok") == 0;
    env->ReleaseStringUTFChars(jsArg, szResult);
    return bVerify;
}

目标:反射调用时上面的代码能用插件直接实现,如果正确则DoTestReflect返回TRUE,否则返回FALSE。

插件实现

插件源码在这里,不想动手写的直接clone, 跳转到github

  • 一、添加菜单
    找到 PluginExport.cpp 的 GetMenu 函数,添加菜单:
    1
    2
    HBMenuChain *pThird = CreateMenu("调用DoTestReflect", 3, pSecond);
    pSecond->next = pThird;
  • 二、处理分发
    找到 PluginExport.cpp 的 DispatchAction 函数,添加id=3的菜单的分发:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    void DispatchAction(int iActId) {
      switch (iActId) {
          ....
          case 3:{
              CTLOG("准备执行 DoTestReflect...");
              com_honeybadger_nativelib_NativeLib1 *pClass = new com_honeybadger_nativelib_NativeLib1(g_pListener->GetEntry());
              // DoTestReflect 没有参数且为静态函数 故只传env和class
              vector<uint32_t> vecPara = {g_pListener->GetJniEnvAddr(), pClass->GetClassID()};
              map<uint32_t, uint64_t> mapResult;
              g_pListener->ExecJniInThread(true, "Java_com_honeybadger_nativelib_NativeLib1_DoTestReflect", 2, vecPara,
                                           &mapResult);
     
              uint32_t iOut = mapResult[ARM_REG_R0];
              string strOut = "执行 DoTestReflect 结束,结果:" + CPublicUtil::Int2String(iOut);
              CTLOG(strOut.c_str());
              break;
          }
          default: {
              std::cout << "没有实现的函数" << std::flush;
              break;
          }
      }
    }
  • 三、在虚拟的Java类中添加函数DoTestReflect
    找到 com_honeybadger_nativelib_NativeLib1.cpp,在构造函数中添加一行
    1
    InsertMethodId("TestReflect(Ljava/lang/String;)Ljava/lang/String;");
    在com_honeybadger_nativelib_NativeLib1的CallStaticObjectMethodV中添加函数实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    uint32_t com_honeybadger_nativelib_NativeLib1::CallStaticObjectMethodV(void *pHbThis, uint32_t iMtdID) {
      if (m_mapMethodID_Sig.find(iMtdID) != m_mapMethodID_Sig.end()) {
          string strMtdSig = m_mapMethodID_Sig[iMtdID];
          DebuggerInterface *pDebugger = (DebuggerInterface*)pHbThis;
          if (strMtdSig == "TestReflect(Ljava/lang/String;)Ljava/lang/String;") {
              // 读取参数并打印
              uint32_t iArgInstID = pDebugger->ReadJniFuncPara(0); // ReadJniFuncPara的参数是真正函数的参数index
              java_lang_String *pJsArg = (java_lang_String *)m_pEntry->InvokeSearchClass_Inst(iArgInstID);
              string strArgLog = "TestReflect被调用,参数是:" + pJsArg->GetContent();
              CTLOG(strArgLog.c_str());
              // 设置返回的对象
              java_lang_String *pjbPara = new java_lang_String(m_pEntry);
              pjbPara->SetContent("ok");
              return pjbPara->GetInstanceID();
          } else {
              assert(false);
          }
      } else {
          assert(false);
      }
      return 0;
    }

编译、配置HoneyBadger,启动测试,等待自动加载,即so的初始化部分完成,此过程不熟悉的小伙伴请移步 虚拟化在调试常规so中的使用及插件开发教程

 

右键,点击 调用DoTestReflect。

 

切到日志页面

 

目的达成。

结束

本文演示反射的场景的处理,由此可知,几行代码即可完成一个Java类的模拟,在实战中,照猫画虎即可。
谢谢大家。

 

需要联系我的话,在程序的关于中有联系方式。


[2023春季班]《安卓高级研修班(网课)》月薪两万班招生中~

收藏
点赞0
打赏
分享
最新回复 (3)
雪    币: 153
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
万里星河 活跃值 2023-1-24 23:29
2
0
其实这个 怎么说呢 感觉和unidbg差不多 只不过unidbg用的开源虚拟机用Java代码补环境这个用的自研虚拟机用c代码补环境
雪    币: 153
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
万里星河 活跃值 2023-1-24 23:31
3
0
然而真正的痛点 如何准确高效地补环境 好像还是没解决
雪    币: 307
活跃值: 活跃值 (791)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
eightmg 活跃值 2023-1-25 09:57
4
0
万里星河 然而真正的痛点 如何准确高效地补环境 好像还是没解决[em_5]

感谢关注。

mmmm      具体一些呢?

比如在实战中,用unidbg做xxxx这件事,这个过程需要做1.xxx 2.xxxx 3.xxx等,很麻烦,而HoneyBadger同样没有解决这些。

最后于 2023-1-25 10:10 被eightmg编辑 ,原因:
游客
登录 | 注册 方可回帖
返回