一
背景
二
代码分析
java层的静态分析
so层分析verify()
从逆向的思维来考虑:既然要满足 v28 = 3g6L2PWL2PXFmR+7ise7iq==的逻辑,我们就反向推算生成的算法。
v28[0] =3
aAbcdefghijklmn[v30[0]] = 3
v30[0] = 29
那么:
v30[1] = 6
v30[2] = 32
.
.
.
以此类似我们用python还原他的算法:
aAb_str = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ" # aAbcdefghijklmn 的定义
def get_aAb_str_len(ab_str):
i = 0
for ab in aAb_str:
if ab == ab_str:
return i
i = i + 1
# 反向求解出 v12 的值
s1 = "3g6L2PWL2PXFmR+7ise7iq=="
s1_one = "3g6L2PWL2PXFmR+7ise7iq"
if __name__ == '__main__':
v30= []
v5 = 0
for one in s1_one:
# aAbcdefghijklmn[v30[v5]] = one
get_len = get_aAb_str_len(one)
v30.append(get_len)
# print(get_len)
print("v30",v30)
aAb_str = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ" # aAbcdefghijklmn 的定义
s1 = "3g6L2PWL2PXFmR+7ise7iq=="
v30 = [29, 6, 32, 49, 28, 53, 60, 49, 28, 53, 61, 43, 12, 55, 36, 33, 8, 18, 4, 33, 8, 16]
print("v30 ",v30)
v7 = 16
v5 = 0
s1_arr = [""] * (len(s1))
while v7 >= 1:
if v7 < 3:
s1_arr[v5] = aAb_str[v30[v5]]
s1_arr[v5 + 1] = aAb_str[v30[v5 + 1]]
print("s1_arr[v5 + 1] ", s1_arr[v5 + 1], v5 + 1, v30[v5 + 1])
if v7 == 1:
v9 = "="
s1_arr[v5 + 2] = "="
v8 = v5 + 3
else:
print(" s1_arr[v5 + 2] ", s1_arr[v5 + 2], v5 + 2)
v8 = v5 + 3
v9 = "="
v5 = v8
else:
s1_arr[v5] = aAb_str[v30[v5]]
s1_arr[v5 + 1] = aAb_str[v30[v5 + 1]]
s1_arr[v5 + 2] = aAb_str[v30[v5 + 2]]
v8 = v5 + 3
v5 = v5 + 4
v9 = aAb_str[v30[v8]]
print("v8 = ", v8)
s1_arr[v8] = v9
print(s1_arr)
print("v7 : ", v7)
v7 = v7 - 3
print("v7 : ", v7)
print("s1_arr ", s1_arr)
print(v30)
v30=[29, 6, 32, 49, 28, 53, 60, 49, 28, 53, 61, 43, 12, 55, 36, 33, 8, 18, 4, 33, 8, 16]
sub_77C(a3_val, (__int64)s, a3_len % 3);
sub_8A4(s, (__int64)v30);
frida hook sub_77C
function hook_so() {
Java.perform(function () {
var syms_addr = Process.getModuleByName("libart.so").enumerateSymbols();
var GetStringUTFChars_addr = NULL;
for (var index = 0; index < syms_addr.length; index++) {
const sym_addr = syms_addr[index];
if (sym_addr.name.indexOf("Check") == -1 && sym_addr.name.indexOf("GetStringUTFChars") >= 0) {
GetStringUTFChars_addr = sym_addr.address;
}
}
Interceptor.attach(GetStringUTFChars_addr, {
onEnter: function (args) {
// console.log("GetStringUTFChars_addr args : ", ptr(args[1]).readCString())
}, onLeave: function (retval) {
// console.log("GetStringUTFChars_addr retval ", hexdump(retval))
}
})
var module_addr = Module.findBaseAddress("libnative-lib.so");
console.log("module_addr ", module_addr);
var sub_8A4 = module_addr.add(0x8A4);
var sub_77C = module_addr.add(0x77C);
console.log("sub_92C ", sub_77C)
var v127 = NULL
Interceptor.attach(sub_77C, {
onEnter: function (args) {
console.log("sub_77C arg[0", args[0].readCString(), hexdump(args[0]))
console.log("sub_77C arg[1", args[1].readCString(), hexdump(args[1]))
// console.log("sub_77C arg[2",args[2].readCString(),hexdump(args[2]))
// console.log("arg[2", args[2])
v127 = args[1]
}, onLeave: function (retval) {
console.log("retval ", v127.readCString())
console.log("retval ", hexdump(v127))
//011000010110001001100011011100100110010101110111011101110110010100
}
})
var v12 = NULL
Interceptor.attach(sub_8A4, {
onEnter: function (args) {
console.log("sub_8A4 arg[0", args[0].readCString(), hexdump(args[0]))
v12 = args[1]
console.log("sub_8A4 v12 args[1] = ", args[1])
}, onLeave: function (retval) {
console.log("sub_8A4 retval v12 = ", v12)
console.log("sub_8A4 retval v12 s = ", ptr(v12).readByteArray(64))
console.log("sub_8A4retval ", v12.readCString())
console.log("sub_8A4 retval ", hexdump(v12))
}
})
})
}
再来看看sub_8A4(s, (__int64)v30)
根据目前的线索:s是二进制字符,v30也知道。推到出s就破案了。
(1)进入一个无限循环,循环变量 i 从 0 开始递增。
(2)获取字符串 s 的长度,将结果存储在 result 中。
(3)如果 i 大于等于字符串长度除以 6 的结果,说明已经处理完字符串,跳出循环。
(4)计算 v7,指向字符串 s 中当前块的起始位置。
(5)计算 v8,将 v4 按照 2 字节对齐的方式,即将最低位清零。
(6)v4 每次递增 6,表示处理一个块的六个字符。
(7) 对于每个字符块,执行一系列位运算:将当前字符块的第一个字符与 *(_BYTE *)(a2 + i) 相加,并将结果存储在 *(_BYTE *)(a2+ i) 中。将第二个字符与 v8 相加,并将结果存储在 *(_BYTE *)(a2 + i) 中。
进行解密:key = th1s_1s_k3y!!!!!
总结 : key 加密流程就是:base64编码后,和自定义的编码表换位。如果熟悉base64编码的底层原理,应该是一眼就能看穿其中的道理。
java层解密明文分析
iArr = iArr2
def Rc4_init(S, K): # S盒初始化置换,K为密钥
j = 0
k = []
for i in range(256):
S.append(256 - i)
k.append(K[i % len(K)])
for i in range(256):
j = (j + S[i] + ord(k[i])) % 256
S[i], S[j] = S[j], S[i] # 交换S[i],S[j]
def rc4_Decrypt1(S, D):
i = j = 0
result = ''
for a in D:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) % 256
k = chr(a ^ S[(S[i] + S[j]) % 256])
result += k
return result
三
完整的算法还原
import base64
import binascii
s1 = "abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ"
s2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
# abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ
data = "3g6L2PWL2PXFmR+7ise7iq=="
data_y = "dGgxc18xc19rM3khISEhIQ=="
print(str.maketrans(s1, s2))
print(data.translate(str.maketrans(s1, s2)).encode('utf-8'))
key = str(base64.b64decode(data.translate(str.maketrans(s1, s2)).encode('utf-8')), encoding="utf-8")
print(" key = ",key)
def Rc4_init(S, K): # S盒初始化置换,K为密钥
j = 0
k = []
for i in range(256):
S.append(256 - i)
k.append(K[i % len(K)])
for i in range(256):
j = (j + S[i] + ord(k[i])) % 256
S[i], S[j] = S[j], S[i] # 交换S[i],S[j]
def rc4_Decrypt1(S, D):
i = j = 0
result = ''
for a in D:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t = (S[i] + S[j]) % 256
k = chr(a ^ S[(S[i] + S[j]) % 256])
result += k
return result
bb = [139, 210, 217, 93, 149, 255, 126, 95, 41, 86, 18, 185, 239, 236, 139, 208, 69]
print("key: " + key)
s = []
Rc4_init(s, key)
z = rc4_Decrypt1(s, bb)
print("Decrypt:" + z)
验证:
看雪ID:西贝巴巴
https://bbs.kanxue.com/user-home-961239.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):Nep CTF password:rc4和换表base64算法分析