安卓逆向百例十-币coin

news2024/11/17 14:15:18
typora-root-url: ./pic

安卓逆向百例十-币coin

现在售价依旧是99¥,计划更新100案例,平均一个案例1块钱,要什么自行车!

案例起源:

有位老哥 经过炒币失败后想做一个这种查询接口的app,但是苦于他的资金比较紧缺,就给我几百块,但是架不住苦苦哀求,我就做了,顺便当个案例。

版本: 币coin 4.0.3

包名: com.temperaturecoin

1.抓包分析

url是https://i.bicoin.com.cn/firmOffer/getUserAccountInfoBySecretNew?salt=8&sign=CDGCCEJCPCIGGGLICMVVBQOIEJTNNUQQN&time=1724126970032&userId=894919

位置在:


其中我们今天要去逆向的是 sign的生成,以及data的解密。

2.关键词定位

搜索

查找用例:

进入方法内 我们可以看的 sign是 经过了AESUtil.s

其中我们可以可以得到 :

time 就是时间戳 ,salt是一个 随机的值 sign 是 time + salt + "getUserAccountInfoBySecretNew"

接下来跳转到AESUtil.s 方法里面 跳转到这个位置:

hook验证一下入参:

查看一下加载的so文件是 ns:

打开ida 反编译 libns.so 搜索java我们发现是静态注册的 并且发现了 Java_com_bcoin_ns_S_s这个字眼:

进去看看喽!顺便把fastcall Java_com_bcoin_ns_S_s(int64 a1 的a1改成 JNIEnv *

jstring __fastcall Java_com_bcoin_ns_S_s(JNIEnv *a1, __int64 a2, __int64 a3)
{
  const char *v5; // x20
  const char *v6; // x21
  jstring result; // x0
  int v8; // w10
  __int64 (__fastcall *v9)(); // x10
  char v10; // w24
  int v11; // w9
  char *v12; // x20
  size_t v13; // w0
  char *v14; // x21
  unsigned __int64 v15; // x10
  char *v16; // x11
  unsigned __int64 v17; // x9
  char *v18; // x10
  char v19; // t1
  _OWORD *v20; // x12
  __int128 *v21; // x13
  unsigned __int64 v22; // x14
  __int128 v23; // q0
  __int128 v24; // q1
  _QWORD v25[2]; // [xsp+0h] [xbp-40h] BYREF
​
  v25[1] = *(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v5 = (*a1)->GetStringUTFChars(a1, a3, 0LL);
  v6 = (*a1)->GetStringUTFChars(a1, token, 0LL);
  (*a1)->ReleaseStringUTFChars(a1, a3, v5);
  (*a1)->ReleaseStringUTFChars(a1, token, v6);
  result = 0LL;
  if ( v5 && v6 )
  {
    v8 = idxMethod % 3;
    if ( idxMethod % 3 )
    {
      if ( v8 == 2 )
      {
        v9 = jointMd5;
        v10 = 100;
      }
      else if ( v8 == 1 )
      {
        v9 = axor;
        v10 = 67;
      }
      else
      {
        v9 = chainXor;
        v10 = 97;
      }
    }
    else
    {
      v9 = cxor;
      v10 = 98;
    }
    if ( idxMethod == 2147483646 )
      v11 = 0;
    else
      v11 = idxMethod + 1;
    idxMethod = v11;
    v12 = (v9)(v5, v6);
    __android_log_print(3, "bc", "pre payload: %s", v12);
    if ( v12 )
    {
      v13 = strlen(v12);
      v14 = v25 - ((v13 + 1 + 15LL) & 0x1FFFFFFF0LL);
      *v14 = v10;
      if ( v13 >= 1 )
      {
        if ( v13 > 0x1FuLL && (v14 + 1 >= &v12[v13] || v12 >= &v14[v13 + 1]) )
        {
          v15 = v13 - (v13 & 0x1F);
          v20 = v14 + 17;
          v21 = (v12 + 16);
          v22 = v15;
          do
          {
            v23 = *(v21 - 1);
            v24 = *v21;
            v22 -= 32LL;
            v21 += 2;
            *(v20 - 1) = v23;
            *v20 = v24;
            v20 += 2;
          }
          while ( v22 );
          if ( (v13 & 0x1F) == 0 )
            goto LABEL_21;
        }
        else
        {
          v15 = 0LL;
        }
        v16 = &v12[v15];
        v17 = v13 - v15;
        v18 = &v14[v15 + 1];
        do
        {
          v19 = *v16++;
          --v17;
          *v18++ = v19;
        }
        while ( v17 );
      }
LABEL_21:
      v14[v13 + 1] = 0;
      free(v12);
      return (*a1)->NewStringUTF(a1, v14);
    }
    return (*a1)->NewStringUTF(a1, "");
  }
  return result;
}

眼尖的朋友已经看见了 这里有个 __android_log_print 我们可以使用adb 来监控他的输出

在设备上运行日志记录工具 logcat,这是 Android 提供的一个工具,用于实时查看系统和应用程序的日志。

在终端中执行以下命令启动 logcat

adb logcat

这会显示设备上的所有日志信息。如果你想要过滤特定标签(例如 "bc"),可以使用:

adb logcat -s bc:D

这里的 -s 选项指定了过滤的标签,D 表示只显示 DEBUG 级别及以上的日志。

logcat:524b5a6860bad1a0bbe0712e48a5debb

抓包:d524b5a6860bad1a0bbe0712e48a5debb

我们从中可以发现 其实就是 结果 加了一个d 对吧,但是我们通过观察 logcat 和 抓包会发现有的时候是大写 有的时候是小写而且规律也不一样。他其实和这个有关系

这段代码根据 idxMethod 的值选择一个函数指针,并根据不同的条件设置函数的参数和值。接着调用这个函数,并将结果保存到 v12 中。代码的目的是根据不同的条件选择合适的处理函数,并在每次调用后更新 idxMethod 的值。

代码解释:

result = 0LL; // 初始化一个 long long 类型的变量 result 为 0
​
if (v5 && v6) // 如果 v5 和 v6 都不为 0(即它们都有效)
{
    v8 = idxMethod % 3; // 计算 idxMethod 除以 3 的余数,并将结果赋值给 v8
​
    if (idxMethod % 3) // 如果 idxMethod 除以 3 的余数不为 0
    {
        if (v8 == 2) // 如果余数是 2
        {
            v9 = jointMd5; // 将 v9 设置为 jointMd5(假设 jointMd5 是一个函数指针)
            v10 = 100; // 将 v10 设置为 100
        }
        else if (v8 == 1) // 如果余数是 1
        {
            v9 = axor; // 将 v9 设置为 axor(假设 axor 是一个函数指针)
            v10 = 67; // 将 v10 设置为 67
        }
        else // 如果余数是 0
        {
            v9 = chainXor; // 将 v9 设置为 chainXor(假设 chainXor 是一个函数指针)
            v10 = 97; // 将 v10 设置为 97
        }
    }
    else // 如果 idxMethod 除以 3 的余数为 0
    {
        v9 = cxor; // 将 v9 设置为 cxor(假设 cxor 是一个函数指针)
        v10 = 98; // 将 v10 设置为 98
    }
​
    if (idxMethod == 2147483646) // 如果 idxMethod 的值为 2147483646
        v11 = 0; // 将 v11 设置为 0
    else
        v11 = idxMethod + 1; // 否则,将 v11 设置为 idxMethod + 1
​
    idxMethod = v11; // 更新 idxMethod 的值为 v11
​
    v12 = (v9)(v5, v6); // 调用 v9 指向的函数,传入 v5 和 v6 作为参数,并将结果赋值给 v12
}
​

我们这里可以直接看他走 jointMd5 的位置 然后 直接查看入参不就行了 ,然后固定参数去请求。这样就不用还原 chainXor 和 cxor 函数了。

我们查看joinmd5这个函数 映入眼帘的是 这两个

v8 = strcpy(v6, s); 
​
strcat(v8, a1);

所以我直接去hook joinMd5 这个函数:

args1来源token:

抓包和adb 记录的:

所以就是 MD5(token + times + salt + 'getUserAccountInfoBySecretNew') 这个已经验证过了是标准的md5

str = '2c06cef65865431546fdb751f255508b'+str(times)+"6"+'getUserAccountInfoBySecretNew'
# print(str)
sign = 'd'+calculate_md5(str)

所以我们请求的时候 固定salt 就行了

具体代码放在文章最后...

3.返回值解密

一般来说 同一个数据包的加密的位置在都在一块,所以我们就顺藤摸瓜,就找到

hook验证:

去so层看看 :

一进去我们就看到 AES 128 pkcs5 的字眼:

进来后发现:

好家伙 Key = getKey(); IV = getIV(); 这么明显吗?那就不怪我了

上frida hook: hook到key 和 iv了

hook代码如下:

​
// key
var addr = Module.findBaseAddress('libns.so');
var KEY = addr.add(0x10144);
console.log(KEY);
Interceptor.attach(KEY,{
    onEnter:function (args){
        console.log("---------key 进入了-----------")
​
    },
    onLeave:function(retval) {
        console.log("--------------------")
        console.log('KEY 返回值:',hexdump(retval,{length:16}))
    }
})
// IV
var addr = Module.findBaseAddress('libns.so');
var IV = addr.add(0x10144);
console.log(IV);
Interceptor.attach(IV,{
    onEnter:function (args){
        console.log("---------IV 进入了-----------")
​
    },
    onLeave:function(retval) {
        console.log("--------------------")
        console.log('IV 返回值:',hexdump(retval,{length:16}))
    }
})

至此我们整个流程就已经完成了。

4.python代码还原

import time
from loguru import logger
import requests
​
import hashlib
from Crypto.Cipher import AES
import base64
import requests
​
def unpad(data):
    """去除填充"""
    pad_length = data[-1]
    return data[:-pad_length]
def decrypt_aes_cbc(ciphertext_base64, key, iv):
    # key = bytes(key_text, 'utf-8')
    # iv = bytes(iv_text, 'utf-8')
    ciphertext = base64.b64decode(ciphertext_base64)
​
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext)
    plaintext = unpad(plaintext).decode('utf-8')
    return plaintext
def calculate_md5(data):
    # 创建一个MD5哈希对象
    md5_hash = hashlib.md5()
    # 更新哈希对象
    md5_hash.update(data.encode('utf-8'))
    # 返回MD5哈希的十六进制表示
    return md5_hash.hexdigest()
​
headers = {
 xxx
}
times = int(time.time() * 1000)
# 17236209670982
# 1723621530537
# 1723621708604
# print(times)
str = 'token'+str(times)+"6"+'getUserAccountInfoBySecretNew'
# print(str)
sign = 'd'+calculate_md5(str)
logger.info("sign签名{}".format(sign))
# print(sign)
headers['Sign'] = sign
headers['Time'] = f'{times}'
url = "https://i.bicoin.com.cn/firmOffer/getUserAccountInfoBySecretNew"
params = {
    "salt": "6",
    "sign": f"{sign}",
    "time": f"{times}",
    "userId": "894919"
}
response = requests.get(url, headers=headers, params=params).json()
print(response)
data = response['data']
key_hex = '8971483f9910300bdffee864cb135f34'
iv_hex = '8971483f9910300bdffee864cb135f34'
data = decrypt_aes_cbc(data, bytes.fromhex(key_hex), bytes.fromhex(iv_hex))
print(data)
#2c06cef65865431546fdb751f255508b17236221106396getUserAccountInfoBySecretNew
#2c06cef65865431546fdb751f255508b17236215305376getUserAccountInfoBySecretNew
交流群:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2066177.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows系统搭建WSUS更新服务问题整理

1、连接微软更新服务器时,总是连接失败,采取了指定TLS1.2连接的方法,还是连接失败。 解决方法:当时服务器的操作系统为windows server 2012 R2,将操作系统换成windows server 2016可以解决这个问题。 2、客户端怎么配…

Linux发送邮件:如何配置SMTP服务器发信?

linux发送邮件至多个收件人的方法?如何用Linux命令? 在Linux系统中,邮件发送是一个常见且重要的功能,无论是用于系统监控通知还是日常通信。AokSend将详细介绍如何在Linux环境下配置SMTP服务器,以确保您的邮件发送既高…

探索RAG与Multi-Agent的结合:解决复杂任务的新方法

最近帮企业定制了一个langgraphrag的项目,跟大家简单介绍一下设计架构和具体的应用。如果大家有兴趣,我也可以出一期视频,给大家详细介绍一下。我们会一步步探讨如何构建一个可以控的Agent,以执行RAG任务,并最终展示一…

C#为复杂属性提供下拉式编辑框和弹出式编辑框

一.为属性提供编辑类 弹出式和下拉式是如何实现的呢,这需要为属性提供一个专门的编辑类。.Net为我们提供了一个System.Drawing.Design.UITypeEditor类,它是所有编辑类的基类,从他继承出了诸如ColorEditor、FontEditor的类,因此我们…

信刻光盘摆渡系统安全合规实现跨网数据单向导入/导出

在当今信息化、数字化时代,各种数据传输和储存技术发展迅速,各安全领域行业对跨网数据交互需求日益迫切,数据传输的安全可靠性对于整个过程的重要性不可忽视。应如何解决网络安全与效率之间的矛盾,如何安全合规地实现跨网数据单向…

Spring Cloud Gateway 之动态路由

前言 熟悉 Spring Cloud Gateway 的人都知道 Gateway 提供了鉴权、路由等功能,本篇我们重点分析的是 Gateway 的动态路由功能。 Gateway Actuator API 方法源码解析 Spring Cloud Gateway 的 Actuator 端点允许监视 Spring Cloud Gateway 应用程序并与之交互&…

k8s学习(三十八) 使用OpenTelemetry+jaeger实现链路追踪

文章目录 前言一、安装jaeger二、安装cert-manager三、安装OpenTelemetry Operator四、配置 OpenTelemetry Collector五、配置 Instrumentation六、编写java示例程序并测试调用链跟踪 前言 OpenTelemetry 可以用于从应用程序收集数据。它是一组工具、API 和 SDK 集合&#xff…

《黑神话:悟空》Steam全球评价出炉:18个语言区好评率超90%

《黑神话:悟空》Steam在线人数已经不断打破纪录,连续三天刷榜,目前最高成绩超过241万。这个成绩也稳坐总榜第二,同时也是单机游戏的历史第一。 除了游玩人数之外,该作的评价口碑也非常出色,根据媒体汇总的数…

Flutter->`Flutter` 通过`ffi`调用`Rust`编译生成的产物.so文件(Android)和.a文件(iOS)接口方法

flutter_rust_ffi Flutter 通过ffi调用Rust编译生成的产物.so文件(Android)和.a文件(iOS)接口方法; 拾用本文您将获取以下技能: Rust编译.so文件的能力;Rust编译.a文件的能力;Flutter调用.so文件的能力;Flutter调用.a文件的能力; 附加Buff: Flutter环境安装指南;Rust环境安…

游戏行业如此竞争激烈,个人开发者是否仍存机会?

在当今这个数字化时代,游戏行业以其庞大的市场规模、高速的增长速度以及无限的创意空间,吸引了无数开发者投身其中。然而,随着技术的进步、资本的涌入以及大型游戏公司的强势扩张,游戏行业的竞争日益激烈,似乎形成了一…

选择合适系统

选择合适系统 原厂SDK系统 硬件兼容性 ⭐⭐⭐⭐⭐软件功能完善度 ⭐⭐⭐⭐⭐开发使用难度 ⭐⭐⭐⭐⭐烧写工具 全志自家烧录器。 TinaSDK-4.0 TF卡系统镜像 tina_v851se-tinyvision_uart0.img 默认TinaSDK编译出来 支持ADB 和默认SDK兼容性最好 tina-4.0_cameratest_ti…

使用知识图谱,大幅提升RAG准确性

大家好,图形检索—增强生成(GraphRAG)的发展势头日益强劲,已成为传统向量搜索检索方法的有力补充。这种方法利用图数据库的结构化特性,将数据组织为节点和关系,从而增强了检索信息的深度和上下文关联性。 知…

Stablediffusion有哪几种模型,小白入门必看!

前言 在Stable Diffusion中,模型有好几种,不同插件有不同的模型,分别作用于不同的功能。 今天卧龙君就带着大家一起来了解一下。 大模型:Stable Diffusion StableDiffusion大模型,可以理解为绘画风格集合&#xff…

16:【stm32】I2C的使用一:I2C片上外设的使用

I2C 1、片上外设1.1:寄存器与内部结构 2、通过I2C向外发送数据2.1:I2C的初始化2.1.1:初始化SCL和SDA2.1.2:使能时钟PCLK1(APB1)2.1.3:配置I2C1的参数 2.2:发送数据2.2.1:…

P2P 文件共享:现代网络中的高效文件传输

在互联网的世界中,不同应用程序的数据传输方法各异。P2P文件共享(Peer-to-Peer File Sharing) 作为一种高效的文件传输方式,使得用户可以在没有中央服务器的情况下直接进行文件交换。本文将详细介绍P2P文件共享的基本原理、优势及…

【通俗理解】CNN复杂度——卷积神经网络的计算成本解析

【通俗理解】CNN复杂度——卷积神经网络的计算成本解析 关键词提炼 #CNN复杂度 #卷积神经网络 #计算成本 #输入数据尺寸 #卷积核大小 #卷积核数量 #复杂度公式 第一节:CNN复杂度的类比与核心概念【尽可能通俗】 1.1 CNN复杂度的类比 CNN的复杂度就像是烹饪一道大…

引擎切换pdf识别简历分析

文章目录 1.EasyCode生成interview_history的crud1.在模板设置中手动指定逻辑删除的值2.生成代码,进行测试 2.PDF识别关键字1.引入依赖2.代码概览3.PDFUtil.java4.keyword1.EndType.java2.FlagIndex.java3.WordType.java4.KeyWordUtil.java 3.策略模式实现引擎切换&…

QT-window记事本

QT-window记事本 一、演示效果二、核心代码三、下载连接 一、演示效果 二、核心代码 #include <QMessageBox> #include <QFileDialog> #include <QDebug> #include <QProcess> #include <QDesktopServices> #include <QDateTime> #includ…

孙宇晨:区块链领域的坚韧领航者,以智慧铸就行业基石

​ 在区块链领域&#xff0c;孙宇晨以其卓越的智慧与不懈的韧性&#xff0c;成为行业内备受瞩目的领军人物。从创立波场 TRON 到引领去中心化的变革&#xff0c;孙宇晨始终以坚定的信念和独特的战略眼光推动着区块链技术的发展。 孙宇晨的成功不仅仅是因为他对技术的深入…

叉车驾驶员状态监控系统,司机身份安全识别,强化监管能力建设!

人脸识别技术作为人工智能领域的一个重要分支&#xff0c;已经广泛应用于安全识别、个人化推荐、社交网络等多个领域。其基于计算机视觉、图像处理、人脸检测、特征提取和人脸识别等先进技术&#xff0c;能够实现对人脸图像的精准分析和识别。在叉车驾驶场景中&#xff0c;AI人…