29.安卓逆向2-frida hook技术-逆向os文件(二)IDA工具下载和使用(利用ai分析so代码)

发布于:2025-07-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

内容参考于:图灵Python学院

工具下载:

链接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwd=zy89

提取码:zy89

复制这段内容后打开百度网盘手机App,操作更方便哦

上一个内容:28.安卓逆向2-frida hook技术-逆向os文件(一)

ida不同版本反编译的伪代码有点差异,下方是ida7.0版本,它反编译的伪代码没有ida9.0好

ida9.0安装看这个大哥写的:https://mrx.hk/posts/f66fa9e6643dec79c46f35361986b601

上一个内容通过找sign参数,发现它的加密是在so文件中,然后又去找了so文件,然后so文件使用c/++写的,然后就需要一个工具对它进行逆向,工具叫做IDA,接下来就开始下载和使用ida

地址:https://52pojie.cn/thread-1874203-1-1.html

注意要下载,后面带Hex-Rays的,这表示有Hex-Rays插件,这个插件可以生成伪代码

下载完后双击下图红框

双击之后,然后点击Next

然后继续Next

继续Next

Password是qY2jts9hEJGy,继续Next

然后选择一个安装目录,选择完点击Next

然后创建桌面图标,然后Next

然后Install

等待安装完成

安装完成,勾选LaunchIDA然后点击Finish

 它的图标,32-bit是用来分析32位的so文件,64位的是用来分析64位的so文件,如果用32位的查看64位的会打不开

然后点击下图红框

点击Finish后

image-20250713163716380

然后点击ok

然后点击go

然后就打开了

然后把so文件拖到上图的窗口中后,如下图直接点ok,用默认的就可以

然后就把文件反编译好了

然后按空格可以展示成下图的样子

如果上图中的窗口不小关闭了,可以在下图位置找

如果窗口乱了

可以点击下图Reset desktop位置恢复

上一个内容中调用了wtf文件中的getSign方法,接下来找一下这个方法,鼠标左键单机下图红框位置

然后按CTRL+F,就会出现下图红框的东西

然后输入getSign,如下图就找到了getSign方法

然后双击下图红框位置就能跳转过去了

双击之后进入getSign方法里

然后按F5,就给它编程成了,c语言代码,注意如果下载的ida没有 Hex-Rays 插件这一步没办法做到,所以一定要下载带 Hex-Rays 插件的

然后开始分析,分析直接给ai,让ai写分析,分析完注释,这里的ai用的豆包,也可以使用ai把下方的代码还原成Python代码

/**
 * JNI函数:Java_cn_thecover_lib_common_manager_SignManager_getSign
 * 功能:生成API请求签名(带入参数:account="accccc", token="", timestamp="1235")
 * 核心逻辑:双重MD5加密 + 参数拼接,最终返回大写签名
 */
jstring __fastcall Java_cn_thecover_lib_common_manager_SignManager_getSign(JNIEnv *env, jclass obj, jstring account, jstring token, jstring timestamp)
{
  // 输入参数固定值:
  // account = "accccc"(用户账号)
  // token = ""(令牌,此处为空字符串)
  // timestamp = "1235"(时间戳)

  jstring v5; // 最终返回的签名字符串
  _jstring *v6; // 从Java获取的应用签名(假设为"appSecret123")
  char *v7; // 应用签名的UTF-8格式("appSecret123")
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char> >::value_type *__rhs; // 账号的UTF-8值("accccc")
  char *v9; // 令牌的UTF-8值("")
  char *v10; // 时间戳的UTF-8值("1235")
  JNIEnv *v11; // 暂存JNI环境指针
  const char *v12; // 最终签名的C字符串
  _jmethodID *v13; // 静态方法getAppSign的ID
  _jclass *v14; // Java类LogShutDown的引用
  jstring v15; // 暂存timestamp参数("1235")
  jstring v16; // 暂存token参数("")
  jstring v17; // 暂存account参数("accccc")
  _jstring *v18; // 临时生成的Java字符串
  char v19; // 待加密字符串的临时副本
  char v20; // 第二次MD5加密的结果
  std::__ndk1::string result; // 最终签名(第二次MD5大写结果)
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char> > __lhs; // 第一步拼接结果
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char> > v23; // 第二步拼接结果
  std::__ndk1::string encrypt; // 最终待加密的完整字符串
  char v25; // 应用签名的临时存储("appSecret123")
  char v26; // 应用签名的MD5结果(假设为"9A0A82F06B516634C4394A7E9072264D")
  std::__ndk1::string appsignMd5result; // 应用签名MD5的最终值("9A0A82F06B516634C4394A7E9072264D")
  JNIEnv *enva; // 暂存JNI环境指针
  __int64 v29; // 栈保护Cookie(防溢出)

  // 保存栈Cookie(用于函数结束时校验栈完整性)
  v29 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  enva = env;
  v17 = account;
  v16 = token;
  v15 = timestamp;
  
  // 步骤1:查找Java类LogShutDown(用于获取应用签名)
  v14 = (_jclass *)_JNIEnv::FindClass(env, "cn/thecover/lib/common/utils/LogShutDown");
  
  if (v14) // 类查找成功
  {
    // 步骤2:获取类中静态方法getAppSign()的ID(方法签名:()Ljava/lang/String;)
    v13 = (_jmethodID *)_JNIEnv::GetStaticMethodID(enva, v14, "getAppSign", "()Ljava/lang/String;");
    
    if (v13) // 方法ID获取成功
    {
      // 步骤3:调用静态方法获取应用签名(假设返回"appSecret123")
      v6 = (_jstring *)_JNIEnv::CallStaticObjectMethod(enva, v14, v13);
      
      // 步骤4:将Java字符串转为C的UTF-8格式("appSecret123")
      v7 = (char *)_JNIEnv::GetStringUTFChars(enva, v6, 0LL);
      
      // 步骤5:将应用签名存入C++字符串(v25 = "appSecret123")
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string<decltype(nullptr)>(
        &v25, v7);
      
      // 步骤6:对应用签名做MD5加密(v26 = MD5("appSecret123") = "9A0A82F06B516634C4394A7E9072264D")
      md5((const std::__ndk1::string *)&v25, (std::__ndk1::string *)&v26);
      
      // 步骤7:MD5结果转大写(保持"9A0A82F06B516634C4394A7E9072264D",因MD5本身大写)
      toUpperCase(&v26);
      // ######################## ida问题说明 ########################
      // 这里应该有一步向 appsignMd5result 中添加v26的数据,这应该由于ida的问题给省略了,这个是问ai没有看到appsignMd5result赋值
      // 到后面 appsignMd5result 怎么就等于 9A0A82F06B516634C4394A7E9072264D了
      // 然后结合这里的代码很奇怪,上方调用getAppSign方法得到了一个数据,然后进行了MD5加密,加密完紧着就调用basic_string释放了内存
      // 并没有使用这个数据,这就很不正常,这里就可能在代码编译的时候进行了优化,然后ida反编译的时候就出错了
      // ######################## ida问题说明 ########################
      // 步骤8:释放临时变量(v25、v26完成使命)
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v26);
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v25);
      
      // 步骤9:将输入参数转为C的UTF-8格式
      __rhs = (std::__ndk1::basic_string::value_type *)_JNIEnv::GetStringUTFChars(enva, v17, 0LL); // __rhs = "accccc"(account)
      v9 = (char *)_JNIEnv::GetStringUTFChars(enva, v16, 0LL); // v9 = ""(token)
      v10 = (char *)_JNIEnv::GetStringUTFChars(enva, v15, 0LL); // v10 = "1235"(timestamp)
      
      // ######################## 核心拼接过程 ########################
      // 规则:appsignMd5result(应用签名MD5) + account + token + timestamp
      
      // 拼接步骤1:appsignMd5result + account
      // 输入:appsignMd5result = "9A0A82F06B516634C4394A7E9072264D",__rhs = "accccc"
      // 输出:__lhs = "9A0A82F06B516634C4394A7E9072264Daccccc"
      std::__ndk1::operator+<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>(
        &__lhs, &appsignMd5result, __rhs);
      
      // 拼接步骤2:上一步结果 + token
      // 输入:__lhs = "9A0A82F06B516634C4394A7E9072264Daccccc",v9 = ""
      // 输出:v23 = "9A0A82F06B516634C4394A7E9072264Daccccc"(因token为空,结果不变)
      std::__ndk1::operator+<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>(
        &v23, &__lhs, (const unsigned __int8 *)v9);
      
      // 拼接步骤3:上一步结果 + timestamp
      // 输入:v23 = "9A0A82F06B516634C4394A7E9072264Daccccc",v10 = "1235"
      // 输出:encrypt = "9A0A82F06B516634C4394A7E9072264Daccccc1235"(最终待加密字符串)
      std::__ndk1::operator+<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>(
        &encrypt, &v23, (const unsigned __int8 *)v10);
      // ######################## 拼接结束 ########################
      
      // 步骤10:释放拼接过程的临时变量(__lhs、v23完成使命)
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v23);
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&__lhs);
      
      // 步骤11:将待加密字符串复制到临时变量(v19 = encrypt = "9A0A82F06B516634C4394A7E9072264Daccccc1235")
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(
        &v19, &encrypt);
      
      // 步骤12:对完整字符串做第二次MD5加密(v20 = MD5("9A0A82F06B516634C4394A7E9072264Daccccc1235"))
      md5((const std::__ndk1::string *)&v19, (std::__ndk1::string *)&v20);
      
      // 步骤13:第二次MD5结果转大写(最终签名值)
      toUpperCase(&v20);
      
      // 步骤14:释放临时变量(v19、v20完成使命)
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v20);
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v19);
      
      // 步骤15:释放输入参数的UTF-8资源(避免内存泄漏)
      _JNIEnv::ReleaseStringUTFChars(enva, v17, (const char *)__rhs); // 释放account
      _JNIEnv::ReleaseStringUTFChars(enva, v16, v9); // 释放token
      _JNIEnv::ReleaseStringUTFChars(enva, v15, v10); // 释放timestamp
      _JNIEnv::ReleaseStringUTFChars(enva, v6, v7); // 释放应用签名
      
      // 步骤16:删除JNI本地引用(释放类和方法的引用)
      _JNIEnv::DeleteLocalRef(enva, &v6->0);
      _JNIEnv::DeleteLocalRef(enva, &v14->0);
      
      // 步骤17:准备返回结果(result = 最终签名值)
      v11 = enva;
      v12 = (const char *)std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::c_str(&result);
      
      // 步骤18:将C字符串转为Java的jstring类型
      v18 = (_jstring *)_JNIEnv::NewStringUTF(v11, v12);
      
      // 步骤19:释放剩余临时变量
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&result);
      std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&encrypt);
      v5 = (jstring)std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&appsignMd5result);
    }
    else // 方法ID获取失败
    {
      _JNIEnv::ExceptionDescribe(enva); // 打印异常
      _JNIEnv::ExceptionClear(enva); // 清除异常状态
      __android_log_print(4LL, "theCover", "find md5mtd error"); // 日志提示方法查找失败
      v5 = (jstring)_JNIEnv::NewStringUTF(enva, "wtf"); // 返回错误标识
      v18 = v5;
    }
  }
  else // 类查找失败
  {
    __android_log_print(4LL, "theCover", "find class error"); // 日志提示类查找失败
    _JNIEnv::ExceptionDescribe(enva);
    _JNIEnv::ExceptionClear(enva);
    v5 = (jstring)_JNIEnv::NewStringUTF(enva, "wtf"); // 返回错误标识
    v18 = v5;
  }
  
  // 校验栈完整性(若栈未被破坏,返回v18;否则返回异常值)
  if (*(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40) == v29)
    v5 = v18;
  
  return v5; // 返回最终签名(或错误标识"wtf")
}

到这就把这个方法分析了,然后接下来就开始使用fridahook它们


img


网站公告

今日签到

点亮在社区的每一天
去签到