Native开发与逆向第一篇-字符串

开发

Android studio新建项目

Android studio新建一个Native C++项目。
默认代码就是调用Native 方法stringFromJNI 返回一个字符串。

public native String stringFromJNI();

C++ 代码

stringFromJNI 函数的代码,默认使用的是静态注册的方式,静态注册是函数格式就是Java_函数路径。
代码很简单,定义一个字符串hello,调用NewStringUTF转成java字符串返回。
注意看参数,java方法是不带参数的,native函数里面有2个参数,一个是JNIEnv ,一个是jobject 。
如何java层是静态方法,那这里第二个参数就不是jobject 而是 jclass了。
参数这个点是逆向的时候需要注意的。
JNI的基础知识感兴趣的自行学习,这里不展开讲了。

extern "C" JNIEXPORT jstring JNICALL
Java_com_mycode_nativehello_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

动态注册

相对静态注册,动态注册的方式函数名字就可以是自定义的。
逆向的话就需要找动态注册的地方,比静态注册稍微安全一点。

jstring xxaa(JNIEnv *env, jobject instance) {
    std::string hello = "Hello from C++ , 这是动态注册";
    return env->NewStringUTF(hello.c_str());
}

jint RegisterNatives(JNIEnv *env) {
    jclass clazz = env->FindClass("com/mycode/nativehello/MainActivity");
    if (clazz == NULL) {
        return JNI_ERR;
    }

    JNINativeMethod methods_MainActivity[] = {
            {"stringFromJNI", "()Ljava/lang/String;", (void *) xxaa}
    };

    return env->RegisterNatives(clazz, methods_MainActivity,
                                sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jint result = RegisterNatives(env);
    return JNI_VERSION_1_6;
}

逆向

ida反编译so

将上面的demo编译好之后apk的lib目录下会生成so文件,分别是arm 和 x86的 32位和64位。
这里主要分析arm64

arm64 汇编

在这里插入图片描述
主要代码就下面三行
ADR指令将内存中的值取到寄存器,这个字符串 ida 已经自动识别出来。
然后是调用std::string::basic_string 和 NewStringUTF ,和源码都能一一对上。

ADR             X1, aHelloFromC ; "Hello from C++"
...
BL              ._ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2B7v170000IDnEEPKc ; std::string::basic_string<decltype(nullptr)>(char const*)
...
BL              ._ZN7_JNIEnv12NewStringUTFEPKc ; _JNIEnv::NewStringUTF(char const*)

F5伪代码

TAB切换到伪代码界面,伪代码里面没有"Hello from C++" 字符串。这是为什么呢?

__int64 __fastcall Java_com_mycode_nativehello_MainActivity_stringFromJNI(_JNIEnv *a1)
{
  const char *v1; // x0
  __int64 v3; // [xsp+18h] [xbp-48h]
  char v5[24]; // [xsp+40h] [xbp-20h] BYREF
  __int64 v6; // [xsp+58h] [xbp-8h]

  v6 = *(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  std::string::basic_string[abi:v170000]<decltype(nullptr)>(); 
  v1 = sub_248A4(v5);
  v3 = _JNIEnv::NewStringUTF(a1, v1);
  std::string::~string(v5);
  _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  return v3;
}

对比下x86的so
x86的so 正常有"Hello from C++" 字符串,对比上面的arm伪代码区别就在于basic_string的参数。
std::string::basic_string[abi:v170000]<decltype(nullptr)>(v4, “Hello from C++”);

__int64 __fastcall Java_com_mycode_nativehello_MainActivity_stringFromJNI(_JNIEnv *a1)
{
  const char *v1; // rsi
  __int64 v3; // [rsp+18h] [rbp-48h]
  char v4[24]; // [rsp+40h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+58h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  std::string::basic_string[abi:v170000]<decltype(nullptr)>(v4, "Hello from C++");
  v1 = sub_23AF0(v4);
  v3 = _JNIEnv::NewStringUTF(a1, v1);
  std::string::~string(v4);
  return v3;
}

回到arm 伪代码界面,按Y确定。就ok了。
伪代码几乎是和源码差不多的,非常方便逆向分析,主要是有些解析的问题。
在这里插入图片描述

__int64 __fastcall Java_com_mycode_nativehello_MainActivity_stringFromJNI(_JNIEnv *a1)
{
  const char *v1; // x0
  __int64 v3; // [xsp+18h] [xbp-48h]
  char v5[24]; // [xsp+40h] [xbp-20h] BYREF
  __int64 v6; // [xsp+58h] [xbp-8h]

  v6 = *(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  std::string::basic_string[abi:v170000]<decltype(nullptr)>(v5, "Hello from C++");
  v1 = sub_248A4(v5);
  v3 = _JNIEnv::NewStringUTF(a1, v1);
  std::string::~string(v5);
  _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  return v3;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android逆向小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值