第一次打CTF遇到APP题当然浅浅记录一下啦,别喷我谢谢各位大哥!
仅仅记录一下过程哈,不做分析了因为真没学过
ISCC校赛题 mobile1 这道题就是纯粹的把apk进行分析,找到加密逻辑,解密就可以了
1.把apk进行解压,找到dex文件进行分析
2.发现加密逻辑以及加密字符串
解密脚本,不是很完善,把重复的连接部分,以及结束符’/‘去掉
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 import base64def decode_last_part (encrypted: str ) -> str : reversed_str = encrypted[::-1 ] return '' .join(chr (ord (c) - 3 ) for c in reversed_str)def decode_front_part (encrypted: str ) -> str : decoded_bytes = base64.b64decode(encrypted) xored = bytes ([b ^ 0x2F for b in decoded_bytes]) return '' .join(chr (c) for c in xored if 32 <= c <= 126 ) last_encrypted = "n33EoohsV" front_encrypted = "fRxLWQ5BHHAA" last_part = decode_last_part(last_encrypted) front_part = decode_front_part(front_encrypted) original = f"{front_part} _{last_part} " print ("Front Part:" , front_part)print ("Last Part:" , last_part)print ("Original:" , original)
mobile2 把apk放入jadx分析逻辑
把MainActivity str字符替换脚本的
hook脚本
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 function hook ( ) { Java .perform (function ( ) { console .log ("Hooking started" ); let MainActivity = Java .use ("com.example.mobile02.MainActivity" ); const enc = "001020001001021220000012010210002201" ; MainActivity ["stringFromJNl" ].implementation = function (key, input ) { console .log (`MainActivity.stringFromJNl is called: key=${key} , input=${input} ` ); let result = this ["stringFromJNl" ](key, input); console .log (`MainActivity.stringFromJNl result=${result} ` ); let correctInput = input.split ('' ); for (let pos = 0 ; pos < input.length ; pos++) { console .log (`\n===== Testing position ${pos} =====` ); for (let charCode = 32 ; charCode <= 126 ; charCode++) { let char = String .fromCharCode (charCode); input = correctInput.join ("" ) let modifiedInput = input.substring (0 , pos) + char + input.substring (pos + 1 ); try { let testResult = this ["stringFromJNl" ](key, modifiedInput); if (testResult === enc) { console .log (modifiedInput) } const testIndex = 6 * pos; const encIndex = 6 * pos + 6 ; if (testIndex < testResult.length && encIndex < enc.length ) { if (testResult.substring (testIndex, encIndex) === enc.substring (testIndex, encIndex)) { console .log (`✅ Match at pos=${pos} , char='${char} ': testResult[${testIndex} ]='${testResult[testIndex]} ' == enc[${encIndex} ]='${enc[encIndex]} '` ); correctInput[pos] = char; if (pos == 5 ) { const finalInput = correctInput.join ('' ); console .log (`\n🎯 Final correct input: "${finalInput} "` ); } else { break ; } } } } catch (e) { console .log (`Error with char '${char} ' at pos ${pos} : ${e} ` ); } } } const finalInput = correctInput.join ('' ); console .log (`\n🎯 Final correct input: "${finalInput} "` ); return result; }; let Intrinsics = Java .use ("kotlin.jvm.internal.Intrinsics" ); Intrinsics ["areEqual" ].overload ('java.lang.Object' , 'java.lang.Object' ).implementation = function (obj, obj2 ) { console .log (`Intrinsics.areEqual is called: obj=${obj} , obj2=${obj2} ` ); let result = this ["areEqual" ](obj, obj2); console .log (`Intrinsics.areEqual result=${result} ` ); return result; }; }); }setImmediate (hook);
这里记录一下frida简单使用教程 使用frida进行hook
1 frida -U -f com.example .mobile02 -l exp .js
frida使用教程
1 2 3 4 5 6 7 8 9 adb push frida-server /data/local /tmp #frida服务端上线 adb install apk #adb把apk安装到模拟器 adb root shell ./frida-servers上线 可以使用frida-ps -U查看正在运行的app
回到mobile2
输入ISCC{XXXXXXXXXXXXXXXX}
ISCC{‘cU2Bc’bwJ8k*9L}
mobile3 第一部分flag
简单置换
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 import base64 CUSTOM_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789><" ENCRYPTED_FLAG = "svndq3TtocPLtta=" STANDARD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def translate_to_standard (s ): result = "" for char in s: if char in CUSTOM_ALPHABET: index = CUSTOM_ALPHABET.index(char) result += STANDARD_ALPHABET[index] return resultdef decrypt_flag (s ): standard_encoded = translate_to_standard(s) padding = len (standard_encoded) % 4 if padding: standard_encoded += '=' * (4 - padding) decoded_bytes = base64.b64decode(standard_encoded) return decoded_bytes.decode('utf-8' )def get_flag (): return decrypt_flag(ENCRYPTED_FLAG) flag = get_flag()print ("解密后的结果:" , flag)
第二部分flag snow隐写 Hook绕过+key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function main (){ Java.perform (function (){ var MainActivity = Java.use ("com.example.mobile03.MainActivity" ) var showAd = MainActivity.showAd showAd.implementation = function (){ } }) var MainActivity = Java.use ("com.example.mobile03.MainActivity" ) var Jformat = MainActivity.Jformat Jformat.implementation = function (str1, str2){ console.log (str1, str2) var result = this.Jformat (str1, str2) console.log (result) return result } } setImmediate (main)
1 frida -U -f com.example .mobile03 -l mobile3.js
输入ISCC{XXXXXXXXXXXX}
key
snow apk解压后在lib/下有个10kb大小的文件
1 snow.exe -C -p "524689" txt
得到
1 729 ff2e082 a5 c 4 b209 ae1e590 fb88097465 c 779 c 3 a17 a10 aebc7 dd26 d52 fc8 bfa5726 bb2 b0 b1 d80028 a8 aedff9 c 1 c 2874 bddaff4 ad9 ab7 ac881109e1 c 6e03177816525 d88472e0 cf452 f708 ea0 c 559 edcf70297e593 a162 b5201 c 0134e2 d3 c 04 f1723381243 f5587 f9831 df57 d266489 b2195 a7 db2 aa9881 feb475 aa6 b54 d973 da18 bbe558e6459 cf785721750 b4 da0 a43 ca4 ab2 c 010 d57 f6 adf5 d604839 b94 dbb68 b76 b94526 a22483e10 b8 f844979 cfb79 f2 ac98 c 07e9 e28 dfb04 c 77 d56877 bb4 c 10 b0 a7 d0 a7539 b8623 f97 cae8 ff2 b560 bdc8e1970 eae8002 f040 b070 b235 ffd61 f8 a53 c 9201 af8369 d0 d2 a43644 ef907 b79 b08 ccc 426 b4 c 0 f9 a576 c 4 d70 cd27 fead1194 bab79151 f4 afc7 f5 f5e07 f101 d31 d3 c 606943139 b874498 d090608 f11 aa96433 de65466 a73688 f5796 cf595 faba453 fbc14925 ebb77323 eb9e8 ae1031 a227 d1 c 9 f0401650826 fd52 a8 ce6 a277 f490 d997e00 ff89765e1 dd45201e06732 e60248 ac0 d122 fc4343 fce1 cfd160 a2 c 27 ecbc7794 b4 bfaafd17 b5e647755139 b0 fc15943327 b8 c 4 a5 bb1 c 6 b14e3 b485 f90 faf3 f8835 c 461 c 6 ab7 cb759 f5332190 b7 def406518688479
获取私钥
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 package com.ISCC;import com.alibaba.fastjson.util.IOUtils;import com.github.unidbg.AndroidEmulator;import com.github.unidbg.Module;import com.github.unidbg.arm.backend.Unicorn2Factory;import com.github.unidbg.debugger.Debugger;import com.github.unidbg.linux.android.AndroidEmulatorBuilder;import com.github.unidbg.linux.android.AndroidResolver;import com.github.unidbg.linux.android.dvm.*;import com.github.unidbg.memory.Memory;import java.io.File;import java.io.FileNotFoundException;public class chal extends AbstractJni { private final AndroidEmulator emulator; private final VM vm; private final Module module ; private final DvmClass swan, pig; private final boolean logging; chal(boolean logging) throws FileNotFoundException { this .logging = logging; emulator = AndroidEmulatorBuilder.for64Bit() .setProcessName("com.twogoat.114514" ) .addBackendFactory(new Unicorn2Factory (true )) .build(); final Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver (23 )); vm = emulator.createDalvikVM(new File ("\\mo3.apk" )); vm.setVerbose(logging); vm.setJni(this ); DalvikModule dm = vm.loadLibrary(new File ("G:\\text\\new\\unidbg-0.9.8\\unidbg-0.9.8\\unidbg-android\\src\\test\\java\\com\\ISCC\\libswan.so" ), false ); module = dm.getModule(); Debugger attach = emulator.attach(); attach.addBreakPoint(module .base + 0x0000000000021B04 ); dm.callJNI_OnLoad(emulator); swan = vm.resolveClass("com/example/mobile03/swan" ); pig = vm.resolveClass("pig" ); } public chal (AndroidEmulator emulator, VM vm, Module module , DvmClass mMainActivity, DvmClass swan, DvmClass pig, boolean logging) { this .emulator = emulator; this .vm = vm; this .module = module ; this .swan = swan; this .pig = pig; this .logging = logging; } void destroy () { IOUtils.close(emulator); if (logging) { System.out.println("destroy" ); } } public static void main (String[] args) throws Exception { chal test = new chal (true ); test.ttEncrypt(); test.destroy(); } boolean ttEncrypt () { String text = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" ; String res = String.valueOf(swan.callStaticJniMethodObject(emulator, "getPvKey()Ljava/lang/String;" , text)); System.out.println(res); return true ; } }

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 41 42 43 44 45 46 import binasciifrom Crypto.PublicKey import RSAfrom Crypto.Cipher import PKCS1_v1_5 PRIVATE_KEY_PEM = """-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBAIKa+PyzKgc3gZWuATRVoU4MC7yt5ArqgWm1Cf5L2qFnc3vya1yP hqS9JxQmZxHfB4DvfmR3lfRrbLrxEmrTRu8CAwEAAQJAUe6YTiazMe3PPC/5j3Q8 ifKilk3cJkDwyHiK+V1qnR+b6Srh7FTyT+yS7HZgOUlVdw9rtpEBCUya87stHNs6 cQIhAMP9EzynijUGydiu04vwz1RP8eLdY6gJwsa0CffI9QDpAiEAqpi2zS0kQv9WX LeSILo/hdu1vWnlFotSyVSa/txfYhcCIQCtyX5CXYnXBWL8ieG6CFnAOHeTpJ6Wxb j6O3FPT9m46QIhAKE2Z6lE+3uEqCw+HY1n9BefJQO2SpMfXkB7/2zQ/CJJAiBi00 BtWEIJak5AWXSBTQM2lRotqhV8XVfHDuT6xM688A== -----END RSA PRIVATE KEY-----""" TARGET_HEX = ( "06fd1efdf350f67f35825a92b4bf612ba33234a92dba2f47df7c80dfedc4a93a5" "5f4c8f3959d77cd866e6c0d7bfa2a5b63feb2a60fdc18df434f3fbeb0fa643c" )def main (): rsa_key = RSA.import_key(PRIVATE_KEY_PEM) cipher = PKCS1_v1_5.new(rsa_key) ciphertext = binascii.unhexlify(TARGET_HEX) try : plaintext = cipher.decrypt(ciphertext, None ) except ValueError: print ("解密失败:可能密文或私钥不匹配" ) return try : flag = plaintext.decode('utf-8' ) except UnicodeDecodeError: flag = plaintext print ("解密得到的 flag:" , flag)if __name__ == "__main__" : main()
拼接前半部分flag
ISCC区域赛 mobile1 1.获取db密钥
1 VlROQ2FHTnRkSE5hVVQwOQ = =
三次base64解密后 db密钥:
这里记录一下sqlcipher简单使用教程 使用sqlcipher-shell32.exe解密db
1 2 3 4 5 6 7 8 9 10 11 12 13 14 sqlite> PRAGMA cipher_version; 3.0 .1 sqlite> PRAGMA key = 'Sparkle '; sqlite> PRAGMA cipher_compatibility = 3 ; sqlite> PRAGMA integrity_check; ok sqlite> ATTACH DATABASE '66666 .db' AS plaintext KEY ''; sqlite> SELECT sqlcipher_export('plaintext ');
使用Navicat打开新db
获得密钥key
2.hook
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 function hook ( ) { Java.perform(function ( ) { console .log ("开始执行 hook..." ); try { let b = Java.use("com.example.mobile01.b" ); b["c" ].implementation = function ( ) { console .log ("[*] 方法 com.example.mobile01.b.c() 被调用,返回固定值" ); return "T0uVwXyZAbCdEfGh" ; }; console .log ("[+] 成功 hook com.example.mobile01.b.c() 方法" ); } catch (e) { console .error("[-] 无法 hook com.example.mobile01.b.c():" , e); } try { let DESHelper = Java.use("com.example.mobile01.DESHelper" ); DESHelper["encrypt" ].implementation = function (str, str2, str3 ) { console .log (`[*] DESHelper.encrypt 被调用: 明文=${str} , 密钥=${str2} , 向量=${str3} ` ); let result = this["encrypt" ](str, str2, str3); console .log (`[*] DESHelper.encrypt 加密结果=${result} ` ); return result; }; console .log ("[+] 成功 hook DESHelper.encrypt() 方法" ); } catch (e) { console .error("[-] 无法 hook DESHelper.encrypt():" , e); } }); } setImmediate(hook);
ISCC{kFV+vQCmogNraCLWryVlHqbByvGfZewL}
mobile2 使用 jadx分析apk文件
解密脚本:
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 def _hex_from_cipher (cipher: str ) -> str : """A.c 的逆操作:字符 ➜ 4 位十六进制,去掉前导 '00'""" hex_str = '' .join(f'{ord (ch):04x} ' for ch in cipher) return hex_str[2 :] if hex_str.startswith('00' ) else hex_strdef _undo_padding (hex_str: str ) -> str : """ 逆操作:去掉每 2 位后强插的 '00'(若剩余长度 %4 == 2 说明末尾少一个 '00'), 得到插针/交换前的串 replaced """ if len (hex_str) % 4 == 2 : hex_str += '00' return '' .join(hex_str[i:i + 2 ] for i in range (0 , len (hex_str), 4 ))def _undo_pair_shuffle (replaced: str ) -> str : """ 逆操作:解析 2-字节或 4-字节(…'21') 片段,复原 sb2+sb 的顺序 """ out, i = [], 0 while i < len (replaced): if i + 3 < len (replaced) and replaced[i + 2 :i + 4 ] == '21' : c2, c1 = replaced[i], replaced[i + 1 ] out.extend([c1, c2]) i += 4 else : out.extend(replaced[i:i + 2 ]) i += 2 return '' .join(out)def _split_and_fix_zero (concat: str ) -> str : """ 按 sb2 | sb 切分,再把被替换成 '3' 的零位改回 '0'。 sb 负责偶数索引(0,2,4…),替换条件 idx==0 或 idx%3==0 sb2 负责奇数索引(1,3,5…),替换条件 idx==1 或 (idx-1)%3==0 """ half = len (concat) // 2 sb2, sb = concat[:half], concat[half:] sb = '' .join('0' if ch == '3' and (idx == 0 or idx % 3 == 0 ) else ch for idx, ch in enumerate (sb)) sb2 = '' .join('0' if ch == '3' and (idx == 1 or (idx - 1 ) % 3 == 0 ) else ch for idx, ch in enumerate (sb2)) res = [] even = odd = 0 for idx in range (len (concat)): if idx % 2 == 0 : res.append(sb[even]); even += 1 else : res.append(sb2[odd]); odd += 1 return '' .join(res)def _hex_to_plain (hex_str: str ) -> str : """ 根据 A.b 的编码规则: - 如果以 '0' 开头则为 3 位组 (0xx) ➜ ASCII/单字节 - 否则为 4 位组 ➜ Unicode (xxxx) """ out, i = [], 0 while i < len (hex_str): if hex_str[i] == '0' and i + 2 < len (hex_str): out.append(chr (int (hex_str[i + 1 :i + 3 ], 16 ))) i += 3 else : out.append(chr (int (hex_str[i:i + 4 ], 16 ))) i += 4 return '' .join(out)def decrypt (cipher: str ) -> str : """顶层接口:密文 ➜ 明文""" step1 = _hex_from_cipher(cipher) step2 = _undo_padding(step1) step3 = _undo_pair_shuffle(step2) step4 = _split_and_fix_zero(step3) return _hex_to_plain(step4)if __name__ == "__main__" : xor_key = [0x53 , 0x68 , 0x65 , 0x72 , 0x6C , 0x6F , 0x63 , 0x6B ] encrypted_hex = "103E50404D59575F302D50415B" encrypted_bytes = [] for i in range (0 , len (encrypted_hex), 2 ): encrypted_bytes.append(int (encrypted_hex[i:i + 2 ], 16 )) decrypted_bytes = [] for i in range (len (encrypted_bytes)): decrypted_bytes.append(encrypted_bytes[i] ^ xor_key[i % len (xor_key)]) intermediate_cipher = bytes (decrypted_bytes).decode() final_plaintext = decrypt(intermediate_cipher) print (f"加密的十六进制: {encrypted_hex} " ) print (f"XOR解密后的密文: {intermediate_cipher} " ) print (f"最终解密的明文: {final_plaintext} " )
ISCC{DedU%3ct}
mobile3 1.hook截断获取密文
hook脚本
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 function hook ( ) { Java.perform(function ( ) { console .log ("开始执行 hook..." ); try { let a = Java.use("com.example.holygrail.a" ); if (!a) { console .error("无法找到 com.example.holygrail.a 类" ); return ; } console .log ("成功获取 com.example.holygrail.a 类" ); var targetClass = Java.use("com.example.holygrail.CipherDataHandler" ); if (!targetClass) { console .error("无法找到 com.example.holygrail.CipherDataHandler 类" ); return ; } console .log ("成功获取 com.example.holygrail.CipherDataHandler 类" ); var args = Java.array ("java.lang.String" , [ "checkBox8" , "checkBox6" , "checkBox7" , "checkBox5" , "checkBox12" , "checkBox3" , "checkBox10" , "checkBox13" , "checkBox11" , "checkBox" , "checkBox9" , "checkBox4" , "checkBox14" ]); console .log ("准备调用 generateCipherText 方法..." ); try { var result = targetClass.generateCipherText(args); console .log ("1. generateCipherText 结果:" , result); } catch (e) { console .error("调用 generateCipherText 时出错:" , e); } console .log ("准备 hook vigenereEncrypt 方法..." ); if (a["vigenereEncrypt" ]) { a["vigenereEncrypt" ].implementation = function (str, str2 ) { console .log (`a.vigenereEncrypt 被调用: str=${str} , str2=${str2} ` ); let originalMethod = a["vigenereEncrypt" ].overload("java.lang.String" , "java.lang.String" ); let result = originalMethod.call(this, str, str2); console .log (`a.vigenereEncrypt 返回结果=${result} ` ); return result; }; console .log ("vigenereEncrypt 方法 hook 成功" ); } else { console .error("未找到 vigenereEncrypt 方法" ); } console .log ("hook 执行完成" ); } catch (e) { console .error("执行 hook 时发生错误:" , e); } }); } setImmediate(hook);
密文:
2.密文解密
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 import itertoolsimport hashlibfrom tqdm import tqdm printable = r"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+,-./:;<=>?@[\]^_`{|}~" def parse_ciphertext (ciphertext ): """将密文解析为块列表""" blocks = [] while ciphertext: if len (ciphertext) >= 4 and ciphertext[2 :4 ] == "21" : blocks.append(ciphertext[:4 ].lower()) ciphertext = ciphertext[4 :] else : blocks.append(ciphertext[:2 ].lower()) ciphertext = ciphertext[2 :] return blocksdef vigenere_decrypt (ciphertext, key ): """使用Vigenere密码解密文本""" decrypted = [] key = key.lower() key_length = len (key) key_index = 0 for char in ciphertext: if char.isalpha(): offset = ord ('a' ) if char.islower() else ord ('A' ) k = ord (key[key_index % key_length]) - ord ('a' ) decrypted_char = chr ((ord (char) - offset - k) % 26 + offset) decrypted.append(decrypted_char) key_index += 1 else : decrypted.append(char) return '' .join(decrypted)def decrypt (hex_encoded_ciphertext ): """解密完整流程""" try : global data if not data: print ("错误: 数据映射表为空" ) return None cipher_bytes = bytes .fromhex(hex_encoded_ciphertext) cipher_hex_str = cipher_bytes.hex () enc_blocks = parse_ciphertext(cipher_hex_str) decrypted_chars = [] for block in enc_blocks: if block in data: decrypted_chars.append(printable[data.index(block)]) else : print (f"警告: 块 {block} 不在数据映射表中" ) decrypted_chars.append('?' ) intermediate_text = '' .join(decrypted_chars) key = "TheDaVinciCode" plaintext = vigenere_decrypt(intermediate_text, key) return plaintext except Exception as e: print (f"解密过程中发生错误: {e} " ) return None def initialize_data_mapping (original_ciphertext ): """初始化数据映射表""" blocks = [] while original_ciphertext: if original_ciphertext[2 :4 ] == "21" : blocks.append(original_ciphertext[:4 ].lower()) original_ciphertext = original_ciphertext[4 :] else : blocks.append(original_ciphertext[:2 ].lower()) original_ciphertext = original_ciphertext[2 :] return blocksif __name__ == "__main__" : original_ciphertext = "39213A213B213C21402141214221432144214521464748494A4B4C505152535455565758595A5B5C60616263646550215121522153215421552156215721582159215A215B215C21303132333435363738393A3B3C272129212A212B212C2130213121322133213421352136213721382146214721482149214A214B214C2140414243444566676869" data = initialize_data_mapping(original_ciphertext) cipher_to_decrypt = b"1Xc``YYVX7HG;!9!;!A!" .hex () plaintext = decrypt(cipher_to_decrypt) if plaintext: print ("解密结果:" , plaintext)
解密结果
ISCC{VitruvianMan2025}
ISCC决赛 mobile1 拖到jadx分析
在com.example.ggad b 得到二进制密文
1 0100001100110110010001000011000100110010001101100100001000110001001101100011010001000011001100110100001100110101
在com.example.ggad c 得到 Vigenère 密码的密文
解密脚本
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 b = '0100001100110110010001000011000100110010001101100100001000110001001101100011010001000011001100110100001100110101' c = '0761FY4R291928' def vigenere_decrypt (text, key ): decrypted_text = [] key_index = 0 for char in text: if char.isalpha(): decrypted_char = chr (((ord (char.upper()) - ord ('A' )) - (ord (key[key_index % len (key)].upper()) - ord ('A' )) + 26 ) % 26 + ord ('A' )) if char.islower(): decrypted_text.append(decrypted_char.lower()) else : decrypted_text.append(decrypted_char) key_index += 1 else : decrypted_text.append(char) return '' .join(decrypted_text)def decrypt (text, key ): return vigenere_decrypt(text, key)def a (cs ): key = 'ExpectoPatronum' return decrypt(cs, key) c2 = a(c)if len (b) % 8 != 0 : b = b.ljust(len (b) + (8 - len (b) % 8 ), '0' ) c1 = '' for i in range (0 , len (b), 8 ): c1 += chr (int (b[i:i + 8 ], 2 )) length = min (len (c1), len (c2)) c_result = '' for i in range (length): c_result += c1[i] c_result += c2[i]if len (c_result) % 2 != 0 : c_result = c_result[:-1 ] try : byte_array = [int (c_result[i:i+2 ], 16 ) for i in range (0 , len (c_result), 2 )]except ValueError: print ("Error: Invalid hexadecimal string" ) exit(1 ) c3 = '' for i in byte_array: a = bin (i)[2 :] c3 += ("{:0>8}" .format (a)) str1 = c3.replace('1' , 'x' ).replace('0' , '1' ).replace('x' , '0' ) c4 = []for i in range (0 , len (str1), 8 ): substr = str1[i:i + 8 ] if len (substr) < 8 : substr = substr.ljust(8 , '0' ) c4.append(int (substr, 2 )) final_cipher = bytes (c4)def rc4_ksa (key ): """密钥调度算法 (KSA) 得到初始置换后的S表 """ if isinstance (key, str ): key = key.encode() S = list (range (256 )) j = 0 for i in range (256 ): j = (j + S[i] + key[i % len (key)]) % 256 S[i], S[j] = S[j], S[i] return S def rc4_prga (S, text ): """伪随机生成算法 (PRGA) 利用S产生伪随机字节流, 将伪随机字节流与明文或密文进行异或,完成加密或解密操作 """ if isinstance (text, str ): text = text.encode() i = j = 0 result = [] for byte in text: i = (i + 1 ) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] t = (S[i] + S[j]) % 256 k = S[t] result.append(byte ^ k) return bytes (result) S = rc4_ksa('ExpectoPatronum' ) res = rc4_prga(S, final_cipher)try : flag = "ISCC{" + res.decode() + "}" print (flag)except UnicodeDecodeError: print ("Error: Failed to decode the result as UTF-8" ) print ("Raw result:" , res)
mobile2 1.把apk进行dump操作
1 frida-dexdump -U -n mobile04
把dump的文件夹拉到jadx
1 68 , 67 , 56 , 66 , 67 , 57 , 53 , 53 , 52 , 66 , 55 , 55 , 54 , 51 , 51 , 55
apk解压后 lib文件夹 x86.64拿到三个so
ida打开Sunday.so
在Java_com_example_mobile04_MainActivity_getEncryptedSegment
1 PQwj5DX0ZcRW0jAIBBN6dKlt
得到第一部分解
2.libMonday.so找到Java_com_example_mobile04_a_checkFlag2 找到异或
解密assets下X86_64
解密脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for i in range(len(data )): # 步骤1 : 循环左移2 位 (等效于原加密的右移2 位) data [i] = (data [i ] << 2) | (data [i ] >> 6) data [i] &= 0xff # 确保结果在0-255范围内 # 步骤2 : 异或操作的逆运算 (相同密钥再次异或即可还原) data [i] ^= 0x11 # 步骤3 : 循环右移3 位 (等效于原加密的左移3 位) data [i] = (data [i ] >> 3) | (data [i ] << 5) data [i] &= 0xff # 确保结果在0-255范围内with open("dereal" , "wb" ) as f: f.write(bytes(data ))
ida打开dereal 在check处找到密文和key
得到第二部分
3.flag={AZNxjY50ueBWLS}