首页
社区
课程
招聘
frida分析支付某扫码转账生成URI流程
发表于: 6天前 4009

frida分析支付某扫码转账生成URI流程

6天前
4009

需要分析支付某打开扫一扫,扫到目标二维码后是如何跳转到转账页面的。

发现扫码从数据包无从下手定位,尝试关键字:QRScan,QRCode,Translate等都找不到对应的函数,所以最终通过View的点击事件。

打印了堆栈,从堆栈结果里面找到了一个疑似创建扫码相机的View:

com.alipay.mobile.scan.ui2.NScanTopView

因为这个类代码量巨大,5000多行,而且都是混淆过的名称,我直接发给ai进行分析,得出了大概扫描后的回调结果是在a方法:

但是代码的过程看不到,只能看Smail指令,并且对应参数r25里面的BQCScanResult也是啥都没有,只能通过方法的执行过程分析(还是给AI分析)随后得出hook代码:

输出结果:

这样就拿到了扫码结果,但是我需要接着跟踪它将扫码结果进行转账通讯的部分,经过分析,最终处理扫描二维码的逻辑是在

输出结果:

然后去查看了 com.alipay.mobile.scan.as.main.MainCaptureActivity 这个类的 a 方法实现过程:

因为无法正常编译成java代码,并且很长,一如既往的让AI帮我分析,AI得出结果这个方法是一个判断扫描结果类型,进行路由分发的

然后在这个混淆的方法里面,找到了关键类
com.alipay.phone.scancode.y.a

最后跟踪进 a 方法里面的实现过程(发现也是无法正常编译的java代码)
这个函数的过程作用大致如下:
1、初始化参数处理
2、检查是否由路由传过来的参数
3、检查是否支持离线支付
4、处理路由
5、缓存处理
6、记录支付行为
最后,hook了这个方法里面的相关涉及的类,得出hook代码以及输出结果

通过输出结果,发现最后会生成出路由URI,用于唤起支付界面的,尝试通过adb直接打开这个URI看看是否能直接跳转到转账界面。

adb shell am start -a android.intent.action.VIEW -d "alipays://platformapi/startapp?appId=20001001&bizType=UTP_QR_CODE&pageData=省略参数"

最后成功直接打开转账界面,由此分析出了整个扫码转账的执行环节。。

var Button = Java.use("android.widget.Button");
Button.setOnClickListener.overload("android.view.View$OnClickListener").implementation = function (listener) {
        console.log("Button clicked");
        //listener.onClick(this);  // 调用原来的点击事件
        printStack();
        this.setOnClickListener(listener);
        console.log("Button clicked end");
        return;
    };
var Button = Java.use("android.widget.Button");
Button.setOnClickListener.overload("android.view.View$OnClickListener").implementation = function (listener) {
        console.log("Button clicked");
        //listener.onClick(this);  // 调用原来的点击事件
        printStack();
        this.setOnClickListener(listener);
        console.log("Button clicked end");
        return;
    };
@Override // com.alipay.mobile.scan.ui.BaseScanTopView
public final void a(com.alipay.mobile.bqcscanservice.BQCScanResult r25) {
// ... existing code ...
}
@Override // com.alipay.mobile.scan.ui.BaseScanTopView
public final void a(com.alipay.mobile.bqcscanservice.BQCScanResult r25) {
// ... existing code ...
}
let NScanTopView = Java.use("com.alipay.mobile.scan.ui2.NScanTopView");
   NScanTopView["a"].overload('com.alipay.mobile.bqcscanservice.BQCScanResult').implementation = function (result) {
       console.log("NScanTopView.a is called");
        
       if(result != null) {
           // 1. 先确认是否是 MultiMaScanResult
           if(result.$className === "com.alipay.mobile.mascanengine.MultiMaScanResult") {
               let multiResult = Java.cast(result, Java.use("com.alipay.mobile.mascanengine.MultiMaScanResult"));
                
               // 2. 获取 maScanResults 数组
               let scanResults = multiResult.maScanResults.value;
               if(scanResults && scanResults.length > 0) {
                   // 3. 获取第一个结果
                   let firstResult = scanResults[0];
                    
                   // 4. 打印扫描文本
                   console.log("Scan Text:", firstResult.text.value);
                    
                   // 5. 打印更多信息(如果需要)
                   if(firstResult.rect) {
                       console.log("Scan Rect:", JSON.stringify({
                           left: firstResult.rect.value.left,
                           top: firstResult.rect.value.top,
                           right: firstResult.rect.value.right,
                           bottom: firstResult.rect.value.bottom
                       }));
                   }
                    
                   if(firstResult.type) {
                       console.log("Scan Type:", firstResult.type.value);
                   }
               }
           }
            
           // 备用方案:通过反射获取所有可能的属性
           try {
               let fields = result.getClass().getDeclaredFields();
               for(let i = 0; i < fields.length; i++) {
                   let field = fields[i];
                   field.setAccessible(true);
                   console.log(`Field ${field.getName()}: ${field.get(result)}`);
               }
           } catch(e) {
               console.log("Error getting fields:", e);
           }
       }
        
       // 调用原始方法
       return this["a"](result);
   };
let NScanTopView = Java.use("com.alipay.mobile.scan.ui2.NScanTopView");
   NScanTopView["a"].overload('com.alipay.mobile.bqcscanservice.BQCScanResult').implementation = function (result) {
       console.log("NScanTopView.a is called");
        
       if(result != null) {
           // 1. 先确认是否是 MultiMaScanResult
           if(result.$className === "com.alipay.mobile.mascanengine.MultiMaScanResult") {
               let multiResult = Java.cast(result, Java.use("com.alipay.mobile.mascanengine.MultiMaScanResult"));
                
               // 2. 获取 maScanResults 数组
               let scanResults = multiResult.maScanResults.value;
               if(scanResults && scanResults.length > 0) {
                   // 3. 获取第一个结果
                   let firstResult = scanResults[0];
                    
                   // 4. 打印扫描文本
                   console.log("Scan Text:", firstResult.text.value);
                    
                   // 5. 打印更多信息(如果需要)
                   if(firstResult.rect) {
                       console.log("Scan Rect:", JSON.stringify({
                           left: firstResult.rect.value.left,
                           top: firstResult.rect.value.top,
                           right: firstResult.rect.value.right,
                           bottom: firstResult.rect.value.bottom
                       }));
                   }
                    
                   if(firstResult.type) {
                       console.log("Scan Type:", firstResult.type.value);
                   }
               }
           }
            
           // 备用方案:通过反射获取所有可能的属性
           try {
               let fields = result.getClass().getDeclaredFields();
               for(let i = 0; i < fields.length; i++) {
                   let field = fields[i];
                   field.setAccessible(true);
                   console.log(`Field ${field.getName()}: ${field.get(result)}`);
               }
           } catch(e) {
               console.log("Error getting fields:", e);
           }
       }
        
       // 调用原始方法
       return this["a"](result);
   };
Scan Text: https://qr.alipay.com/fkx17137rejqeh4j3v4cib0?t=1733923946234
Scan Rect: {"left":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4b8","0x7ae97bd488","0x7ae97c07a0"]},"top":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4d8","0x7ae97bd488","0x7ae97c07a0"]},"right":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4c8","0x7ae97bd488","0x7ae97c07a0"]},"bottom":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4a8","0x7ae97bd488","0x7ae97c07a0"]}}
Scan Type: QR
Field candidate: false
Field classicFrameCount: 44
Field frameCount: 44
Field frameType: 0
Field maScanResults: [Lcom.alipay.mobile.mascanengine.MaScanResult;@178c311
Field readerParams: null
Field recognizedPerformance: type=Normal^scanType=3^unrecognizedFrame=42^sumDurationOfUnrecognized=1694^durationOfRecognized=51^durationOfBlur=0^durationOfBlurSVM=0^detectFrameCountBlurSVM=0^detectAvgDurationBlurSVM=0.0^durationOfNoNeedCheckBlurSVM=0^whetherGetTheSameLaplaceValue=false^sameLaplaceValueCount=0^
Field rsBinarized: false
Field rsBinarizedCount: 0
Field rsInitTime: 0
Field totalEngineCpuTime: null
Field totalEngineTime: 2811662
Field totalScanTime: 48566
Scan Text: https://qr.alipay.com/fkx17137rejqeh4j3v4cib0?t=1733923946234
Scan Rect: {"left":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4b8","0x7ae97bd488","0x7ae97c07a0"]},"top":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4d8","0x7ae97bd488","0x7ae97c07a0"]},"right":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4c8","0x7ae97bd488","0x7ae97c07a0"]},"bottom":{"_p":["<instance: android.graphics.Rect>",2,{"className":"int","name":"I","type":"int32","size":1,"byteSize":4,"defaultValue":0},"0x7048f4a8","0x7ae97bd488","0x7ae97c07a0"]}}
Scan Type: QR
Field candidate: false
Field classicFrameCount: 44
Field frameCount: 44
Field frameType: 0
Field maScanResults: [Lcom.alipay.mobile.mascanengine.MaScanResult;@178c311
Field readerParams: null
Field recognizedPerformance: type=Normal^scanType=3^unrecognizedFrame=42^sumDurationOfUnrecognized=1694^durationOfRecognized=51^durationOfBlur=0^durationOfBlurSVM=0^detectFrameCountBlurSVM=0^detectAvgDurationBlurSVM=0.0^durationOfNoNeedCheckBlurSVM=0^whetherGetTheSameLaplaceValue=false^sameLaplaceValueCount=0^
Field rsBinarized: false
Field rsBinarizedCount: 0
Field rsInitTime: 0
Field totalEngineCpuTime: null
Field totalEngineTime: 2811662
Field totalScanTime: 48566
// 1. Hook BaseScanTopView 的所有方法,找到使用 c 的地方
  let BaseScanTopView = Java.use("com.alipay.mobile.scan.ui.BaseScanTopView");
  let NScanTopView = Java.use("com.alipay.mobile.scan.ui2.NScanTopView");
  // Hook 所有方法
  let methods = BaseScanTopView.class.getDeclaredMethods();
  methods.forEach(method => {
      let methodName = method.getName();
      if(BaseScanTopView[methodName]) {
          try {
              BaseScanTopView[methodName].implementation = function() {
                  console.log(`\n[*] BaseScanTopView.${methodName} 被调用`);
                   
                  // 尝试通过反射获取 c 字段的值
                  try {
                      let field = this.getClass().getSuperclass().getDeclaredField("c");
                      field.setAccessible(true);
                      let cValue = field.get(this);
                      if(cValue) {
                          console.log("[*] c 字段值类型:", cValue.$className);
                           
                          // 如果是 by 接口的实现,hook 它的方法
                          if(Java.use("com.alipay.mobile.scan.ui.by").class.isAssignableFrom(cValue.getClass())) {
                              console.log("[*] 找到 by 接口实现类:", cValue.$className);
                               
                              // Hook 这个实现类的方法
                              let implClass = Java.use(cValue.$className);
                              if(implClass.a) {
                                  implClass.a.overload('com.alipay.mobile.mascanengine.MaScanResult',
                                                     'com.alipay.mobile.scan.util.DataTransChannel')
                                  .implementation = function(maScanResult, dataTransChannel) {
                                      console.log("\n[*] by 实现类的 a 方法被调用");
                                      if(maScanResult != null) {
                                          console.log("[*] 扫描结果:", maScanResult.text.value);
                                      }
                                      return this.a(maScanResult, dataTransChannel);
                                  };
                              }
                          }
                      }
                  } catch(e) {
                      //console.log("[!] 获取 c 字段失败:", e);
                  }
                   
                  // 调用原方法
                  return this[methodName].apply(this, arguments);
              };
          } catch(e) {
              //console.log(`[!] Hook ${methodName} 失败:`, e);
          }
      }
  });
   
  // 2. Hook NScanTopView 的 b 方法
  NScanTopView["b"].overload('com.alipay.mobile.bqcscanservice.BQCScanResult').implementation = function (bQCScanResult) {
      console.log("\n[*] NScanTopView.b 被调用");
       
      // 在调用前尝试获取 c 字段
      try {
          let field = this.getClass().getSuperclass().getDeclaredField("c");
          field.setAccessible(true);
          let cValue = field.get(this);
          if(cValue) {
              console.log("[*] c 字段实现类:", cValue.$className);
          }
      } catch(e) {
          console.log("[!] 获取 c 字段失败:", e);
      }
       
      let result = this["b"](bQCScanResult);
      return result;
  };
   
  // 3. 动态查找并 hook by 接口的实现类
  Java.choose("com.alipay.mobile.scan.ui.by", {
      onMatch: function(instance) {
          console.log("[*] 找到 by 接口实例:", instance.$className);
          // Hook 这个实例的方法
          let implClass = Java.use(instance.$className);
          if(implClass.a) {
              implClass.a.overload('com.alipay.mobile.mascanengine.MaScanResult',
                                 'com.alipay.mobile.scan.util.DataTransChannel')
              .implementation = function(maScanResult, dataTransChannel) {
                  console.log("\n[*] by 实现类的 a 方法被调用");
                  if(maScanResult != null) {
                      console.log("[*] 扫描结果:", maScanResult.text.value);
                  }
                  return this.a(maScanResult, dataTransChannel);
              };
          }
      },
      onComplete: function() {}
  });
// 1. Hook BaseScanTopView 的所有方法,找到使用 c 的地方
  let BaseScanTopView = Java.use("com.alipay.mobile.scan.ui.BaseScanTopView");
  let NScanTopView = Java.use("com.alipay.mobile.scan.ui2.NScanTopView");
  // Hook 所有方法
  let methods = BaseScanTopView.class.getDeclaredMethods();
  methods.forEach(method => {
      let methodName = method.getName();
      if(BaseScanTopView[methodName]) {
          try {
              BaseScanTopView[methodName].implementation = function() {
                  console.log(`\n[*] BaseScanTopView.${methodName} 被调用`);
                   
                  // 尝试通过反射获取 c 字段的值
                  try {
                      let field = this.getClass().getSuperclass().getDeclaredField("c");
                      field.setAccessible(true);
                      let cValue = field.get(this);
                      if(cValue) {
                          console.log("[*] c 字段值类型:", cValue.$className);
                           
                          // 如果是 by 接口的实现,hook 它的方法
                          if(Java.use("com.alipay.mobile.scan.ui.by").class.isAssignableFrom(cValue.getClass())) {
                              console.log("[*] 找到 by 接口实现类:", cValue.$className);
                               
                              // Hook 这个实现类的方法
                              let implClass = Java.use(cValue.$className);
                              if(implClass.a) {
                                  implClass.a.overload('com.alipay.mobile.mascanengine.MaScanResult',
                                                     'com.alipay.mobile.scan.util.DataTransChannel')
                                  .implementation = function(maScanResult, dataTransChannel) {
                                      console.log("\n[*] by 实现类的 a 方法被调用");
                                      if(maScanResult != null) {
                                          console.log("[*] 扫描结果:", maScanResult.text.value);
                                      }
                                      return this.a(maScanResult, dataTransChannel);
                                  };
                              }
                          }
                      }
                  } catch(e) {
                      //console.log("[!] 获取 c 字段失败:", e);
                  }
                   
                  // 调用原方法
                  return this[methodName].apply(this, arguments);
              };
          } catch(e) {
              //console.log(`[!] Hook ${methodName} 失败:`, e);
          }
      }
  });
   
  // 2. Hook NScanTopView 的 b 方法
  NScanTopView["b"].overload('com.alipay.mobile.bqcscanservice.BQCScanResult').implementation = function (bQCScanResult) {
      console.log("\n[*] NScanTopView.b 被调用");
       
      // 在调用前尝试获取 c 字段
      try {
          let field = this.getClass().getSuperclass().getDeclaredField("c");
          field.setAccessible(true);
          let cValue = field.get(this);
          if(cValue) {
              console.log("[*] c 字段实现类:", cValue.$className);
          }
      } catch(e) {
          console.log("[!] 获取 c 字段失败:", e);
      }
       
      let result = this["b"](bQCScanResult);
      return result;
  };
   
  // 3. 动态查找并 hook by 接口的实现类
  Java.choose("com.alipay.mobile.scan.ui.by", {
      onMatch: function(instance) {
          console.log("[*] 找到 by 接口实例:", instance.$className);
          // Hook 这个实例的方法
          let implClass = Java.use(instance.$className);
          if(implClass.a) {
              implClass.a.overload('com.alipay.mobile.mascanengine.MaScanResult',
                                 'com.alipay.mobile.scan.util.DataTransChannel')
              .implementation = function(maScanResult, dataTransChannel) {

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 6天前 被kzzll编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (3)
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
6天前
0
雪    币: 2428
活跃值: (10698)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
大佬能否提供一个apk样本
5天前
0
雪    币: 102
活跃值: (2150)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
能否提供一个apk样本
5天前
0
游客
登录 | 注册 方可回帖
返回
//