首页 > 其他分享 >学习笔记-Frida反调试思路和hook native

学习笔记-Frida反调试思路和hook native

时间:2022-11-11 09:24:08浏览次数:56  
标签:Log MainActivity hook env Frida com native

用户代码 native hook

  1. Frida反调试与反反调试基本思路(Java层API,Native层API,Syscall)
  2. 六月题的Frida反调试的实现以及
  3. Native函数的Java hook以及主动调用
  4. 静态注册函数参数,返回值打印和替换

0x1 反调试

17种的so的反调试,同样适用于frida,另外三种的话是 1. 遍历连接手机所有端口发送D-bus消息,如果返回”REJECT”这个特征则认为存在frida-server 2. 直接调用openat的syscall的检测在text节表中搜索frida-gadget*.so / frida-agent*.so字符串,避免了hook libc来anti-anti的方法。 https://github.com/b-mueller/frida-detection-demo/blob/master/AntiFrida/app/src/main/cpp/native-lib.cpp 3. 内存中存在frida rpc字符串,认为有frida-server https://github.com/qtfreet00/AntiFrida/blob/master/app/src/main/cpp/detect.cpp

第二种直接通过sycall来进行检测的话,就无法通过hook libc的api进行反反调试了,毕竟已经是从底层入手,用汇编sycall实现一个方法,反反调试的话,只能通过修改内核或者改硬编码了, 这里也给出手写汇编syscall方式反反调试,手写汇编调用syscall的方式,可以在内存(或so)里搜pattern,定位到具体的位置,将此处的调用patch掉。
题外话: 不是手写汇编的方式,直接调用syscall检测frida特征。 想要anti这种反调试的话,可以: 1. 编译frida源码,所有地方改成frita; 2. 编译内核源码,hook openat 的syscall

0x02 native层hook入门

  1. native函数的java hook 其实就是直接把native函数,当成java的函数进行操作,和之前的几乎一样,无论是动态注册还是静态的,一样的可以进行hook和主动调用。 比如这个app:
    java层的是这样的:

package com.example.demoso1;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

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 = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
//Log.i("r0add", String.valueOf(this.myfirstjni()));
//Log.i("r0add", MainActivity.stringFromJNI());

//init();
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("r0addRegistNatives", MainActivity.stringFromJNI2());
Log.i("r0addstatic", MainActivity.stringFromJNI());
Log.i("r0addargs",MainActivity.myfirstjniJNI("pediy1202010"));
}

//testField();
//testMethod();

}

public void testField(){
Class testClazz = null ;
try {
testClazz = MainActivity.class.getClassLoader().loadClass("com.example.demoso1.Test");
Log.i("r0reflection", "Classloader.loadClass->" + testClazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class testClazz2 = null ;
try {
testClazz2 = Class.forName("com.example.demoso1.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Log.i("r0reflection", "Class.forName->"+testClazz2);
Class testClazz3 = Test.class;
Log.i("r0reflection", ".class->"+testClazz3.getName());

try {
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
Log.i("r0reflection", "testClazz3.getDeclaredField->"+publicStaticField_field);
String value = (String)publicStaticField_field.get(null);
Log.i("r0reflection", "publicStaticField_field.get->"+value);

Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
privateStaticField_field.setAccessible(true);
privateStaticField_field.set(null,"modified");
String valuePrivte = (String)privateStaticField_field.get(null);
Log.i("r0reflection", "privateStaticField_field.get->"+valuePrivte);

} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
Field[] fields = testClazz3.getDeclaredFields();
for(Field i :fields){
Log.i("r0reflection", "testClazz3.getDeclaredFields->"+i);
}
Field[] fields_2 = testClazz3.getFields();
for(Field i :fields_2){
Log.i("r0reflection", "testClazz3.getFields->"+i);
}


}


public void testMethod(){
Class testClazz = Test.class;

Method publicStaticFunc_method = null;
try {
publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
Log.i("r0reflection", "testClazz.getDeclaredMethod->"+publicStaticFunc_method);
publicStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}


Method privateStaticFunc_method = null;
try {
privateStaticFunc_method = testClazz.getDeclaredMethod("privateStaticFunc");
Log.i("r0reflection", "testClazz.getDeclaredMethod->"+privateStaticFunc_method);
privateStaticFunc_method.setAccessible(true);
privateStaticFunc_method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}


Method[] methods = testClazz.getMethods();
for(Method i : methods){
Log.i("r0reflection", "testClazz.getMethods()->"+i);
}

Method[] methods2= testClazz.getDeclaredMethods();
for(Method i:methods2){
Log.i("r0reflection", "testClazz.getDeclaredMethods->"+i);
}

};


/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/

public static native String stringFromJNI();
public static native String stringFromJNI2();
public static native String myfirstjniJNI(String context);
public native int myfirstjni();
public native int init();
}

jni层:

#include <jni.h>
#include <pthread.h>
#include <string>
#include <android/log.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define APPNAME "FridaDetectionTest"

#define TAG "r0add"

// 定义info信息

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)

// 定义debug信息

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)

// 定义error信息

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

int r0add(int x,int y){
int i ;
for (i = 0; i<x; i++){
LOGI("now i is %i",i);
i=i+y;
}
return i;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_demoso1_MainActivity_stringFromJNI(
JNIEnv* env,
jclass clazz) {

jclass testClass = env->FindClass("com/example/demoso1/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField = env->GetStaticFieldID(testClass,"publicStaticField","Ljava/lang/String;");
jstring publicStaticField_value = (jstring)env->GetStaticObjectField(testClass,publicStaticField);

const char* value_ptr = env->GetStringUTFChars(publicStaticField_value, nullptr);
LOGI("now content is %s",value_ptr);

std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}



extern "C" JNIEXPORT jstring JNICALL
Java_com_example_demoso1_MainActivity_myfirstjniJNI(
JNIEnv* env,
jclass,jstring content ) {
const char* a = env->GetStringUTFChars(content, nullptr);
int content_size = env->GetStringUTFLength(content);
if(a!=0){
LOGI("now a is %s",a);
LOGI("now content is %s",content);
}
env->ReleaseStringUTFChars(content,a);
jstring result = env->NewStringUTF("Hello I`m from myfirstjnienv!");
return result;
}


extern "C" JNIEXPORT jint JNICALL
Java_com_example_demoso1_MainActivity_myfirstjni(
JNIEnv* env,
jobject clazz) {
return r0add(50,1);
}


void *detect_frida_loop(void *) {
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
inet_aton("0.0.0.0", &(sa.sin_addr));
int sock;
int i;
int ret;
char res[7];
while(1){
/*
* 1:Frida Server Detection
*/
//LOGI("entering frida server detect loop started");
for(i=20000;i<30000;i++){
sock = socket(AF_INET,SOCK_STREAM,0);
sa.sin_port = htons(i);
LOGI("entering frida server detect loop started,now i is %d",i);

if (connect(sock , (struct sockaddr*)&sa , sizeof sa) != -1) {
memset(res, 0 , 7);
send(sock, "\x00", 1, NULL);
send(sock, "AUTH\r\n", 6, NULL);
usleep(500); // Give it some time to answer
if ((ret = recv(sock, res, 6, MSG_DONTWAIT)) != -1) {
if (strcmp(res, "REJECT") == 0) {
LOGI("FOUND FRIDA SERVER: %s,FRIDA DETECTED [1] - frida server running on port %d!",APPNAME,i);
}else{
LOGI("not FOUND FRIDA SERVER");
}
}
}
close(sock);
}
}
}


extern "C" JNIEXPORT void JNICALL
Java_com_example_demoso1_MainActivity_init(
JNIEnv* env,
jobject clazz) {

pthread_t t;
pthread_create(&t,NULL,detect_frida_loop,(void*)NULL);

LOGI("frida server detect loop started");
}

JNIEXPORT jstring JNICALL stringFromJNI2(
JNIEnv* env,
jclass clazz) {

jclass testClass = env->FindClass("com/example/demoso1/Test");
//jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
jfieldID publicStaticField = env->GetStaticFieldID(testClass,"publicStaticField","Ljava/lang/String;");
jstring publicStaticField_value = (jstring)env->GetStaticObjectField(testClass,publicStaticField);

const char* value_ptr = env->GetStringUTFChars(publicStaticField_value, nullptr);
LOGI("now content is %s",value_ptr);


std::string hello = "Hello from C++ stringFromJNI2";
return env->NewStringUTF(hello.c_str());
}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
JNIEnv *env;
vm->GetEnv((void **) &env, JNI_VERSION_1_6);
JNINativeMethod methods[] = {
{"stringFromJNI2", "()Ljava/lang/String;", (void *) stringFromJNI2},
};
env->RegisterNatives(env->FindClass("com/example/demoso1/MainActivity"), methods, 1);
return JNI_VERSION_1_6;

这里我写的hook代码,其实和java hook的东西完全一样的,毕竟只要在java层声明了,那么必然还是得遵循java层的逻辑来操作。

function main()
{
Java.perform(function(){
Java.use("com.example.demoso1.MainActivity").stringFromJNI.implementation=function(){
var result=this.stringFromJNI();
console.log("result is->",result);
return result;
}
console.log("invoke stringFromJNI : =>"+Java.use("com.example.demoso1.MainActivity").stringFromJNI());
Java.use("com.example.demoso1.MainActivity").stringFromJNI2.implementation=function(){
var result=this.stringFromJNI2();
console.log("result is->",result);
return result;
}
console.log("invoke stringFromJNI2 : =>"+Java.use("com.example.demoso1.MainActivity").stringFromJNI2());
Java.use("com.example.demoso1.MainActivity").init.implementation=function(){
console.log("hook init successfully");
return this.init();
}
Java.choose("com.example.demoso1.MainActivity",{
onMatch:function(instance){
console.log("Found instance",instance);
instance.myfirstjniJNI();

},onComplete:function(){console.log("search complete!")}
})
})
}

  1. native函数的native hook
    这里终于进入了主题,毕竟在正式逆向或者ctf中,大部分加密还是在自定义的函数中的,虽然今天这节课也没说2333,估计等下次课了。这里我直接放hook的代码根据代码来讲解可能会好一点

function hook_nativelib()
{
var native_lib_addr=Module.findBaseAddress("libnative-lib.so");
console.log("native_lib_addr ->",native_lib_addr);
var myfirstjniJNI=Module.findExportByName("libnative-lib.so","Java_com_example_demoso1_MainActivity_myfirstjniJNI");
console.log("myfirstjiniJNI addr ->",myfirstjniJNI);
Interceptor.attach(myfirstjniJNI,{
onEnter:function(args){
//console.log("Interceptor.attach myfirstjniJNI args:",args[0],args[1],args[2]);
//console.log("jstring is",Java.vm.getEnv().getStringUtfChars(args[2],null).readCString());
},onLeave:function(reval){
//console.log("Interceptor.attach myfirstjniJNI retval",reval);
}
})
}
function main1()
{
hook_nativelib();
}
setImmediate(main1);

操作步骤:

  1. objection先打开,然后memory list modules,查看一下加载的so文件,然后再进一步查看导出函数有哪些,这步和在ida中查看其实是一样的。
  2. 先找到so的基址(Module.findBaseAddress(“模块名”)),然后如果hook在java层有定义的方法,或者直接知道native函数在jni中的函数名,可以直接使用Module.findExportByName(“模块名”,“jni中的函数名”),可以直接找出虚拟地址,大多数自定义函数,得先找出偏移,加上模块的基址
  3. 再调用Interceptor.attach(“之前找的jni函数虚拟地址”,{ onEnter:function(args){ 这里是输入,也就是参数的地方, args是参数的数组形式,不过存到好像是args的地址,所以需要有特殊的函数进行转换,上次nu1lctf中看到有个师傅的手法是ptr(args[0]).readCString(),比上面那行代码简洁,上面的话,实际就是根据jni开发的手法进行的,无论是javahook还是nativehook,最终都是变成开发的问题。 },onLeave:function(reval){
  • } })
  1. 开发很重要

 

 

点击关注,共同学习!
[安全狗的自我修养](https://mp.weixin.qq.com/s/E6Kp0fd7_I3VY5dOGtlD4w)


[github haidragon](https://github.com/haidragon)


https://github.com/haidragon

标签:Log,MainActivity,hook,env,Frida,com,native
From: https://www.cnblogs.com/haidragon/p/16879521.html

相关文章

  • 学习笔记-hook和主动调用
    用户代码nativehook静态注册函数参数,返回值打印和替换调用栈主动调动符号hook==偏移hook枚举并保存结果0x01修改返回值以及参数和主动调用修改返回值修改......
  • 学习笔记-JNI框架层的Hook利用
    系统框架nativehookJNI函数符号hookJNI函数参数、返回值打印和替换动态注册JNI_OnloadhookRegisterNativesjnitrace引入一个例子,hookGetStringUTFChars这个jn......
  • 学习笔记-libc框架层的Hook利用
    系统框架层nativehooklibc函数符号hooklibc函数参数、返回值打印和替换主动调用libc读写文件hooklinkerdlopenfrida-trace引入例子,先hookpthread这个libc函......
  • SpringBoot启动报错The APR based Apache Tomcat Native..
    SpringBoot项目启动报错TheAPRbasedApacheTomcatNativel...一、报错信息2022-11-1009:50:53org.apache.catalina.core.AprLifecycleListenerinit信息:TheAP......
  • react在create-react-app中使用hooks常见注意事项
    useState它的作用就是声明状态变量useState注意点:初始参数只在组件的首次渲染的时候使用,再次更新是会被忽略每次通过setXxx修改状态都会引起组件重新渲染useS......
  • Knative架构解析
    CNCF针对无服务器计算的定义为:无服务器计算是指一种构建和运行不需要服务器管理的应用程序的理念。它描述了一个更细粒度的部署模型,即将以一个或多个功能方式提供的应用程序......
  • Jenkins: Generic Webhook Trigger
     GitHub:PayloadURL:https://jenkins-new.jam.only.sap/generic-webhook-trigger/invoke?token=resurrect&jobQuietPeriod=1      Pipeline:pipe......
  • React Native -- FlatList 之 下拉刷新,上拉加载
    FlatList组件importReact,{FC,useEffect,useState,useRef}from'react';import{ActivityIndicator,FlatListasRNFlastList,FlatListProps}from'react-......
  • react-native-device-info(获取设备信息)在React Native安装使用步骤
    官方参考地址:https://github.com/react-native-community/react-native-device-info1.安装//下载依赖npminstall--savereact-native-device-info||yarnadd......
  • React Native中的DeviceEventEmitter的使用
    react-native中采用了DeviceEventEmitter来实现对事件的监听,实现非父子关系的页面之间的通信。具体来说,我们可以在一个页面中通过DeviceEventEmitter来对特定名称的事件进......