echo "- System page size: $SYS_PAGESIZE bytes (0x$HEX_PAGESIZE)" # 检查libil2cpp.so是否存在 if ! grep -q "libil2cpp.so" /proc/$pid/maps; then echo "! libil2cpp.so not found in memory" exit fi
echo "═══════════════════════════════════════" echo " ELF ${EI_CLASS}-bit $E_TYPE (${EI_DATA})" echo " Address: 0x$offset - 0x$end" echo " Permissions: $perms" echo " File offset: 0x$fileOffset" else echo " Segment at 0x$offset [$perms]" fi # 确定输出文件名 if [[ $isELF == true ]]; then local fileOut="${out}/${offset}_${package}_libil2cpp.so" else local fileOut="${out}/${offset}_${package}_segment.bin" fi # Dump当前段 echo " Dumping: $range..." dd if="/proc/$pid/mem" bs=$SYS_PAGESIZE skip=$(echo "ibase=16;${offset}/$HEX_PAGESIZE" | bc) count=$(echo "ibase=16;(${end}-${offset})/$HEX_PAGESIZE" | bc) of="$fileOut" 2>/dev/null
if [[ $? -ne 0 ]]; then echo " Failed to dump, skipping..." rm -f "$fileOut" continue fi # 检查.bss段 local bss_memory=$(grep -i "^${end}-" "/proc/$pid/maps" | grep "\[anon:.bss\]" | head -1) if [[ $bss_memory != "" ]]; then local bss_range=$(echo $bss_memory | awk '{print $1}') local bss_end=$(echo $bss_range | awk -F'-' '{print toupper($2)}') local bss_blocks=$(echo "ibase=16;(${bss_end}-${end})/${HEX_PAGESIZE}" | bc)
echo " Adding .bss segment ($bss_blocks blocks)..." dd if="/proc/$pid/mem" bs=$SYS_PAGESIZE skip=$(echo "ibase=16;${end}/$HEX_PAGESIZE" | bc) count=$bss_blocks of="$out/tmp" 2>/dev/null cat "$out/tmp" >> "$fileOut" end=$bss_end fi # 合并逻辑 if [[ $isELF == true ]]; then # 这是一个新的ELF文件,不合并 lastFile=$fileOut lastOffset=$offset else # 非ELF段,尝试合并到上一个文件 if [[ $lastFile != "" ]]; then echo " Checking merge possibility..." local skipMerge=false
if [[ $lastEnd != $offset ]]; then # 存在间隙 local gap_blocks=$(echo "ibase=16;(${offset}-${lastEnd})/${HEX_PAGESIZE}" | bc)
if [[ $gap_blocks -gt $SYS_PAGESIZE ]]; then # 间隙太大,不合并 echo " Gap too large ($gap_blocks blocks), treating as separate file" skipMerge=true lastFile=$fileOut else # 间隙较小,填充并合并 echo " Filling gap ($gap_blocks blocks)..." dd if="/proc/$pid/mem" bs=$SYS_PAGESIZE skip=$(echo "ibase=16;${lastEnd}/$HEX_PAGESIZE" | bc) count=$gap_blocks of="$out/tmp" 2>/dev/null cat "$out/tmp" >> "$lastFile" fi fi
if [[ $skipMerge == false ]]; then # 合并当前段到上一个文件 echo " Merging into previous file..." cat "$fileOut" >> "$lastFile" rm -f "$fileOut" fi else # 第一个非ELF段,无法合并 echo " No previous ELF file to merge" lastFile=$fileOut fi fi
lastEnd=$end rm -f "${out}/tmp" echo "" done
echo "═══════════════════════════════════════" echo " Dump completed!" echo "" echo "Output files in: $out" ls -lh "$out"/*.so 2>/dev/null # 找出最大的.so文件(通常是完整的libil2cpp.so) largest=$(ls -S "$out"/*.so 2>/dev/null | head -1) if [[ $largest != "" ]]; then size=$(stat -c%s "$largest" 2>/dev/null) size_mb=$(echo "scale=2; $size/1024/1024" | bc) echo "" echo "Largest file (likely the complete libil2cpp.so):" echo "→ $(basename $largest) (${size_mb} MB)" # 验证ELF文件 dd if="$largest" bs=1 count=4 of="${out}/tmp" 2>/dev/null if [[ $(cat "${out}/tmp") == $(echo -ne "ELF") ]]; then echo "ELF header verified" else echo "Warning: ELF header not found in largest file" fi rm -f "${out}/tmp" fi
echo "" echo "Next steps:" echo "1. Check the largest .so file" echo "2. Use 'file' or 'readelf' to verify" echo "3. Extract global-metadata.dat from APK" echo "4. Use Il2CppDumper for analysis" echo "" echo "- Done!"
然后再IDA中按G,输入地址后跳跃到对应的位置,之后在对应位置右键查询 List cross references to…
看名字就知道这是个存档的地方,然后分析这个函数:
看看是谁调用的这个函数:
可以跳转到了LoadGameData,然后分析可以找到加密函数:
进去看看:
接着跟,这个代码就是最新版元气骑士的解密算法:
使用python简单复现一下:
1 2 3 4 5 6 7 8
defxor_crypt(data: bytes, password: str) -> bytes: result = bytearray(data) pwd_len = len(password) for i inrange(len(result)): pc = ord(password[i % pwd_len]) t = ((i % 15) * (i % 5)) % 92# 修正括号 result[i] = result[i] ^ pc ^ t returnbytes(result)
defxor_crypt(data: bytes, password: str) -> bytes: result = bytearray(data) pwd_len = len(password) for i inrange(len(result)): pc = ord(password[i % pwd_len]) t = ((i % 15) * (i % 5)) % 92# 修正括号 result[i] = result[i] ^ pc ^ t returnbytes(result)
defcrack_by_your_idea(ciphertext: bytes, max_pwd_len: int = 50): """破解XOR密码""" cipher_bytes = bytearray(ciphertext) # JSON 常见的重复模式 patterns = [b'{"', b'":', b',"', b'"}', b':[', b']}'] candidates = {} for pattern in patterns: pattern_bytes = list(pattern) # 对每个密码长度 for pwd_len inrange(1, max_pwd_len + 1): votes = [defaultdict(int) for _ inrange(pwd_len)] # 扫描密文的每个位置 for start_pos inrange(len(cipher_bytes) - len(pattern_bytes) + 1): temp_pwd = {} conflict = False for i, plain_byte inenumerate(pattern_bytes): pos = start_pos + i t = ((pos % 15) * (pos % 5)) % 92# 修正括号 recovered = cipher_bytes[pos] ^ t ^ plain_byte # 必须是可打印字符 ifnot (32 <= recovered < 127): conflict = True break pwd_idx = pos % pwd_len if pwd_idx in temp_pwd: if temp_pwd[pwd_idx] != recovered: conflict = True break else: temp_pwd[pwd_idx] = recovered ifnot conflict: for pwd_idx, byte_val in temp_pwd.items(): votes[pwd_idx][byte_val] += 1 # 构建密码 password = bytearray(pwd_len) min_votes = float('inf') for i inrange(pwd_len): ifnot votes[i]: password = None break most_common = max(votes[i].items(), key=lambda x: x[1]) password[i] = most_common[0] min_votes = min(min_votes, most_common[1]) if password and min_votes >= 2: try: pwd_str = password.decode('utf-8') if pwd_str notin candidates or min_votes > candidates[pwd_str][1]: candidates[pwd_str] = (pattern.decode('utf-8'), min_votes) except: pass returnsorted(candidates.items(), key=lambda x: -x[1][1])