未加固
版本:9.2.3
前置知識:
(v41 & 0xFFFFFFFFFFFFFFFELL)
是一種高效的奇偶檢查方法,用于判斷數值 v41
是否為奇數。
std::sort<std::lessstd::string,std::string &,std::string>(a1, v6, s); 排序算法
# 完全等價的字符串列表排序 strings = ["banana", "apple", "cherry"] strings.sort() ?# 原地排序 # 或 sorted_strings = sorted(strings) ?# 返回新列表
hash常見系列防護:
1.加鹽salt的值
2.魔改算法,修改算法的部分常量
3.也就是我們今天遇到的,利用自定義的校驗和計算與數據重排算法
我們可以先認識一下這段代碼:生成校驗值(0-10)然后對其進行重新排序
def reorder_before_sha256(input_hex):"""SHA256前的重排序算法(精確還原C++邏輯):param input_hex: 輸入hex字符串:return: 重排后的hex字符串"""# 1. 將hex轉換為可變字節數組data = bytearray.fromhex(input_hex)length = len(data)if length == 0:return ""# 2. 計算校驗值(模擬C++的v19計算)checksum = 0i = 0# 成對處理字節(偶數部分)paired_len = length & 0xFFFFFFFFFFFFFFFE # 取偶數長度while i < paired_len:checksum ^= data[i] ^ data[i + 1]i += 2# 處理可能的最后一個字節(奇數長度)if length % 2 != 0:checksum ^= data[-1]checksum %= 11 # 最終校驗值(0-10)# 3. 動態重排(核心邏輯)reordered = bytearray(length)for original_pos in range(length):# 計算新位置:(校驗值 + 原位置) % 總長度new_pos = (checksum + original_pos) % lengthreordered[new_pos] = data[original_pos]return reordered
一、抓包確定位置:
這里的signature,觸發在登錄之后每次點擊我的頁面會觸發:相關類是 weread.encrypt.EncryptUtils 的nativeGetSignatures方法
我們可以看到輸入一個strArr數組 返回的是64位 通過下表我們可以猜測可能是經過了sha256的加密。
找到關鍵so libencrypt.so 這個上面jadx關鍵類下就有就不多提了。
構建主動調用:
function call() {Java.perform(function () {// 1. 獲取 EncryptUtils 類let EncryptUtils = Java.use("weread.encrypt.EncryptUtils");let instance = EncryptUtils.$new();
// 2. 構造主動調用函數try {let inputArray = ["09cdcfd7f","1045","1145656655","12676517_1736697599_1779983999_0_1_0_935_34327344_23181744",]// let inputArray = [// "sgh256245",// "7895",// "1745675066",// "52676517_1736697599_1779983999_0_1_0_935_34327344_23181744",// ]// 3. 將 JavaScript 數組轉換為 Java String[]let javaStringArray = Java.array('java.lang.String', inputArray);// 4. 調用 nativeGetSignatures 方法let result = instance.nativeGetSignatures(javaStringArray);// 5. 打印結果console.log("主動調用 nativeGetSignatures 結果:", result);return result;} catch (e) {console.error("主動調用失敗:", e);}})
}
二、算法分析:
Java_weread_encrypt_EncryptUtils_nativeGetSignatures(JNIEnv *a1, __int64 a2, void *rucan_list)
此函數是我們加密生成Signature的函數:
可以看到這個位置 調用了生成GenSignature
這個n其實是我們的js_string類型的strArr數組:我們可以hook weread::GenSignature(n);
這里我們只看到了第一個的值 其他值應該是在指針里面 或者是一個結構體 這里我們只是合理猜測 但是沒具體去實踐。
但是我是直接hook的 weread::GenSignature 中的 std::string::append(&v66, v14, v15);函數
我們可以看到 他是先進行了排序在進行拼接成一個字符串。 我們可以看到我們這里就有5個參數進行了append
那么我們傳遞了四個參數為什么是5個進行了append
let inputArray = [
? ? "09cdcfd7f",
? ? "1045",
? ? "1145656655",
? ? "12676517_1736697599_1779983999_0_1_0_935_34327344_23181744",
]
而且拼接的也有規律 是按照了 sort排序 我們由下圖可以看出。
原來這里 的5a6f1其實是鹽的值呢
那下面我們可以解決一下為什么我們的
let inputArray = [
? ? "09cdcfd7f",
? ? "1045",
? ? "1145656655",
? ? "12676517_1736697599_1779983999_0_1_0_935_34327344_23181744",
] 以及鹽的值
明文變成了上述那些字符嘞?他的上一層函數中有weread::RemapString(n); 很是可疑。weread::RemapString
很可能是一個字符串重映射函數
關鍵byte_D9FC字符數組
byte_D9FC = [0x34, 0xCA, 0x55, 0x40, 0x1D, 0xB6, 0x93, 0xC6, 0x31,0x30, 0x29, 0x35, 0x32, 0xA7, 0xB8, 0x11, 0xC2, 0xB5,0x16, 0xFA, 0x8B, 0xB1, 0x24, 0xA4, 0x10, 0x90, 4,0xE9, 8, 0xF8, 0x3B, 0x8A, 0x9C, 0x8C, 0x44, 0xF9,0xBC, 0x5C, 0x69, 0xE2, 0xA1, 0xDA, 0xD2, 0xD3, 0x75,0x89, 0xF7, 0x1E, 0x2D, 0x50, 0x56, 0xD7, 0x72, 0x53,0xBF, 0x22, 0xFB, 0x20, 0xF, 1, 0x2E, 0x45, 0x87, 0x6E,0x66, 0x48, 0xF2, 0xE0, 0xCD, 0xFE, 0x67, 0xA9, 0x43,0xF4, 0x94, 0x51, 0xCE, 0xA5, 0x4A, 0xEE, 0x13, 0x26,0x8E, 0xCC, 0xAA, 0x33, 0x14, 0x5D, 0xE, 0x39, 0xBB,0xCF, 0x91, 0x2B, 0x81, 0x4D, 0xEA, 0x99, 0xEC, 0x1A,0x2C, 0x85, 0xC5, 0xD9, 0x36, 0x74, 0x4B, 0x18, 0xE1,0xF1, 0x3D, 0x9D, 0x41, 0x9F, 0xB4, 0x17, 0xD, 0xD6,0x4C, 0xBE, 0xDC, 0xAF, 0x97, 0x28, 0x77, 0xF0, 0x62,0xFF, 0x71, 0xC1, 0xC8, 0x27, 0x8F, 0x6C, 0x68, 0xA8,0x9B, 0xE6, 0x59, 0x1C, 0x1B, 0x12, 9, 0x98, 0x4E,0x3F, 6, 0x37, 0, 0xBA, 0x1F, 0xA, 0x19, 0x2F, 0xC9,0xD5, 0xD0, 0x57, 0x49, 0x6F, 0xFD, 0x25, 0xE4, 0x61,0xC, 0x42, 0xCB, 0x96, 0x64, 0x5F, 0xDB, 0xAD, 0x60,0x23, 0x8D, 0x9A, 0x6D, 0xC3, 0xC4, 0x5E, 0x3E, 0xB9,0x92, 0x6A, 0xBD, 0x5B, 7, 0x7F, 0x76, 0x95, 0xED,0x4F, 0xAB, 0x84, 0x7A, 0x80, 0xE7, 0x78, 0xC7, 0xE5,0xEB, 0x73, 0x83, 0x6B, 0xFC, 0x38, 0x46, 0x7D, 0x47,0x65, 0xB3, 0x52, 0x63, 0x3A, 5, 0xD1, 0xEF, 0xA3,0xA6, 0xDE, 0x9E, 0x3C, 2, 0xAE, 0xB2, 0x7B, 0xA0,0xF6, 0xF3, 0x2A, 0xC0, 0xAC, 0x86, 3, 0x5A, 0x54,0xB, 0xF5, 0x82, 0xD4, 0x7E, 0xE3, 0xDF, 0xB0, 0xD8,0xDD, 0x21, 0xE8, 0x7C, 0x88, 0xA2, 0x79, 0x58, 0x70,0xB7, 0x15]
這里不帶大家驗證了 其實就是我們的傳入的數組做了這個的映射
代碼:
def Tranfrom(input_str):byte_D9FC = [0x34, 0xCA, 0x55, 0x40, 0x1D, 0xB6, 0x93, 0xC6, 0x31,0x30, 0x29, 0x35, 0x32, 0xA7, 0xB8, 0x11, 0xC2, 0xB5,0x16, 0xFA, 0x8B, 0xB1, 0x24, 0xA4, 0x10, 0x90, 4,0xE9, 8, 0xF8, 0x3B, 0x8A, 0x9C, 0x8C, 0x44, 0xF9,0xBC, 0x5C, 0x69, 0xE2, 0xA1, 0xDA, 0xD2, 0xD3, 0x75,0x89, 0xF7, 0x1E, 0x2D, 0x50, 0x56, 0xD7, 0x72, 0x53,0xBF, 0x22, 0xFB, 0x20, 0xF, 1, 0x2E, 0x45, 0x87, 0x6E,0x66, 0x48, 0xF2, 0xE0, 0xCD, 0xFE, 0x67, 0xA9, 0x43,0xF4, 0x94, 0x51, 0xCE, 0xA5, 0x4A, 0xEE, 0x13, 0x26,0x8E, 0xCC, 0xAA, 0x33, 0x14, 0x5D, 0xE, 0x39, 0xBB,0xCF, 0x91, 0x2B, 0x81, 0x4D, 0xEA, 0x99, 0xEC, 0x1A,0x2C, 0x85, 0xC5, 0xD9, 0x36, 0x74, 0x4B, 0x18, 0xE1,0xF1, 0x3D, 0x9D, 0x41, 0x9F, 0xB4, 0x17, 0xD, 0xD6,0x4C, 0xBE, 0xDC, 0xAF, 0x97, 0x28, 0x77, 0xF0, 0x62,0xFF, 0x71, 0xC1, 0xC8, 0x27, 0x8F, 0x6C, 0x68, 0xA8,0x9B, 0xE6, 0x59, 0x1C, 0x1B, 0x12, 9, 0x98, 0x4E,0x3F, 6, 0x37, 0, 0xBA, 0x1F, 0xA, 0x19, 0x2F, 0xC9,0xD5, 0xD0, 0x57, 0x49, 0x6F, 0xFD, 0x25, 0xE4, 0x61,0xC, 0x42, 0xCB, 0x96, 0x64, 0x5F, 0xDB, 0xAD, 0x60,0x23, 0x8D, 0x9A, 0x6D, 0xC3, 0xC4, 0x5E, 0x3E, 0xB9,0x92, 0x6A, 0xBD, 0x5B, 7, 0x7F, 0x76, 0x95, 0xED,0x4F, 0xAB, 0x84, 0x7A, 0x80, 0xE7, 0x78, 0xC7, 0xE5,0xEB, 0x73, 0x83, 0x6B, 0xFC, 0x38, 0x46, 0x7D, 0x47,0x65, 0xB3, 0x52, 0x63, 0x3A, 5, 0xD1, 0xEF, 0xA3,0xA6, 0xDE, 0x9E, 0x3C, 2, 0xAE, 0xB2, 0x7B, 0xA0,0xF6, 0xF3, 0x2A, 0xC0, 0xAC, 0x86, 3, 0x5A, 0x54,0xB, 0xF5, 0x82, 0xD4, 0x7E, 0xE3, 0xDF, 0xB0, 0xD8,0xDD, 0x21, 0xE8, 0x7C, 0x88, 0xA2, 0x79, 0x58, 0x70,0xB7, 0x15]encoded_bytes = []for char in input_str:# 獲取字符的ASCII碼作為索引index = ord(char)# 確保索引在查找表范圍內if index < 0 or index >= len(byte_D9FC):raise ValueError(f"字符 '{char}' (ASCII: {index}) 超出查找表范圍")# 使用查找表進行編碼encoded_byte = byte_D9FC[index]encoded_bytes.append(encoded_byte)# 將編碼后的字節轉換為十六進制字符串hex_str = ''.join([f"{b:02x}" for b in encoded_bytes])return hex_str
三、兩次sha256算法
ida中的代碼
v18 = 0;if ( (mingwen_str & 1) != 0 )v19 = v17;elsev19 = mingwen_str >> 1;if ( (mingwen_str & 1) != 0 )v20 = v16;elsev20 = &mingwen_strv66 + 1;if ( v19 ){v21 = v20;if ( v19 == 1 )goto LABEL_34;v22 = 0;v23 = 0;v21 = &v20[v19 & 0xFFFFFFFFFFFFFFFELL];v24 = v20 + 1;v25 = v19 & 0xFFFFFFFFFFFFFFFELL;do{v26 = *(v24 - 1);v27 = *v24;v24 += 2;v25 -= 2LL;v22 ^= v26;v23 ^= v27;}while ( v25 );v18 = v23 ^ v22;if ( v19 != (v19 & 0xFFFFFFFFFFFFFFFELL) ){
LABEL_34:v28 = &v20[v19];do{v29 = *v21++;v18 ^= v29;}while ( v28 != v21 );}v18 %= 11;}v63[1] = 0LL;v64 = 0LL;v63[0] = 0LL;if ( v19 << 32 ){std::string::append(v63, v19, 0);if ( v19 <= 0 )goto LABEL_47;}else{*(v63 + v19 + 1) = 0;LOBYTE(v63[0]) = 2 * v19;if ( v19 <= 0 )goto LABEL_47;}v30 = 0LL;do{v31 = v67;v32 = (v18 + v30) % v19;v33 = v64;if ( (mingwen_strv66 & 1) == 0 )v31 = &mingwen_strv66 + 1;v34 = v31[v30++];if ( (v63[0] & 1) == 0 )v33 = v63 + 1;v33[v32] = v34;}while ( v19 != v30 );
LABEL_47:
============================================
SHA256();v36 = 0LL;v37 = s;v74 = 0;v72 = 0u;v73 = 0u;v71 = 0u;*s = 0u;do{sub_3684(v37, -1LL, v35, v75[v36++]);v37 += 2;}while ( v36 != 32 );std::string::basic_string<decltype(nullptr)>(&sha256_res, s);v38 = v62;v39 = 0;if ( (sha256_res & 1) != 0 )v40 = *&v61[7];elsev40 = sha256_res >> 1;if ( (sha256_res & 1) == 0 )v38 = v61;if ( v40 ){v41 = v38;if ( v40 == 1 )goto LABEL_59;v42 = 0;v43 = 0;v41 = &v38[v40 & 0xFFFFFFFFFFFFFFFELL];v44 = v38 + 1;v45 = v40 & 0xFFFFFFFFFFFFFFFELL;do{v46 = *(v44 - 1);v47 = *v44;v44 += 2;v45 -= 2LL;v42 ^= v46;v43 ^= v47;}while ( v45 );v39 = v43 ^ v42;if ( v40 != (v40 & 0xFFFFFFFFFFFFFFFELL) ){
LABEL_59:v48 = &v38[v40];do{v49 = *v41++;v39 ^= v49;}while ( v48 != v41 );}v39 %= 11;}v58[1] = 0LL;v59 = 0LL;v58[0] = 0LL;if ( v40 << 32 ){std::string::append(v58, v40, 0);if ( v40 <= 0 )goto LABEL_72;}else{*(v58 + v40 + 1) = 0;LOBYTE(v58[0]) = 2 * v40;if ( v40 <= 0 )goto LABEL_72;}v50 = 0LL;do{v51 = v62;v52 = (v39 + v50) % v40;v53 = v59;if ( (sha256_res & 1) == 0 )v51 = v61;v54 = v51[v50++];if ( (v58[0] & 1) == 0 )v53 = v58 + 1;v53[v52] = v54;}while ( v40 != v50 );
LABEL_72:
============================================SHA256();
我們可以看到有兩次sha256算法我們hook一下sha256函數看下入參:
進入Sha256================
"rr- ,�,"�5a6f1P-rSPPrS�S��SSPV�"�SP"MP"?� "S ?MP"" ?�� ? M-MPM-M �SM�r�V"�rrMV�P�P
? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
79b4efde50 ?22 72 72 2d 20 1a 2c 1a c5 2c 22 c5 35 61 36 66 ?"rr- .,..,".5a6f
79b4efde60 ?31 50 2d 72 53 50 50 72 53 bf 53 bf bf 53 53 50 ?1P-rSPPrS.S..SSP
79b4efde70 ?56 bf 22 bf 53 50 22 4d 50 22 d7 bf bf 20 22 53 ?V.".SP"MP"... "S
79b4efde80 ?20 20 4d 50 22 22 20 20 fb d7 20 20 20 4d 2d 4d ? ?MP"" ?.. ? M-M
79b4efde90 ?50 4d 2d 4d 20 d7 53 4d d7 72 d7 56 22 d7 72 72 ?PM-M .SM.r.V".rr
79b4efdea0 ?4d 56 d7 50 fb 50 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?MV.P.P
長度 86
Sha256 result= ? ? ? ? ? ? 0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
778d94dac8 ?ac 03 64 81 c4 68 6f 92 94 7a ed 19 bd 46 7d f5 ?..d..ho..z...F}.
778d94dad8 ?6c 82 1b 96 10 cd b8 f4 ef f7 da 61 84 fc 43 e8 ?l..........a..C.
進入Sha256================
3e8ac036481c4686f92947aed19bd467df56c821b9610cdb8f4eff7da6184fc4
? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
79a4edb990 ?33 65 38 61 63 30 33 36 34 38 31 63 34 36 38 36 ?3e8ac036481c4686
79a4edb9a0 ?66 39 32 39 34 37 61 65 64 31 39 62 64 34 36 37 ?f92947aed19bd467
79a4edb9b0 ?64 66 35 36 63 38 32 31 62 39 36 31 30 63 64 62 ?df56c821b9610cdb
79a4edb9c0 ?38 66 34 65 66 66 37 64 61 36 31 38 34 66 63 34 ?8f4eff7da6184fc4
長度 64
Sha256 result= ? ? ? ? ? ? 0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
778d94dac8 ?36 c0 8d b7 02 cb 2f 47 7e f9 92 31 f2 97 5d 1b ?6...../G~..1..].
778d94dad8 ?97 d7 a7 87 75 81 93 0a c4 67 34 28 3b 97 d1 45 ?....u....g4(;..E
主動調用 nativeGetSignatures 結果: 36c08db702cb2f477ef99231f2975d1b97d7a7877581930ac46734283b97d145
?
對比append函數:
進入append================
? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
7a2505af71 ?2d 20 1a 2c 1a c5 2c 22 c5 ? ? ? ? ? ? ? ? ? ? ? - .,..,".
進入append================? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
7a2505af89 ?35 61 36 66 31 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 5a6f1
進入append================? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
7a2505afa1 ?50 2d 72 53 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?P-rS
進入append================? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
7a2505afb9 ?50 50 72 53 bf 53 bf bf 53 53 ? ? ? ? ? ? ? ? ? ?PPrS.S..SS
進入append================? ? ? ? ? ? ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F ?0123456789ABCDEF
7994eb1450 ?50 56 bf 22 bf 53 50 22 4d 50 22 d7 bf bf 20 22 ?PV.".SP"MP"... "
7994eb1460 ?53 20 20 4d 50 22 22 20 20 fb d7 20 20 20 4d 2d ?S ?MP"" ?.. ? M-
7994eb1470 ?4d 50 4d 2d 4d 20 d7 53 4d d7 72 d7 56 22 d7 72 ?MPM-M .SM.r.V".r
7994eb1480 ?72 4d 56 d7 50 fb 50 22 72 72 ? ? ? ? ? ? ? ? ? ?rMV.P.P"rr
?
我們發現兩次sha256之前均進行了重排序。
兩次重排序:
-
計算校驗和:通過異或運算生成一個0-10的校驗值
-
數據重排序:根據校驗值對輸入字節進行循環位移
def process_sha256_result(sha256_res):# sha256_res = bytearray.fromhex(input_hex)# 1. 初始化參數v40 = 0length = len(sha256_res)# 2. 計算校驗和(異或)if length >= 2:v43 = v44 = 0for i in range(0, length - 1, 2):v43 ^= sha256_res[i]v44 ^= sha256_res[i + 1]v40 = (v43 ^ v44) % 11# 3. 數據重排result = bytearray(length)for i in range(length):new_pos = (v40 + i) % lengthresult[new_pos] = sha256_res[i]return bytes(result)
這些代碼 根據ida 代碼翻譯得來:
例如翻譯這個:
do{v46 = *(v44 - 1);v47 = *v44;v44 += 2;v45 -= 2LL;v42 ^= v46;v43 ^= v47;}while ( v45 );v39 = v43 ^ v42;if ( v40 != (v40 & 0xFFFFFFFFFFFFFFFELL) ){
LABEL_59:v48 = &v38[v40];do{v49 = *v41++;v39 ^= v49;}while ( v48 != v41 );}v39 %= 11;}v58[1] = 0LL;v59 = 0LL;v58[0] = 0LL;if ( v40 << 32 ){std::string::append(v58, v40, 0);if ( v40 <= 0 )goto LABEL_72;}else{*(v58 + v40 + 1) = 0;LOBYTE(v58[0]) = 2 * v40;if ( v40 <= 0 )goto LABEL_72;}v50 = 0LL;do{v51 = v62;v52 = (v39 + v50) % v40;v53 = v59;if ( (sha256_res & 1) == 0 )v51 = v61;v54 = v51[v50++];if ( (v58[0] & 1) == 0 )v53 = v58 + 1;v53[v52] = v54;}while ( v40 != v50 );
所以我們在兩次sha256之前 進行重排序即可。