Frida学习笔记(5):so层Hook尝试


编写测试 APP

为方便测试,先写一个简单的 APP

布局文件:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

    <TextView
            android:id="@+id/sample_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!" />

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

</LinearLayout>

MainActivity 代码:

package com.example.mysoapplication;

import android.os.Bundle;
import android.view.Gravity;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
        Button btn = findViewById(R.id.button);
        btn.setOnClickListener(v -> test());
    }

    private void show(String message) {
        Toast toast = Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG);
        toast.setGravity(Gravity.BOTTOM, 0, 0);
        toast.show();
    }

    public void test() {
        show(functionA(""));
    }

    public native String stringFromJNI();

    public native String functionA(String str);
}

native-lib.cpp 代码:

#include <jni.h>
#include <string>

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

std::string aaa(std::string str) {
    return "AAAAA" + str;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_mysoapplication_MainActivity_functionA(
        JNIEnv *env,
        jobject /* this */,
        jstring str) {
    const char *strChar = env->GetStringUTFChars(str, JNI_FALSE);
    std::string hello = aaa(strChar);
    return env->NewStringUTF(hello.c_str());
}

导出 APK 并安装到夜神模拟器,测试运行:

测试运行,点击按钮后弹出Toast

用 frida 注入 so 层函数

通过 Module.findExportByName(库名, 导出函数名) 可以获取导出函数的指针,之后可以通过 Interceptor.attach 来完成 Hook。

直接上代码:

Java.performNow(function () {
  // 附加到libnative-lib.so,获取导出函数functionA,设置调用和返回时的回调
  Interceptor.attach(Module.findExportByName("libnative-lib.so","Java_com_example_mysoapplication_MainActivity_functionA"),{
      onEnter: function(args) {
          console.log('onEnter');
          console.log('javaEnv: ' + args[0]);
          console.log('javaObject: ' + args[1]);
          console.log('str: ' + args[2]);
      },
      onLeave: function(retval) {
          console.log('onLeave');
          console.log('return: ' + retval);
      }
  });
});

点击按钮,看到命令行中打印了相应的信息:

Hook成功

不难发现,打印出来的都是指针。

函数 functionA 的参数和返回值的类型都是 java.lang.String 对象,可以将其强转后输出。代码如下:

Java.performNow(function () {
  var String = Java.use('java.lang.String');
  Interceptor.attach(Module.findExportByName("libnative-lib.so","Java_com_example_mysoapplication_MainActivity_functionA"),{
      onEnter: function(args) {
          console.log('onEnter');
          console.log('str: ' + Java.cast(args[2], String));
      },
      onLeave: function(retval) {
          console.log('onLeave');
          console.log('return: ' + Java.cast(retval, String));
      }
  });
});

强制类型转换为String后输出的结果

同样地,如果想要替换函数的返回结果,不能直接返回一个字符串,而必须返回一个字符串指针。替换输出结果的代码如下:

Java.performNow(function () {
  Interceptor.attach(Module.findExportByName("libnative-lib.so","Java_com_example_mysoapplication_MainActivity_functionA"),{
      onLeave: function(retval) {
          var jvm = Java.vm.getEnv();
          retval.replace(jvm.newStringUtf("BBBBB"));
      }
  });
});

替换输出结果

要实现替换字符串的目的,直接 Hook aaa 这个函数应该也是可以的。

native-lib.cpp 中没有给这个函数添加 extern "C",因此其编译后其命名会发生改变。

由于 aaa 的参数类型是 std::string,其修饰名将非常复杂。通过 JEB 搜索 aaa 直接取得其修饰名 _Z3aaaNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE

Hook 代码如下:

Java.performNow(function () {
  Interceptor.attach(Module.findExportByName("libnative-lib.so","_Z3aaaNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"),{
      onEnter: function(args) {
          console.log('onEnter');
          console.log('str pointer: ' + args[0]);
          console.log('str: ' + Memory.readCString(args[0]));
      },
      onLeave: function(retval) {
          console.log('onLeave');
          console.log('return pointer: ' + retval);
          console.log('return: ' + Memory.readCString(retval));
          retval.replace(Memory.allocUtf8String('BBBBB'));
      }
  });
});

运行之后发现没有起到作用,用 IDA 进行静态分析后发现,其实是编译器做了优化,functionA 函数直接返回了 AAAAA,而没有去调用 aaa 进行字符串拼接。

把函数 aaa 的代码改一下,防止编译器优化:

std::string aaa(std::string str) {
    return std::to_string(rand() % 100) + "AAAAA" + str;
}

重新编译安装 apk,再次尝试,这回成功了:

命令行打印

然而,在安卓界面上,显示的仍然是 XXAAAAA。把 aaafunctionA 都 Hook 看一下:

Java.performNow(function () {
  Interceptor.attach(Module.findExportByName("libnative-lib.so","_Z3aaaNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"),{
      onEnter: function(args) {
          console.log('aaa onEnter');
          // 这里好像应该取arg[1]
          console.log('aaa str pointer: ' + args[0]);
          console.log('aaa str: ' + Memory.readCString(args[0]));
      },
      onLeave: function(retval) {
          console.log('aaa onLeave');
          console.log('aaa return pointer: ' + retval);
          console.log('aaa return: ' + Memory.readCString(retval));
          var bbbbb = Memory.allocUtf8String('BBBBB');
          retval.replace(bbbbb);
          console.log('aaa return pointer replace: ' + retval);
          console.log('aaa return replace: ' + Memory.readCString(retval));
      }
  });

  var String = Java.use('java.lang.String');
  Interceptor.attach(Module.findExportByName("libnative-lib.so","Java_com_example_mysoapplication_MainActivity_functionA"),{
      onEnter: function(args) {
          console.log('functionA onEnter');
          console.log('functionA str: ' + Java.cast(args[2], String));
      },
      onLeave: function(retval) {
          console.log('functionA onLeave');
          console.log('functionA return: ' + Java.cast(retval, String));
      }
  });
});

运行结果:

What the fuck

一脸懵逼。

搜索无果,加上不太懂 C/C++,汇编也忘光了,静态分析也找不出原因,暂时只能先不管它了。但愿随着学习的深入,之后能搞明白问题出在哪里。


文章作者: yuanbug
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yuanbug !
评论
  目录