首页
社区
课程
招聘
[原创]入门级,无壳app登录算法还原并实现发包
2023-7-3 17:58 20819

[原创]入门级,无壳app登录算法还原并实现发包

2023-7-3 17:58
20819

前言

如题本文主要讲某app登录算法还原并实现发包的全过程,此app非常适合小白入门时拿来练手,
一个无壳无混淆的app,软件链接我会放在文章最下面,建议先看一下文章后,再去下载上手。

追踪关键代码

首先将apk拖入jadx进行关键字LoginActivity搜索,然后进行分析得到下图代码,
是手机号加密码的发包位置,其中可见手机号和密码都被传入了login,跟踪进去后,可见的相关代码如下图。
这里login又调用的私有方法doLogin,将手机号和密码传给了UserService里面,我们进行追踪,得到如下图代码。

这里可以看到了密码登录参数,手机号和密码被传入了这里,密码被传进了getMD5后,加密后存入了params又传入了getRSAParams,然后传入request,先继续追踪getMD5,

是一个普通的MD5,用python进行一下复现,代码如下。

部分代码复现:

1
2
3
4
5
6
7
8
9
10
import hashlib
 
def get_md5(val):
    m = hashlib.md5()
    m.update(val.encode('utf-8'))
    result = m.hexdigest()
    return result
 
print(get_md5("qwerty"))
# 结果:d8578edf8458ce06fbc5bb76a58c5ca4

然后返回追进getRSAParams里面看到下图代码,一层base64加密,还有RSA加密。

上frida,开始动刀子:

先用frida来对这个对象动刀子,得结果如下

1
2
3
4
5
6
7
*getRSAParams is called, params: {password=d8578edf8458ce06fbc5bb76a58c5ca4, os=android, mobile=15536263522, version=2.2.3}
 
getRSAParams ret value is {data=eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==,
 
sign=DmxjCCvf8aJnZNve4BQkcy6turIGzkE13DkIu9JSnJF7yUDp3ZUxANRnSn6+BCN2nEogZsHFOm0fzzTU/NMnEqijA8lklHoxZspzVOe6Hkp8jYRrzvf0PQIh25lEL2GGWSslgzEK710opNDoQUVHA95ArOv9FQN95HxZuj7ywio=,
 
timestamp=1687264102}*

传入之前的密码,python复现的MD5结果相同,传入getRSAParams后成了后面的三项,data sign 和 timestamp,然后这些数据和charles抓包得到的login数据相同。

再次追踪关键代码

需要先解决data,进行追踪得到下图代码。Base64加密与解密。

对其进行python复现得到以下代码,运行结果与frida打印信息相同,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import base64
import json
 
def encode(data, charset='UTF-8'):
    datas=json.dumps(data,separators=(',', ':'))
    try:
       return base64.b64encode(datas.encode(charset)).decode(charset)
    except Exception as e:
        print('Error', e)
        return None
 
data = {"password":"d8578edf8458ce06fbc5bb76a58c5ca4","os":"android","mobile":"15536263522","version":"2.2.3"}
result = encode(data)
print(result)
#结果:eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==

接着追踪sign的相关代码,得到下图代码

frida再次出刀

这里用frida进行hook,得到信息如下,可见这里是data与timestamp的拼接被传入了sign,被privateKey进行了加密,然后得到的sign。

1
2
3
sign is called, content: data=eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==&timestamp=1687276955,
privateKey: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMNGABIfN+iron2hbwB7mLK1Dm05V1qLZBILTDj7dypr+GJzQ9fk0V7gIIchpFG7pDQEXMbb2nj8VkNAIIDBaw7UY1h9n+sCBT+Xzz6BB2UxLBBMQVwOwv55tJkZ2YBcHFQDGz51HjxAonKJdHwGpjIp7bwdx375gybn2ic4qNuFAgMBAAECgYEAmcha7eqgASCKCx5DaMHtc2+bOPFblfcIjB1Rnd6L7mCxb/cOisutB2bCtykLW0LHAiAdYI5r87Ply3iJIF0yjU35I8aieDVmeaQXXQfpisimXLOmz6p4VlBzAkz493oXPEH81cHqbwnFkiFE3VVtHbCNoZqXlFWthIdae2kpjlECQQDyCMl09eyDBNGuzg1r4tAQ4CeZe7aCkEFwK2at76Raqz9NKrynBiZHsKLU3JedRm2eZ7JimUhsuKbbkS/mcxBrAkEAzop7/PyddSXGDFDECyuXtuEKyzzUvdGiyNmOexhSwTmTZ7QdQqe5p382yCQcY8RXxZ6W9CLjuukfa9I6Tcz/zwJAQbjpG318D8fLOHBzbIxWe36iwia51JJfcpoWc7zTIFvIAKhOOfyNgIISdULBWM+7DHyUD/oXlI4/oPe3zhgIqQJAQd3gFJnrDQTy19KZ8oYAaA30h0PrBG3qX+shiRgErCJUY+oIus0KY+Qp8EGz3A0tgJRGx6you17E6nmspksN+QJBAKhaBGeHqs0+Z5wtFcunuqc6hV7WlhBYCe5YSxBNSiaohXDr6nQwjiOY22Q3m8aInp9KS+lDwW0o4C58VARKyo8=
sign ret value is Z5dDahgsBp06lh76v1fos8GSqX4HdcB+vJBJgw4qWN6wJ52T2LkX9e/WqtLrb+biSg10J23/f0KIQW57GFLIWEgLwQRWgKaacvJJVMtoeETAXhgMvYU9LWmjFzrtd1EuRLyHLVkWik7IbJM5mySllm+bG/nfcPvohXp6ATRqy7k=

然后sign的python还原,生成的结果与frida的hook结果相同,sign完成,然后就是将已经实现的python代码进行拼接,并完成发包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from Crypto.Hash import SHA
from Crypto.Signature import pkcs1_15
from Crypto.PublicKey import RSA
import base64
 
def sign(content, private_key):
    private_key_value = base64.b64decode(private_key)
    key = RSA.import_key(private_key_value)
    # 计算 SHA-1 哈希值
    hash_value = SHA.new(content.encode('utf-8'))
    # RSA 的钥长度为 1024 位
    k = 1024
    # 计算最大签名长度,根据 Java 的实现方式计算
    em_len = k // 4
    h_len = 20
    t_len = 3
    s_len = em_len - h_len - t_len - 1
    # 进行签名
    signature_value = pkcs1_15.new(key).sign(hash_value)[:s_len]
    # 返回 Base64 编码的结果
    return base64.b64encode(signature_value).decode()
content = "data=eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==&timestamp=1687276955"
private_key = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMNGABIfN+iron2hbwB7mLK1Dm05V1qLZBILTDj7dypr+GJzQ9fk0V7gIIchpFG7pDQEXMbb2nj8VkNAIIDBaw7UY1h9n+sCBT+Xzz6BB2UxLBBMQVwOwv55tJkZ2YBcHFQDGz51HjxAonKJdHwGpjIp7bwdx375gybn2ic4qNuFAgMBAAECgYEAmcha7eqgASCKCx5DaMHtc2+bOPFblfcIjB1Rnd6L7mCxb/cOisutB2bCtykLW0LHAiAdYI5r87Ply3iJIF0yjU35I8aieDVmeaQXXQfpisimXLOmz6p4VlBzAkz493oXPEH81cHqbwnFkiFE3VVtHbCNoZqXlFWthIdae2kpjlECQQDyCMl09eyDBNGuzg1r4tAQ4CeZe7aCkEFwK2at76Raqz9NKrynBiZHsKLU3JedRm2eZ7JimUhsuKbbkS/mcxBrAkEAzop7/PyddSXGDFDECyuXtuEKyzzUvdGiyNmOexhSwTmTZ7QdQqe5p382yCQcY8RXxZ6W9CLjuukfa9I6Tcz/zwJAQbjpG318D8fLOHBzbIxWe36iwia51JJfcpoWc7zTIFvIAKhOOfyNgIISdULBWM+7DHyUD/oXlI4/oPe3zhgIqQJAQd3gFJnrDQTy19KZ8oYAaA30h0PrBG3qX+shiRgErCJUY+oIus0KY+Qp8EGz3A0tgJRGx6you17E6nmspksN+QJBAKhaBGeHqs0+Z5wtFcunuqc6hV7WlhBYCe5YSxBNSiaohXDr6nQwjiOY22Q3m8aInp9KS+lDwW0o4C58VARKyo8='
signature = sign(content, private_key)
print(signature)
#Z5dDahgsBp06lh76v1fos8GSqX4HdcB+vJBJgw4qWN6wJ52T2LkX9e/WqtLrb+biSg10J23/f0KIQW57GFLIWEgLwQRWgKaacvJJVMtoeETAXhgMvYU9LWmjFzrtd1EuRLyHLVkWik7IbJM5mySllm+bG/nfcPvohXp6ATRqy7k=

部分代码复现


先实现上图的login,实现手机号与密码的输入,传入parems,形成相同的login。
代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import hashlib
def login(mobile, word):
    params = {
        "password": get_md5(word),
        "os": "android",
        "mobile": mobile,
        "version": "2.2.3",
    }
    return params
 
def get_md5(val):
    m = hashlib.md5()
    m.update(val.encode('utf-8'))
    result = m.hexdigest()
    return result
 
mobile = input("请输入16手机号:")
parem = input("请输入密码:")
params = login(mobile,parem)
print(params)
#运行结果:{'password': 'a7026e07535acfd00f0a8a4a97992291', 'os': 'android', 'mobile': '13685446446', 'version': '2.2.3'}

接着实现下图代码:

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def jiaparams(params):
    # 将参数转换为 JSON 字符串,并进行 Base64 编码
    params_json = json.dumps(params,separators=(',', ':'))
    print("json转成功:",params_json)
    data = base64.b64encode(params_json.encode('utf-8')).decode('utf-8')
    print("data成功:",data)
 
    # 对加密后的参数进行签名
    signstr = f"data={data}&timestamp={times()}"
    print("时间戳:",times())
    print("拼接成功",signstr)
 
    signss = sign(signstr, pey)#sign加密的地方
    print("sign成功",signss)
 
    # 创建一个空字典用于存储加密后的参数
    result = {}
    result['data'] = data
    result['sign'] = signss
    result['timestamp'] = times()
    return result

最后代码拼接一起,得到下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import requests
  
import json
  
import time
  
import hashlib
  
from Crypto.Hash import SHA
  
from Crypto.Signature import pkcs1_15
  
from Crypto.PublicKey import RSA
  
import base64
  
  
  
pey = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMNGABIfN+iron2hbwB7mLK1Dm05V1qLZBILTDj7dypr+GJzQ9fk0V7gIIchpFG7pDQEXMbb2nj8VkNAIIDBaw7UY1h9n+sCBT+Xzz6BB2UxLBBMQVwOwv55tJkZ2YBcHFQDGz51HjxAonKJdHwGpjIp7bwdx375gybn2ic4qNuFAgMBAAECgYEAmcha7eqgASCKCx5DaMHtc2+bOPFblfcIjB1Rnd6L7mCxb/cOisutB2bCtykLW0LHAiAdYI5r87Ply3iJIF0yjU35I8aieDVmeaQXXQfpisimXLOmz6p4VlBzAkz493oXPEH81cHqbwnFkiFE3VVtHbCNoZqXlFWthIdae2kpjlECQQDyCMl09eyDBNGuzg1r4tAQ4CeZe7aCkEFwK2at76Raqz9NKrynBiZHsKLU3JedRm2eZ7JimUhsuKbbkS/mcxBrAkEAzop7/PyddSXGDFDECyuXtuEKyzzUvdGiyNmOexhSwTmTZ7QdQqe5p382yCQcY8RXxZ6W9CLjuukfa9I6Tcz/zwJAQbjpG318D8fLOHBzbIxWe36iwia51JJfcpoWc7zTIFvIAKhOOfyNgIISdULBWM+7DHyUD/oXlI4/oPe3zhgIqQJAQd3gFJnrDQTy19KZ8oYAaA30h0PrBG3qX+shiRgErCJUY+oIus0KY+Qp8EGz3A0tgJRGx6you17E6nmspksN+QJBAKhaBGeHqs0+Z5wtFcunuqc6hV7WlhBYCe5YSxBNSiaohXDr6nQwjiOY22Q3m8aInp9KS+lDwW0o4C58VARKyo8='
  
  
  
url = 'https://api.langshiyu.com/user/v2/account/login'
  
  
  
def md5(pwd_str):
  
    md5_obj = hashlib.md5()
  
    md5_obj.update(pwd_str.encode('utf-8'))
  
    return md5_obj.hexdigest()
  
  
  
def times():
  
    return str(int(time.time()))
  
  
  
def sign(content, private_key):
  
    private_key_value = base64.b64decode(private_key)
  
    key = RSA.import_key(private_key_value)
  
  
  
    # 计算 SHA-1 哈希值
  
    hash_value = SHA.new(content.encode('utf-8'))
  
  
  
    # RSA 的密钥长度为 1024 位
  
    k = 1024
  
  
  
    # 计算最大签名长度,根据 Java 的实现方式计算
  
    em_len = k // 4
  
    h_len = 20
  
    t_len = 3
  
    s_len = em_len - h_len - t_len - 1
  
  
  
    # 进行签名
  
    signature_value = pkcs1_15.new(key).sign(hash_value)[:s_len]
  
  
  
    # 返回 Base64 编码的结果
  
    return base64.b64encode(signature_value).decode()
  
  
  
def login(mobile, param):
  
    params = {
  
        "password": md5(param),
  
        "os": "android",
  
        "mobile": mobile,
  
        "version": "2.2.3",
  
    }
  
    return params
 
def jiaparams(params):
  
    # 将参数转换为 JSON 字符串,并进行 Base64 编码
  
    params_json = json.dumps(params,separators=(',', ':'))
  
    print("json转成功:",params_json)
  
    data = base64.b64encode(params_json.encode('utf-8')).decode('utf-8')
  
    print("data成功:",data)
  
    # 对加密后的参数进行签名
  
    signstr = f"data={data}&timestamp={times()}"
  
    print("时间戳:",times())
  
    print("拼接成功",signstr)
 
    signss = sign(signstr, pey)#sign加密的地方
  
    print("sign成功",signss)
 
    # 创建一个空字典用于存储加密后的参数
  
    result = {}
  
    result['data'] = data
  
    result['sign'] = signss
  
    result['timestamp'] = times()
  
    return result
  
mobile = input("请输入16位手机号:")
  
parem = input("请输入密码:")
  
params = login(mobile,parem)
  
paramss = jiaparams(params)
  
print("发包内容:",paramss)
  
fb= requests.post(url, paramss)
  
print("结果:",fb.text)bjsdm
  
#结果:{"code":0,"message":"该账户不存在","reqdata":{"data":0},"reqtime":"0.015238"}

最后总结:

有部分简单的代码实现,以及开头通过objection的SSL屏蔽后charles进行抓包进行找的关键点,都没有在文章里面写,不过这些都是不必要的,所以文章是有一小部分省略。
整体对小白非常友好的软件,难度不算很高,只要花时间就可以掌握关键点,进行复现。
app放在有道云文章里面,想尝试一下的小白可以进去拿一下。
算法还原过程有道云文章链接:https://note.youdao.com/s/Yf9jGzqk


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞5
打赏
分享
最新回复 (5)
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
charley_Lu 2023-7-3 19:11
2
0
666
雪    币: 19485
活跃值: (29158)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-7-3 21:11
3
1
感谢分享
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
CC小白p 2023-11-1 14:59
4
0
感谢,学习
雪    币: 227
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_fxzrovrw 2023-11-13 10:04
5
0
感谢大佬,学习一下!
雪    币: 62
活跃值: (566)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
万里星河 2023-11-13 20:25
6
0
支持一下
游客
登录 | 注册 方可回帖
返回