usb_network_gate 破解

usb_network_gate 破解

逆向分析

激活码验证请求:

image-20220912155419045

响应为密文,反编译so文件,发现一个RSA公钥。

image-20220912155344674

使用公钥解密后:

1
hid=0619db3b0bc88d3f&license_key_code=A8F73-720BD-8F4B8-A57E4-93361&licenseName=Single License, 1 shared USB device &product_id=123&product_name= USB Network Gate&product_version=10&serverDate=2022-09-12&activationDate=&nextActivation=&firstActivation=&errorCode=NO_AVAILABLE_ACTIVATIONS&key_type=2&registed_name=zhigaows null&license_options=1&key_options=14&serverTime=1662969248&activation_param=&subscription=0&hash=5087915c33478d5280686883baf4b758

试用KEY响应解密:

1
hid=2e3ec7a107834ed8&license_key_code=292AA-7E6FB-8D336-F03EF-F8E02&licenseName=Single License, 1 shared USB device &product_id=123&product_name= USB Network Gate&product_version=10&serverDate=2022-09-12&activationDate=2022-09-12&nextActivation=2022-09-15&firstActivation=2022-09-12&errorCode=0&key_type=2&registed_name=test test2&license_options=1&key_options=14&serverTime=1662953951&activation_param=&subscription=0&hash=99b06159bc364aa8dc7d8b0a105be8e5

破解过程

生成一对RSA密钥

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
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1j5Th/zLm0kuZiQh1LCg
2UHWd3AFZyQS1a7twkeuxRw9FzeR2mk8/B3O9wP9/B/O11d3hpJOxZNSwGAuDpLH
6COimw9ELEYpGaRAovFoyaBHcchoBtvVtqItOACbFFn8N86mFsb584rRkwhjvNeH
8Pputv9A8KBwciZKImfSogr9N3nvXKlyq3iMsr3Uaq3jOzVI8kHu0QDar7knpDxq
zdTbWPGjjb+XHP2Ud9IL0bIKDxDk6zFKovtAV3A2aElyVOKW3ta2HmLY6yFomJ0f
en1SnuEl6YeWtfSuqDM09Qf1/GN9MyCdebAXiSjGqnR4Pfyk0pcn9unorIJ8riAk
uwIDAQAB
-----END PUBLIC KEY-----


-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDWPlOH/MubSS5m
JCHUsKDZQdZ3cAVnJBLVru3CR67FHD0XN5HaaTz8Hc73A/38H87XV3eGkk7Fk1LA
YC4OksfoI6KbD0QsRikZpECi8WjJoEdxyGgG29W2oi04AJsUWfw3zqYWxvnzitGT
CGO814fw+m62/0DwoHByJkoiZ9KiCv03ee9cqXKreIyyvdRqreM7NUjyQe7RANqv
uSekPGrN1NtY8aONv5cc/ZR30gvRsgoPEOTrMUqi+0BXcDZoSXJU4pbe1rYeYtjr
IWiYnR96fVKe4SXph5a19K6oMzT1B/X8Y30zIJ15sBeJKMaqdHg9/KTSlyf26eis
gnyuICS7AgMBAAECggEABTAfL7TUbV85S2nmk9LvIjOw8NV6nAPInPoOcdzwg39q
j0xeMM7JlkVWhK7JELjodlh84Xpbg/dMUrJBi0LMqndqcZzOBtBwIFnU4t07r149
PKkgs8HRDAojVC7XkoeXTvanGPgah2Y+KvRSDfmeJhy6X54QwYLxFiKNOwYOmLPm
EhWLXwxgtyD7aVCstaxM9vFWoBNBEz5S876SeEkMb8tvJbeM0DGJMyQU0jdPE2jm
RJUomQeI/e/Mw1Pn7YtfMarO5hWQ/xQwvIyIGYN4UEiFZuAGxxWHwwZ6IYGAWD6z
w1ldVNC7OmKDA3GCVSsXmDCycKyLwpbavZ+m6qaBMQKBgQDzWde4TRcEQoWBZ6V4
W6yd6mpaeQr0JWrOcAYCGDIfjDw12eqUyGPrVonnQlGQ9IOTrG2NWH+VYTqXyTpY
/kpZDP3mfdJFENSShjZcaSVTCV70Ff/iq+W7dlrYYxn3vb6gL3W3vyw9UDaPh2hw
oG8K+/a6tW2oCSBoUz49fMLUiQKBgQDhYSn3FhCdRQajCelqCVuAejNehIakczls
ik1TOI8Y892JFMXNe0KaungzDIILxC6s/elSYXkVpP4BQXrdV2W8CXc6o7mUmTh8
zEZn9sGLooGgSRfXd6eG9GRBtJ+ij1rBYu1Ww8sSttMn33ga4+IjVQK/d9IpHzXZ
UKddobrmIwKBgQCLr6+fMEkc/d5KqKcYDm9oei2wXzjFalEDzNP2EdN7vnC1bLA1
1U1dwmAIak4Nyf1ZICoGb51UruoGRNEEUnt04HtB+klfsciDjxzXMNfm8UMbEyG+
CHQnhkmhWkmGptksc/G7lrYbP9cBznVJ6R20jWtUQ159jTeYCuh4PQxNGQKBgHGD
bo90HYoYae9T3qv5aoya/6RrOyU4o4N3ZNq1cd0vdjTEsZt8qV8k68VKV3V1qcyX
VhyX21R7dTNMt4Ujr6m1Wse+Doo5Yan5eLyEG5cokgEYz1lGeqoODTDKuFw3t44P
NVqt4mnaix9Z/jQM8qi4+FlXEBKGFakOZj1SZ7/hAoGBAKgINEQpqzqT37VGWaFA
39OsHOusmBaC7m+vzjuTalsW6OvzgYNQZNu7InJqfWKMEtgVu8vnXpcgtKW19ofj
AREgbn9sTya217y4MIn/o7DE5p21PAi142rKx4N2m1IC/cmlTvafTWnlWFNvmIeX
6pZ3LUcHEk8u9dV6Vm+DfljM
-----END PRIVATE KEY-----

使用新公钥替换so中公钥,再用新私钥加密响应明文。

尝试后失败,一番调试后发现会从另一个地方读取原始公钥(未找到具体地方),尝试使用frida hook。

1
2
3
4
5
6
7
8
9
10
11
12
Interceptor.attach(Module.findExportByName("libc.so", "strlen"), {
onEnter: function (args) {
var str = Memory.readCString(args[0]);
if (str.indexOf('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwknN5/81qIoM+k1WEc/5') > -1) {
console.log('patch....')
Memory.writeUtf8String(args[0], "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1j5Th/zLm0kuZiQh1LCg\r\n2UHWd3AFZyQS1a7twkeuxRw9FzeR2mk8/B3O9wP9/B/O11d3hpJOxZNSwGAuDpLH\r\r6COimw9ELEYpGaRAovFoyaBHcchoBtvVtqItOACbFFn8N86mFsb584rRkwhjvNeH\r\n8Pputv9A8KBwciZKImfSogr9N3nvXKlyq3iMsr3Uaq3jOzVI8kHu0QDar7knpDxq\r\nzdTbWPGjjb+XHP2Ud9IL0bIKDxDk6zFKovtAV3A2aElyVOKW3ta2HmLY6yFomJ0f\r\nen1SnuEl6YeWtfSuqDM09Qf1/GN9MyCdebAXiSjGqnR4Pfyk0pcn9unorIJ8riAk\r\nuwIDAQAB\r\n-----END PUBLIC KEY-----");
}
},
onLeave: function (retval) {

}
});

使用frida hook后成功。

客户端没有校验服务端返回的hash参数,不然还得逆向还原hash计算流程。

源码:

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
from flask import Flask
from flask import request
from flask import Response
import M2Crypto
import base64
from M2Crypto import *
from base64 import b64encode, b64decode
import time

app = Flask(__name__)

PRIVATE_KEY = '''-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDWPlOH/MubSS5m
JCHUsKDZQdZ3cAVnJBLVru3CR67FHD0XN5HaaTz8Hc73A/38H87XV3eGkk7Fk1LA
YC4OksfoI6KbD0QsRikZpECi8WjJoEdxyGgG29W2oi04AJsUWfw3zqYWxvnzitGT
CGO814fw+m62/0DwoHByJkoiZ9KiCv03ee9cqXKreIyyvdRqreM7NUjyQe7RANqv
uSekPGrN1NtY8aONv5cc/ZR30gvRsgoPEOTrMUqi+0BXcDZoSXJU4pbe1rYeYtjr
IWiYnR96fVKe4SXph5a19K6oMzT1B/X8Y30zIJ15sBeJKMaqdHg9/KTSlyf26eis
gnyuICS7AgMBAAECggEABTAfL7TUbV85S2nmk9LvIjOw8NV6nAPInPoOcdzwg39q
j0xeMM7JlkVWhK7JELjodlh84Xpbg/dMUrJBi0LMqndqcZzOBtBwIFnU4t07r149
PKkgs8HRDAojVC7XkoeXTvanGPgah2Y+KvRSDfmeJhy6X54QwYLxFiKNOwYOmLPm
EhWLXwxgtyD7aVCstaxM9vFWoBNBEz5S876SeEkMb8tvJbeM0DGJMyQU0jdPE2jm
RJUomQeI/e/Mw1Pn7YtfMarO5hWQ/xQwvIyIGYN4UEiFZuAGxxWHwwZ6IYGAWD6z
w1ldVNC7OmKDA3GCVSsXmDCycKyLwpbavZ+m6qaBMQKBgQDzWde4TRcEQoWBZ6V4
W6yd6mpaeQr0JWrOcAYCGDIfjDw12eqUyGPrVonnQlGQ9IOTrG2NWH+VYTqXyTpY
/kpZDP3mfdJFENSShjZcaSVTCV70Ff/iq+W7dlrYYxn3vb6gL3W3vyw9UDaPh2hw
oG8K+/a6tW2oCSBoUz49fMLUiQKBgQDhYSn3FhCdRQajCelqCVuAejNehIakczls
ik1TOI8Y892JFMXNe0KaungzDIILxC6s/elSYXkVpP4BQXrdV2W8CXc6o7mUmTh8
zEZn9sGLooGgSRfXd6eG9GRBtJ+ij1rBYu1Ww8sSttMn33ga4+IjVQK/d9IpHzXZ
UKddobrmIwKBgQCLr6+fMEkc/d5KqKcYDm9oei2wXzjFalEDzNP2EdN7vnC1bLA1
1U1dwmAIak4Nyf1ZICoGb51UruoGRNEEUnt04HtB+klfsciDjxzXMNfm8UMbEyG+
CHQnhkmhWkmGptksc/G7lrYbP9cBznVJ6R20jWtUQ159jTeYCuh4PQxNGQKBgHGD
bo90HYoYae9T3qv5aoya/6RrOyU4o4N3ZNq1cd0vdjTEsZt8qV8k68VKV3V1qcyX
VhyX21R7dTNMt4Ujr6m1Wse+Doo5Yan5eLyEG5cokgEYz1lGeqoODTDKuFw3t44P
NVqt4mnaix9Z/jQM8qi4+FlXEBKGFakOZj1SZ7/hAoGBAKgINEQpqzqT37VGWaFA
39OsHOusmBaC7m+vzjuTalsW6OvzgYNQZNu7InJqfWKMEtgVu8vnXpcgtKW19ofj
AREgbn9sTya217y4MIn/o7DE5p21PAi142rKx4N2m1IC/cmlTvafTWnlWFNvmIeX
6pZ3LUcHEk8u9dV6Vm+DfljM
-----END PRIVATE KEY-----
'''

def rsa_pkcs1_encrypt(plaintext, private_key):
"""
RSA私钥加密
:param plaintext: 明文
:param private_key: 私钥
:return:
"""

# 密钥长度为1024时,最大加密块117
# 密钥长度为2048时,最大加密块245
max_encrypt_block = 245
padding = RSA.pkcs1_padding
plainBytes = plaintext.encode(encoding='utf-8')
plaintext_length = len(plainBytes)
# 不需要分段加密
if plaintext_length < max_encrypt_block:
return b64encode(private_key.private_encrypt(plainBytes, padding)).decode(encoding='utf-8')
# 分段加密
offset = 0
ciphers = []
while plaintext_length - offset > 0:
if plaintext_length - offset > max_encrypt_block:
ciphers.append(private_key.private_encrypt(plainBytes[offset:offset + max_encrypt_block], padding))
else:
ciphers.append(private_key.private_encrypt(plainBytes[offset:], padding))
offset += max_encrypt_block
return b64encode(b"".join(ciphers)).decode(encoding='utf-8')


def reg(hid, license_key_code):
l = ['hid={}'.format(hid),
'&license_key_code={}'.format(license_key_code),
'&licenseName=Single License, 1 shared USB device ',
'&product_id=123',
'&product_name= USB Network Gate',
'&product_version=10',
'&serverDate=2022-09-12',
'&activationDate=2022-09-12',
'&nextActivation=2099-09-15',
'&firstActivation=2022-09-12',
'&errorCode=0',
'&key_type=2',
'&registed_name=test test2',
'&license_options=1',
'&key_options=14000',
'&serverTime={}'.format(int(time.time())),
'&activation_param=',
'&subscription=0',
'&hash=99b06159bc364aa8dc7d8b0a105be8e5']
bio = M2Crypto.BIO.MemoryBuffer(PRIVATE_KEY.encode())
rsa_pri = M2Crypto.RSA.load_key_bio(bio)

#rsa_pri = M2Crypto.RSA.load_key('/tmp/new/private.pem')
encrypt_data = b''
for msg in l:
primsg = rsa_pkcs1_encrypt(msg, rsa_pri)
encrypt_data += base64.b64decode(primsg.encode())
return base64.b64encode(encrypt_data).decode()


@app.route('/')
def index():
return __file__


@app.route('/activator', methods=["POST"])
def activator():
hid = request.form.get('hid')
license_key_code = request.form.get('license_key_code')
if hid is None or license_key_code is None:
return 'error'
data = reg(hid, license_key_code)
return data


if __name__ == '__main__':
app.debug = False
app.run(host='127.0.0.1', port=9002)