APP逆向 day15 某物逆向

本文旨在学习,与平台方无关

一.前言

今天我们来逆向一下得物的主页面,来学习一些东西,主要这次是学习加密的逆向,因为高版本的比较难,这次我们就和大家说一个低版本的,低版本的会简单很多。

二.app版本选择与绕过强制更新

2.1 app版本选择

app版本我们选择 4.74.5,大家可以去豌豆荚下载,这里给出下载链接

2021得物v4.74.5老旧历史版本安装包官方免费下载_豌豆荚 (wandoujia.com)https://www.wandoujia.com/apps/6696712/history_v369大家可以看的出来这个版本是十分老的,因为这个比较简单,我们前期先和大家讲这个,没办法,app逆向太难了!因为这个是基础教程阶段,后期会给大家出专门的专题学习深层次的。

我们下载好只会直接adb install 到手机,看了前几期的教程,大家肯定都已经会了

2.2 绕过强制更新 

进来这个可以看到这个页面,之前上一期和大家讲了如何绕过强制更新,这个得物的强制更新十分简单,只需要断网再进来就可以了,再联网就可以了,这样就成功绕过app的强制更新了。

三.抓包分析

我们获取这个页面的数据

该app禁用了手机端代理,所以我们使用SocksDroid代理(上期我们已经配置过了,以后我们都可以直接采取SocksDroid代理),这里就不带大家再次配置了

这里可以看到我们想要的数据

地址:https://app.dewu.com/sns-rec/v1/recommend/all/feed

抓包分析再改包发现,这些值都不用逆向,但是其他的接口有点难度,先通过学习的目的和大家讲讲这个·

-newSign
-X-Auth-Token

-uuid (duuid)

四.破解newSign

4.1 定位位置

我们把下载好的apk文件拖入jadx进行反编译

我们搜索"newSign"发现了很多如下

然后我们还想,前面五个都是在一个包下,这里我告诉大家就是第五个,点进去可以发现

我们再点进去,可以发现

 这么多,我们hook一下这个,直接说一下流程,启动手机端frida,端口转发,获取正在运行app的名字,进行hook,查看和抓包的是否一样

4.2 hook加密位置

用正常的hook代码看不出来参数,所以我们要处理一下,这里给出hook代码

import frida
import sys

# attach:手动运行app,打开APP后,再运行Hook脚本。

# 连接手机设备
rdev = frida.get_remote_device()

session = rdev.attach("得物(毒)")  # 写获取手机上的软件名

scr = """ 
Java.perform(function () {
    var RequestUtils = Java.use("com.shizhuang.duapp.common.utils.RequestUtils"); // Java.use("需要hook的地方的包名和大类名")
    RequestUtils.c.implementation = function(map,int){
        console.log("第一个参数是:::",map)
        console.log('第一个参数类型',JSON.stringify(map))
        var Map = Java.use('java.util.HashMap');
        var obj = Java.cast(map, Map);
        console.log('第一个参数字符串形式',obj.toString()); // 把map转成字符串形式打印出来
        
        console.log("第二个参数是:::",int)
        
        var res = this.c(map,int);  //调用原来函数加密密文
        console.log("最后结果是:::",res);
        console.log("========")
        return res;
    }
});
"""

script = session.create_script(scr)


def on_message(message, data):
    print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

这个可以看成是遇到字典类型就这么写

 hook出来的结果和抓包抓到的一致

第一个参数:{abValue=1, deliveryProjectId=0, abRectagFengge=0, abType=social_brand_strategy_v454, limit=20, lastId=, abRecReason=0, abVideoCover=2}

第二个参数: 十三位时间戳

第一个参数不就是载荷的字典吗

 4.3 分析代码

1. 把uuid,platform,v,loginToken,timestamp放入map中
map.put("uuid", DuHttpConfig.d.getUUID());
map.put("platform", "android");
map.put("v", DuHttpConfig.d.getAppVersion());
map.put("loginToken", DuHttpConfig.d.getLoginToken());
map.put("timestamp", String.valueOf(j2));

2. 把map转成ArrayList,并进行排序
ArrayList arrayList = new ArrayList(map.entrySet());
Collections.sort(arrayList, new Comparator<Map.Entry<String, String>>()

3.  构建字符串
StringBuilder sb = new StringBuilder();
sb.append()

4. 执行AESEncrypt.encode 加密 
AESEncrypt.encode(DuHttpConfig.f15796c, sb2)

5. 把返回结果当参数传入a中
a(AESEncrypt.encode(DuHttpConfig.f15796c, sb2));

4.4 查看a函数 

点击a函数进去 

看到这个就是一个md5(没有加盐),我这里就不hook了,大家也可以hook一下看看。

4.5 查看AESEncrypt.encode

看名字就知道是aes加密,毕竟这个难度的肯定不会是假的。当然我们还是得确定一下

点击去

进来之后发现,那我们先读一下逻辑

在getByteValues获取一个个字符串,然后再把字符串的取反,然后再进行加密 ,那我们现在就来hook一下这个encode

我们发现在这个类下不止有一个encode,这是因为重载了,所以hook代码要改一下

import frida
import sys

# attach:手动运行app,打开APP后,再运行Hook脚本。

# 连接手机设备
rdev = frida.get_remote_device()

session = rdev.attach("得物(毒)")  # 写获取手机上的软件名

scr = """ 
Java.perform(function () {
    var AESEncrypt = Java.use("com.duapp.aesjni.AESEncrypt"); // Java.use("需要hook的地方的包名和大类名")
    AESEncrypt.encode.overload('java.lang.Object', 'java.lang.String').implementation = function(obj,str){
        console.log("第一个参数是:::",obj))
        console.log("第二个参数是:::",str)
        var res = this.encode(obj,str);  
        console.log("最后结果是:::",res);
        console.log("========")
        return res;
    }
});
"""

# 重载方法中间要加上.overload('java.lang.Object', 'java.lang.String') 里面写你要hook的参数的类型名 j

script = session.create_script(scr)


def on_message(message, data):
    print(message, data)


script.on("message", on_message)
script.load()
sys.stdin.read()

主要是重载方法要额外加上overload("参数类型")来区分用的是哪个方法

 根据载荷可以判断出是下面的进行了加密,把值进行md5签名就可以得到和抓包一样,这里大家可以去测试一下

4.6 查看getByteValues返回值是否固定 

我们在上面都能看见,aes算法是在jni 里写的加密逻辑,所以的encode和getByteValues是在so文件中写的逻辑,但是我们可以提前hook一下getByteValues是不是固定值,那我们现在来hook一下

这里hook代码就是正常的hook代码,和以前说的一样,这里就不给出了

hook结果

 发现始终不变,都是101001011101110101101101111100111000110100010101010111010001000101100101010010010101110111010011101001011101110101100101001100110000110100011101010111011011001101001101011101010100001101000011

然后进行取反,然后进行encode,encode我们就要去so文件里找了,这里我前面都说过了

名字就叫liblibJNIEncrypt.so,找到这个so文件放到idea里面反编译 

我们进来之后点击这个export,然后就可以了

4.7 读so文件找到encrypt逻辑 

我们首先要知道是动态注册还是啥,动态注册肯定是 java_包名_类名....

动态注册的话就要找Onload找对应关系,刚好我们看到了个Onload,点进去,再隐藏变量,导入jni.h

 jni的标准写法

jclass clazz = (*env)->FindClass(env, "com/justin/s11day13/Dynamic");
int res = (*env)->RegisterNatives(env, clazz, gMethods, 2);

 我们可以知道off_15010就是gMethod,我们就要点进去找对应关系

可发现encode就是对应的c语言函数名字,点进去,按f5

可以看到这个a1是jni中的env不用管,所以重点关注就是这个v18,而v18在上面,传入两个参数,我们可以hook一下得到传入的两个参数,没错,hookc语言

import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")

scr = """
Java.perform(function () {
    // 去libJNIEncrypt.so找到AES_128_ECB_PKCS5Padding_Encrypt内存地址
    // 放入文件名和函数名
    var addr_func = Module.findExportByName("libJNIEncrypt.so", "AES_128_ECB_PKCS5Padding_Encrypt");
    Interceptor.attach(addr_func, {
        onEnter: function(args){
            // 函数开始执行,触发onEnter,所有参数都在args
            console.log("--------------------------执行函数--------------------------");
            console.log("参数1:", args[0].readUtf8String());
            console.log("参数2:", args[1].readUtf8String());
        },
        onLeave: function(retValue){
            // 函数离开,触发onLeave,返回值在:retValue
            console.log("返回值:", retValue.readUtf8String());
        }

    })

});
"""

script = session.create_script(scr)
def on_message(message, data):
    print(message, data)
script.on("message", on_message)
script.load()
sys.stdin.read()

hook完的结果 

 参数二这不明显就是aes的key吗

我们再进去看一下

aesecb,再进行base64编码,大家可以反解一下,结果是正确的,所以,前面得到的那一大堆字符串好像没有用上。。。

接下来两个就简单和大奖讲

五.破解uuid

1 之前不知道的
    uuid:ee13885e68d76ed4 ,这个手机一直没变 #map.put("uuid", DuHttpConfig.d.getUUID());
        -猜:手机设备id号
        -搜:hashMap.put("uuid",
            -找一个生成uuid的位置,读代码,还原,放到到咱们加密中测试,能不能正常发送请求
    loginToken:写死的 platformandroid # DuHttpConfig.d.getLoginToken()
                       
                       
2 破 X-Auth-Token ,也用了uuid
hashMap.put("uuid", HPDeviceInfo.b(BaseApplication.c()).a((Activity) null));
telephonyManager.getDeviceId() # 设备id号
3 模拟生成即可
def generate_imei():
    return "".join(random.choices('0123456789abcdef', k=15))   

六.破解X-Auth-Token

1 搜索
2 hashMap.put("X-Auth-Token", ServiceManager.a().getJwtToken());
3 结合:抓包抓到的:
eyJhbGciOiJSUzI1NiJ9. # 头,固定的

eyJpYXQiOjE3MTUzNDI3NzIsImV4cCI6MTc0Njg3ODc3MiwiaXNzIjoiZWUxMzg4NWU2OGQ3NmVkNCIsInN1YiI6ImVlMTM4ODVlNjhkNzZlZDQiLCJ1dWlkIjoiZWUxMzg4NWU2OGQ3NmVkNCIsInVzZXJJZCI6MjE0MTY4MTg1MywidXNlck5hbWUiOiLlvpfniallci1KMEQ4UzZHNSIsImlzR3Vlc3QiOnRydWV9.
荷载:放用户数据

eGbSAsGkJTZkVsXbJFXqbGphbeyOVIrm2BVj7AfqM8YLYOyhUo79Ae0u0ToUa2OGEOw99MFTZ39WJxlnGrg07Fdf7JIDgXqW5HmTPLMcJHM8w-6t5MlrpfSBMuqBMQOi-n7IlntiAp0hcRRX1D3mZ31X37GSwUN_eU3H3HREzd1kt-F8qynu_3Y9a59U7iFWXqKjKFTy_qugB0KHYJrVfYSC4GBvhJIN3I7zBCpR0JNQMVlf9YeXvtYp-cmE_NlWs-r9wqQd_BgXy20EwdpuF_hWhqIPrkNfEwJD33wQfpWmQkZ7gKh5cXvhqAQl1Vd8y9sEW56o6GDLgip7qlcPvw
签名:通过头和荷载签名得到

4 一定是后端给的

5 清空数据,重新打开app--》有个接口,返回这个数据--》保存到手机上,以后每次发请求携带

这个数据格式,一看就是jwt格式的

 什么是jwt

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在不同实体之间安全地传输信息。它由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature)。它们通过.连接组成一个形如 header.payload.signature 的字符串。

具体来说:

  1. 头部(Header): 头部通常由两部分组成:令牌的类型(即 JWT)和所使用的签名算法,例如 HMAC SHA256 或 RSA。头部会被 Base64 编码后形成JWT的第一部分。

  2. 载荷(Payload): 载荷包含了JWT的主要信息,这些信息可以被安全地传输和存储,例如用户的ID、角色或者其他相关数据。载荷也会被 Base64 编码后形成JWT的第二部分。

  3. 签名(Signature): 签名是由头部、载荷以及一个密钥(只有服务器才知道)生成的,用于验证消息的完整性。签名可以确保在传输过程中JWT没有被篡改。签名是对编码后的头部、编码后的载荷、以及一个密钥使用指定算法生成的哈希值。

JWT 的主要特点包括:

  • 自包含性(Self-contained): JWT本身包含了所有需要传递的用户信息,因此减少了需要查询数据库的次数。

  • 易于传输(Compact and Easy to Transmit): JWT是一个紧凑的字符串,适合在URL、POST参数或HTTP标头中传输。

  • 安全性(Security): JWT使用签名来验证发送者的身份,并确保在传输过程中数据没有被篡改。

  • 无状态(Stateless): 由于JWT本身包含了所需的信息,服务器不需要在存储用户的会话信息。这使得服务端可以更容易地扩展和分布式。

JWT通常用于认证和授权,典型的应用场景包括单点登录(Single Sign-On, SSO)系统和API的安全通信。

jwt分析

# 1 jwt  json web token-->前后端认证的方式--》三段
eyJhbGciOiJSUzI1NiJ9. # 头,固定的

eyJpYXQiOjE3MTUzNDI3NzIsImV4cCI6MTc0Njg3ODc3MiwiaXNzIjoiZWUxMzg4NWU2OGQ3NmVkNCIsInN1YiI6ImVlMTM4ODVlNjhkNzZlZDQiLCJ1dWlkIjoiZWUxMzg4NWU2OGQ3NmVkNCIsInVzZXJJZCI6MjE0MTY4MTg1MywidXNlck5hbWUiOiLlvpfniallci1KMEQ4UzZHNSIsImlzR3Vlc3QiOnRydWV9.
# 荷载:放用户数据

eGbSAsGkJTZkVsXbJFXqbGphbeyOVIrm2BVj7AfqM8YLYOyhUo79Ae0u0ToUa2OGEOw99MFTZ39WJxlnGrg07Fdf7JIDgXqW5HmTPLMcJHM8w-6t5MlrpfSBMuqBMQOi-n7IlntiAp0hcRRX1D3mZ31X37GSwUN_eU3H3HREzd1kt-F8qynu_3Y9a59U7iFWXqKjKFTy_qugB0KHYJrVfYSC4GBvhJIN3I7zBCpR0JNQMVlf9YeXvtYp-cmE_NlWs-r9wqQd_BgXy20EwdpuF_hWhqIPrkNfEwJD33wQfpWmQkZ7gKh5cXvhqAQl1Vd8y9sEW56o6GDLgip7qlcPvw
# 签名:通过头和荷载签名得到

# 2 一定是后端返回的
    用户登录,返回token
    用户没登录--》返回一个isGuest的token
    
# 3 抓包,找到token

总结 

这次讲的很长,主要是学习技术和概念。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

往日情怀酿作酒yx

往日情怀酿作酒 感谢你的支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值