逆向签名_sign

逆向签名_sign

搜索关键字:"_sign,发现五个位置存在,通过观察可发现前四个属于同一个类,最后一个是单独的

  • 这时需要分析是否是第一个toSign进行加密的,hook toSign确认位置是否正确
    • 寻找toSign方法的包和类

 运行hook脚本,并在手机端进行登录

点击登录后,hook脚本并未执行,说明_sign加密不是这里

继续寻找确认是LaunchModel下的常量位置:public static final String KEY_SIGN = "_sign";

查找用例,发现很多都是signByType做加密,所以需要hook确认是否是这个位置

 

 

此时发现lambda$initRequestCommonParams$0和signByType传入的都是相同的参数i,所以直接hook lambda$initRequestCommonParams$0方法即可,继续寻找包名和类名 

编写hook脚本运行,然后进行登录,发现打印了内容 

  • 逆向udid
    通过上述步骤发现了加密的位置,后面需要破解SignManager.INSTANCE.signByType是如何加密的以及udid是怎么来的
  • 跳到声明,找到udid的位置

 

  • udid加密过程
    通过SecurityUtil.encode3Des,传入了context和字符串
  • 首先了解下字符串都包含什么
  • getIMEI(context)                          # 手机的IMEI
    HiAnalyticsConstant.REPORT_VAL_SEPARATOR  # 固定的字符串就是 | 
    System.nanoTime()                         # 手机开机时间 
    HiAnalyticsConstant.REPORT_VAL_SEPARATOR  # 固定的字符串就是 | 
    SPUtils.getDeviceId()                     # 设备id号
    
  • 在了解一下context是什么东西
  • 在安卓(Android)开发中,Context是一个非常重要的概念,它代表了应用程序的当前状态信息。每个Android应用程序都有一个Context,它允许应用程序访问系统资源和执行各种操作。Context通常是由Android系统传递给应用程序的各个组件(如Activity、Service、BroadcastReceiver等),以便它们能够与系统和其他组件进行交互。
    # getContext()  获取这个对象
    
    Context的主要作用包括:
    1、访问资源:通过Context,您可以访问应用程序的资源,如布局文件、字符串、图片等。这是因为Context持有对应用程序资源的引用,使您能够在应用程序中加载和使用这些资源。
    2、启动组件:通过Context,您可以启动其他组件,如Activity、Service、BroadcastReceiver等。例如,您可以使用Context启动一个新的Activity来打开新的界面。
    3、获取系统服务:通过Context,您可以获取系统级别的服务,例如获取系统的传感器、网络状态、存储管理等。这些服务是通过系统提供的服务注册表(Service Registry)来获取的。
    4、应用程序级别的操作:Context还可以用于执行应用程序级别的操作,如发送广播、获取应用程序包名、获取应用程序的数据目录等。
  • getIMEI(context)

 

public static String getIMEI(Context context) {
    if (PermissionsCheckerUtil.hasReadPhoenStatePermission(context)) {
        String imei = SPUtils.getIMEI();  # 从xml中 读取 SharedPreference中读取,一开始没有
        if (imeiIsNull(imei)) {# 手机设备id和网卡的mac地址混合得到一个串
            imei = ((TelephonyManager) context.getSystemService("phone")).getDeviceId();
            if (imeiIsNull(imei)) {
                String macAddress = ((WifiManager) context.getSystemService(NetworkUtil.NETWORK_TYPE_WIFI)).getConnectionInfo().getMacAddress();
                if (macAddress != null) {
                    try {
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        # 如果手机拿不到网卡信息,就返回一个随机uuid
                        # return UUID.randomUUID().toString();

                        context = getIMEIbyAndroidIDandUUID(context);
                    }
                    if (macAddress.length() > 0 && !isInBlackList(macAddress)) {
                        context = UUID.nameUUIDFromBytes(macAddress.getBytes("utf8")).toString();
                        imei = context;
                    }
                }
                # 获取随机uuid
                context = getIMEIbyAndroidIDandUUID(context);
                imei = context;
            }
            if (!imeiIsNull(imei)) {
                SPUtils.saveIMEI(imei); # 只要生成过以后,以后直接放到SharedPreference中xml中
            }
        }
        return imei;
    }
    return "sssss";
}
  • HiAnalyticsConstant.REPORT_VAL_SEPARATOR

 

  • System.nanoTime()
    java中是一个前系统时间的纳秒数,在安卓中这个表示手机开机时间
import random
nano_time = random.randint(5136066335773,7136066335773)
  • SPUtils.getDeviceId()
public static String getDeviceId() {
        return getSpUtil().getString(KEY_DEVICE_ID, "");
    }

#这个值经过测试发现是固定的,向后端发送请求:/tradercloud/v100/push/regdevice.ashx 拿回来存入的

SecurityUtil.encode3Des加密 

 

  • 从源码中可以看出这里是DES加密,有key和iv值,它所加密的明文就是上文中提到的字符串
  • 寻找iv值 iv = "appapich"

 寻找key值,从图中可以看到是在String desKey = AHAPIHelper.getDesKey(context);获取key值

继续跟踪getSignDeskey 

找到get3desKey,可以发现这里是使用JNI开发的,通过so生成的,所以后面需要逆向libnative-lib文件 

如果去逆向so文件有点太麻烦了,一般DES加密的key是固定的,所以我们可以通过多次hook得到DES的key值,如果发现就是固定的,那么就可以直接拿来使用了。

运行hook脚本,多次点击登录,可以得到相同的key值 DesKey值: appapiche168comappapiche168comap

  • 最后使用代码实现udid加密
# pip install pycryptodome
import base64
from Crypto.Cipher import DES3

BS = 8
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)

# 3DES的MODE_CBC模式下只有前24位有意义
key = b'appapiche168comappapiche168comap'[0:24]
iv = b'appapich'

plaintext = pad("cf15599b-5e93-3be5-a705-a39403227dfd|13359325995159|366586").encode("utf-8")

# 使用MODE_CBC创建cipher
cipher = DES3.new(key, DES3.MODE_CBC, iv)
result = cipher.encrypt(plaintext)
res = base64.b64encode(result)
print(res)

最终逆向_sign

treeMap.put("_sign", SignManager.INSTANCE.signByType(i, treeMap));

最后我们需要查看signByType是如何加密的

str:因为i是1,str=KEY_V2 KEY_V2是固定的值: W@oC!AH_6Ew1f6%8
sb.append(str):字符串拼接:先把固定字符串W@oC!AH_6Ew1f6%8拼接上,之后把循环的字典在进行拼接,后面又出现sb.append(str)是把字符串W@oC!AH_6Ew1f6%8拼到最后,然后使用MD5进行加密。

  • 整合代码即可得到_sign的值
import hashlib


def md5(data_string):
    obj = hashlib.md5()
    obj.update(data_string.encode('utf-8'))
    return obj.hexdigest()


data = "W@oC!AH_6Ew1f6%8"

data_dict = {
    "_appid": "atc.android",
    "appversion": "3.37.0",
    "channelid": "csy",
    "pwd": "e10adc3949ba59abbe56e057f20f883e",
    "udid": "fBricFHfQGIN%2Bam5jCTzG5yze/fO/pLP%2BIZgbuZKaGN0HiRwd1IPVQeCzaqV QpGO0zsKKfLxUlSpiG6FGQaNxA==",
    "username": "13111111111"
}

result = "".join(["{}{}".format(key, data_dict[key]) for key in sorted(data_dict.keys())])

un_sign_string = f"{data}{result}{data}"
sign = md5(un_sign_string).upper()
print(sign)

 

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值