吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 13496|回复: 72
上一主题 下一主题
收起左侧

[Android 原创] 小红书APP Unidbg shield 分享知识点

  [复制链接]
跳转到指定楼层
楼主
胡凯莉 发表于 2023-6-26 16:56 回帖奖励

shield 分享知识点

1、hook NewStringUTF

  • 找到在so层 c语言字符串转化为jni中的jstring类型 使用的函数NewStringUTF(env,c中字符串)

  • var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrNewStringUTF = null;
    for (var i = 0; i < symbols.length; i++) {
      var symbol = symbols[i];
    
      if (symbol.name.indexOf("NewStringUTF") >= 0 && symbol.name.indexOf("CheckJNI") < 0) {
          addrNewStringUTF = symbol.address;
          console.log("NewStringUTF is at ", symbol.address, symbol.name);
      }
    }
    
    if (addrNewStringUTF != null) {
      Interceptor.attach(addrNewStringUTF, {
          onEnter: function (args) {
              var c_string = args[1];
              var dataString = c_string.readCString();
    
              if (dataString.indexOf("XYAAAAAQAAAAEAAAB") != -1) {
                  console.log(dataString);
                  console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
                  console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
              }
    
          }
      });
    }
    
    // frida -UF  -l  1.so_utfstring.js -o token.txt

2、hook 所有的interceptor

  • public class XhsHttpInterceptor implements Interceptor {
    //在so中创建请求头的值,并添加到request对象中,在继续执行下一个拦截器
    public native Response intercept(Interceptor.Chain chain, long j2) throws IOException;
    
    }
  • 拦截器中做的事情:

    • public native Response intercept(Interceptor.Chain chain, long j2)
    • 1、获取request对象  request = chain.request()
    • 2、使用request对象添加请求头
    • 3、执行下一个拦截器 chain.proceed(request)
  • //hook所有拦截器
    Java.perform(function () {
      var Builder = Java.use('okhttp3.OkHttpClient$Builder');
    
      Builder.addInterceptor.implementation = function (inter) {
          //console.log("实例化:");
    
          console.log(JSON.stringify(inter) );
          //console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
          return this.addInterceptor(inter);
      };
    })
    
    //  frida -U -f com.xingin.xhs  -l 6.all_inter.js
    
    "<instance: okhttp3.Interceptor, $className: p.d0.g1.j.b>"
    "<instance: okhttp3.Interceptor, $className: p.d0.g1.j.a>"
    "<instance: okhttp3.Interceptor, $className: com.xingin.shield.http.XhsHttpInterceptor>"
    "<instance: okhttp3.Interceptor, $className: p.d0.v1.e0.n0.h>"
    "<instance: okhttp3.Interceptor, $className: p.d0.g1.k.a>"
    "<instance: okhttp3.Interceptor, $className: p.d0.v1.e0.r0.k>"

3、验证前后两个拦截器的内容 是否是在某个拦截器中生成的参数

  • 验证1:  com.xingin.shield.http.XhsHttpInterceptor

  • Java.perform(function () {
      var XhsHttpInterceptor = Java.use('com.xxx.shield.http.XhsHttpInterceptor');
      var Buffer = Java.use("okio.Buffer");
      var Charset = Java.use("java.nio.charset.Charset");
    
      XhsHttpInterceptor.intercept.overload('okhttp3.Interceptor$Chain').implementation = function (chain, j2) {
          console.log('\n--------------------请求来了--------------------');
          var request = chain.request();
    
          var urlString = request.url().toString();
          console.log("网址:")
          console.log(urlString)
          console.log("\n请求头:")
          console.log(request.headers().toString());
    
          var requestBody = request.body();
          if (requestBody) {
              var buffer = Buffer.$new();
              requestBody.writeTo(buffer);
              console.log("请求体:")
              console.log(buffer.readString(Charset.forName("utf8")));
          }
    
          var res = this.intercept(chain);
          return res;
      };
    })
    //  frida -UF  -l 8.next_request.js

  • 验证2 : p.d0.v1.e0.n0.h

  • Java.perform(function () {
      var XhsHttpInterceptor = Java.use('p.d0.v1.e0.n0.h');
      var Buffer = Java.use("okio.Buffer");
      var Charset = Java.use("java.nio.charset.Charset");
    
      XhsHttpInterceptor.intercept.overload('okhttp3.Interceptor$Chain').implementation = function (chain, j2) {
          console.log('\n--------------------请求来了--------------------');
          var request = chain.request();
    
          var urlString = request.url().toString();
          console.log("网址:")
          console.log(urlString)
          console.log("\n请求头:")
          console.log(request.headers().toString());
    
          var requestBody = request.body();
          if (requestBody) {
              var buffer = Buffer.$new();
              requestBody.writeTo(buffer);
              console.log("请求体:")
              console.log(buffer.readString(Charset.forName("utf8")));
          }
    
          var res = this.intercept(chain);
          return res;
      };
    })
    //  frida -UF  -l 8.next_request.js

4、逆向

  • 判断so方法是静态方法 还是 实例方法

    • 静态方法 :

    • 方法格式如: public  static native Response intercept()   注意static

    • 直接  类.方法

    • 实例方法:

    • 格式 : public native Response intercept(Interceptor.Chain chain, long j2)

    • 需要实例化类对象:

      • 1 、 XhsHttpInterceptor  i1  =    new  XhsHttpInterceptor()
      • 2 、 Builder.addInterceptor(i1)
      • 3、  i1.intercept(chain, 10)
    • 注意:

      • 1  static中的内容要补全
      • 2  构造方法中的事情要补全

    • static {
            initializeNative();
        }
      
      1 public static native void initializeNative();
      
      public XhsHttpInterceptor(String str, a<Request> aVar) {
            this.cPtr = initialize(str);
            this.predicate = aVar;
        }
      
      2 public native long initialize(String str);
      
      3 public native Response intercept(Interceptor.Chain chain, long j2) throws IOException;
      
  • 注意: shield 请求头的值  不是通过返回值获取的  ,在intercept内部

    • request  = chain.request()
    • request.addHeader("shield","zzzxxx")
    • 补环境时遇到request对象和addHeader 方法时  注意看一下参数 是否是shield

5、 补环境 initializeNative

  • 签名调用
DvmClass cls = vm.resolveClass("com/xxx/shield/http/XhsHttpInterceptor");
        // 调用方法 StringObject---unidbg中
        cls.callStaticJniMethodObject(
                emulator,
                "initializeNative()V",
        );
  • 偏移地址调用

    • hook 函数地址的偏移

    • 
      var symbols = Module.enumerateSymbolsSync("libart.so");
      var addrRegisterNatives = null;
      for (var i = 0; i < symbols.length; i++) {
      var symbol = symbols[i];
      if (symbol.name.indexOf("art") >= 0 &&
          symbol.name.indexOf("JNI") >= 0 &&
          symbol.name.indexOf("RegisterNatives") >= 0 &&
          symbol.name.indexOf("CheckJNI") < 0) {
          addrRegisterNatives = symbol.address;
          // console.log("RegisterNatives is at ", symbol.address, symbol.name);
      }
      }
      // console.log("addrRegisterNatives=", addrRegisterNatives);

    if (addrRegisterNatives != null) {
    Interceptor.attach(addrRegisterNatives, {
    onEnter: function (args) {
    var env = args[0];
    var java_class = args[1];
    var class_name = Java.vm.tryGetEnv().getClassName(java_class);
    // 只有类名为com.xxx.nativelibrary.LibBili,才打印输出
    console.log(class_name);
    var taget_class = "com.xxx.shield.http.XhsHttpInterceptor";

            if (class_name === taget_class) {
                // console.log("\n[RegisterNatives] method_count:", args[3]);
                var methods_ptr = ptr(args[2]);
                var method_count = parseInt(args[3]);
    
                for (var i = 0; i < method_count; i++) {
                    // Java中函数名字的
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    // 参数和返回值类型
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    // C中的函数指针
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
    
                    var name = Memory.readCString(name_ptr); // 读取java中函数名
                    var sig = Memory.readCString(sig_ptr); // 参数和返回值类型
                    var find_module = Process.findModuleByAddress(fnPtr_ptr); // 根据C中函数指针获取模块
    
                    var offset = ptr(fnPtr_ptr).sub(find_module.base) // fnPtr_ptr - 模块基地址
                    // console.log("[RegisterNatives] java_class:", class_name);
                    console.log("name:", name, "sig:", sig, "module_name:", find_module.name, "offset:", offset);
                    //console.log("name:", name, "module_name:", find_module.name, "offset:", offset);
    
                }
            }
        }
    });

    }
    -----------------------------------或者下面----------------------------------------

    function find_RegisterNatives(params) {
    var symbols = Module.enumerateSymbolsSync("libart.so");
    var addrRegisterNatives = null;
    for (var i = 0; i < symbols.length; i++) {
    var symbol = symbols[i];

        //_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
        if (symbol.name.indexOf("art") >= 0 &&
                symbol.name.indexOf("JNI") >= 0 && 
                symbol.name.indexOf("RegisterNatives") >= 0 && 
                symbol.name.indexOf("CheckJNI") < 0) {
            addrRegisterNatives = symbol.address;
            console.log("RegisterNatives is at ", symbol.address, symbol.name);
            hook_RegisterNatives(addrRegisterNatives)
        }
    }

    }

    function hook_RegisterNatives(addrRegisterNatives) {

    if (addrRegisterNatives != null) {
        Interceptor.attach(addrRegisterNatives, {
            onEnter: function (args) {
                console.log("[RegisterNatives] method_count:", args[3]);
                var java_class = args[1];
                var class_name = Java.vm.tryGetEnv().getClassName(java_class);
                //console.log(class_name);
    
                var methods_ptr = ptr(args[2]);
    
                var method_count = parseInt(args[3]);
                for (var i = 0; i < method_count; i++) {
                    var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));
                    var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));
                    var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));
    
                    var name = Memory.readCString(name_ptr);
                    var sig = Memory.readCString(sig_ptr);
                    var symbol = DebugSymbol.fromAddress(fnPtr_ptr)
                                        //console.log("[RegisterNatives] java_class:", class_name)
                                        var target_ClassName = "com.xxx.shield.http.XhsHttpInterceptor";
                                        if(class_name === target_ClassName){
                                                 console.log("[RegisterNatives] java_class:", class_name, "name:", name, "sig:", sig, "fnPtr:", fnPtr_ptr,  " fnOffset:", symbol, " callee:", DebugSymbol.fromAddress(this.returnAddress));
    
                         //console.log("name:", name, "sig:", sig, "module_name:", find_module.name, "offset:", offset);
                                        }
                }
            }
        });
    }

    }

    setImmediate(find_RegisterNatives);

    
    
    
    
    
    
    
    
    
    
    
  • ArrayList<Object> args = new ArrayList<>(10);
    args.add(vm.getJNIEnv());   args.add(vm.addLocalObject(vm.resolveClass("com/xxx/shield/http/XhsHttpInterceptor")));
    module.callFunction(
      emulator,
      0x94289,
      args.toArray()
          );
    
    --------------------------------------------------------------
    module.callFunction(emulator,
                   0x94289,
                   vm.getJNIEnv(),
      vm.addLocalObject(vm.resolveClass("com/xxx/shield/http/XhsHttpInterceptor")) //jclass
                  );
  • 补环境

  • java.lang.UnsupportedOperationException: java/nio/charset/Charset->defaultCharset()Ljava/nio/charset/Charset;
          at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:503)
    • @Override
      public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
          if (signature.equals("java/nio/charset/Charset->defaultCharset()Ljava/nio/charset/Charset;")){
              return vm.resolveClass("java/nio/charset/Charset").newObject(Charset.defaultCharset());
          }
          return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
      }
    • 需要hook一下参数   静态字段  .value 获取值

    • 
      Java.perform(function () {
      var ContextHolder = Java.use('com.xxxx.shield.http.ContextHolder');
      console.log('sAppId=',ContextHolder.sAppId.value);
      console.log('sDeviceId=',ContextHolder.sDeviceId.value);
      })

6、 补环境 initialize

  • 注意返回值是long

  • public long initialize (){
          Number number = module.callFunction(emulator,
                  0x937B1,
                  vm.getJNIEnv(),
                  vm.addLocalObject(vm.resolveClass("com/xingin/shield/http/XhsHttpInterceptor")), //jclass
                  vm.addLocalObject(new StringObject(vm,"main"))
          );
          return number.longValue();
      }
  • hook时机: 在加载so文件后进行hook

    • 
      function do_hook() {
      setTimeout(function () {
          Java.perform(function () {
              var XhsHttpInterceptor = Java.use('com.xxx.shield.http.XhsHttpInterceptor');
              XhsHttpInterceptor.initialize.implementation = function (str) {
                  console.log("str=", str);
                  return this.initialize(str);
              };
          })
      }, 40);
      }

    function load_so_and_hook() {
    var dlopen = Module.findExportByName(null, "dlopen");
    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

    Interceptor.attach(dlopen, {
        onEnter: function (args) {
            var path_ptr = args[0];
            var path = ptr(path_ptr).readCString();
            // console.log("[dlopen:]", path);
            this.path = path;
        }, onLeave: function (retval) {
            if (this.path.indexOf("libshield.so") !== -1) {
                console.log("[dlopen:]", this.path);
                do_hook();
    
            }
        }
    });
    
    Interceptor.attach(android_dlopen_ext, {
        onEnter: function (args) {
            var path_ptr = args[0];
            var path = ptr(path_ptr).readCString();
    
            this.path = path;
        }, onLeave: function (retval) {
            if (this.path.indexOf("libshield.so") !== -1) {
                console.log("\nandroid_dlopen_ext加载:", this.path);
                do_hook();
    
            }
        }
    });

    }

    load_so_and_hook();

    
    
    
    
    
    
    
    
    
    
    

getSharedPreferences

  • 开发时 读取xml文件会使用这个api

  • SharedPreferences  sp  = getSharedPreferences("s",0)//第一个参数是xml文件的名字,第二个参数是模式
    
    String  token = sp.getString("main","");//读取xml文件中内容
  • public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
          if (signature.equals("android/content/Context->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;")){
              String xmlName =  vaList.getObjectArg(0).getValue().toString();
              System.out.println("xmlName : " + xmlName);
              return vm.resolveClass("android.content.SharedPreferences")
                      .newObject(null);
          }
          if (signature.equals("android/content/SharedPreferences->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;")){
              String arg1 = vaList.getObjectArg(0).getValue().toString();
              String arg2 = vaList.getObjectArg(1).getValue().toString();
              System.out.println("key : " + arg1);
              System.out.println(arg2);
          }
          return super.callObjectMethodV(vm, dvmObject, signature, vaList);
      }

  • 没有mian的值 返回空即可

Base64

  • base64 补的是标准的base64   注意不要导包导错了

  • import org.apache.commons.codec.binary.Base64;
    byte[] decode = Base64.decodeBase64(arg1);

7、 补环境 intercept

public native Response intercept(Interceptor.Chain chain, long j2) throws IOException;
  • 如果要执行  必须要传入参数

    • chain 这个参数流程可以猜测如下

    • ```.
      1、request = chain.request
      2、读取request中的内容

    • 1 可以直接传空值  然后看后面读取什么补什么

    • 2 在Unidbg中引用okhttp3  构造一个真的chain对象

引用okhttp3

  • <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>3.10.0</version>
    </dependency>

免费评分

参与人数 15吾爱币 +14 热心值 +14 收起 理由
chenwen6 + 1 + 1 谢谢@Thanks!
Behind1 + 1 我很赞同!
SPT + 1 + 1 鼓励转贴优秀软件安全工具和文档!
yuanyou + 1 + 1 谢谢@Thanks!
ycwlhc + 1 + 1 热心回复!
lyx2002py + 1 + 1 我很赞同!
15513979586 + 1 用心讨论,共获提升!
junjia215 + 1 用心讨论,共获提升!
parallel + 1 谢谢@Thanks!
·温雅 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
featmellwo + 2 + 1 用心讨论,共获提升!
canty胖胖 + 1 + 1 热心回复!
T0T20230313 + 1 + 1 谢谢@Thanks!
helian147 + 1 + 1 热心回复!
Li1y + 2 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
风未眠 发表于 2023-6-26 20:27
谢谢分享
头像被屏蔽
3#
moruye 发表于 2023-6-26 21:58
4#
zjh889 发表于 2023-6-27 00:36
5#
你就是我的阳光 发表于 2023-6-27 08:29
感谢分享
6#
a2504028411 发表于 2023-6-27 10:45
楼主是那个版本的app
7#
rb30173504 发表于 2023-6-27 14:47

又有好东西让俺们学习了,谢谢!
8#
djshitaincai 发表于 2023-6-28 10:30
文章专业详细,谢谢楼主
9#
ningmng 发表于 2023-6-28 17:26
感谢分享!
10#
xtvfhptei 发表于 2023-6-28 20:59
都不怎么用小红书app了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 19:24

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表