仅给出我个人解出的题目的WriteUp.
Crypto方向: lcgp: n-1光滑,先恢复c:
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 from math import gcdfrom Crypto.Util.number import *random_outputs = [ 11250327355112956284720719987943941825496074893551827972877616718074592862130806975889275745497426515405562887727117008818863728803549848574821067056997423443681347885027000632462241968640893471352200125748453396098854283137158609264944692129301617338233670002547470932851350750870478630955328653729176440142198779254117385657086615711880537380965161180532127926250520546846863536247569437 , 1289730679860726245234376434590068355673648326448223956572444944595048952808106413165882424967688302988257332835229651422892728384363094065438370663362237241013242843898967355558977974152917458085812489310623200114007728021151551927660975648884448177346441902806386690751359848832912607313329587047853601875294089502467524598036474193845319703759478494109845743765770254308199331552085163360820459311523382612948322756700518669154345145757700392164795583041949318636 , 147853940073845086740348793965278392144198492906678575722238097853659884813579087132349845941828785238545905768867483183634111847434793587821166882679621234634787376562998606494582491550592596838027522285263597247798608351871499848571767008878373891341861704004755752362146031951465205665840079918938797056361771851047994530311215961536936283541887169156535180878864233663699607369701462321037824218572445283037132205269900255514050653933970174340553425147148993214797622395988788709572605943994223528210919230924346860415844639247799805670459 , 7426988179463569301750073197586782838200202717435911385357661153208197570200804485303362695962843396307030986052311117232622043073376409347836815567322367321085387874196758434280075897513536063432730099103786733447352512984165432175254784494400699821500026196293994318206774720213317148132311223050562359314735977091536842516316149049281012797103790472349557847649282356393682360276814293256129426440381745354969522053841093229320186679875177247919985804406150542514337515002645320320069788390314900121917747534146857716743377658436154645197488134340819076585888700553005062311578963869641978771532330577371974731136 , 10389979373355413148376869524987139791217158307590828693700943753512488757973725227850725013905113587408391654379552713436220790487026223039058296951420273907725324214990441639760825661323514381671141482079783647253661594138658677104054180912818864005556386671430082941396497098166887200556959866845325602873713813206312644590812141400536476615405444030140762980665885244721798105034497461675317071497925846844396796854201566038890503298824928152263774446268093725702310124363765630370263370678902342200494544961012407826314577564991676315451785987248633724138137813024481818431889574317602521878974976264742037227074 ] n = 604805773885048132038788501528078428693141138274580426531445179173412328238102786863592612653315029009606622583856638282837864213048342883583286440071990592001905867027978355755042060684149344414810835371740304319571184567860694439564098306766474576403800046937218588251809179787769286393579687694925268985445059 s1, s2, s3, s4, s5 = random_outputs d1 = s2 - s1 d2 = s3 - s2 d3 = s4 - s3 d4 = s5 - s4 x = d2**2 - d1 * d3 y = d3**2 - d2 * d4 m = gcd(x, y) inv_d1 = inverse(d1, m) a = (d2 * inv_d1) % m b = (s2 - a * s1) % m inv_a = inverse(a, m) c = (s1 - b) * inv_a % m print ("Recovered c:" , c)
得到
1 c=98136663393066487319477131255488756533037186459124433869847045986870213783395243380337142782779765255670853582334927187474123853371504168896312528278296763527266828907487342102002206806408616944398694810398049626860321901229014612541564249969665358849039818103044159048535403863928440335143886672949700153798350
接下来求解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from Crypto.Util.number import long_to_bytesn = 604805773885048132038788501528078428693141138274580426531445179173412328238102786863592612653315029009606622583856638282837864213048342883583286440071990592001905867027978355755042060684149344414810835371740304319571184567860694439564098306766474576403800046937218588251809179787769286393579687694925268985445059 e = 2024 c = 98136663393066487319477131255488756533037186459124433869847045986870213783395243380337142782779765255670853582334927187474123853371504168896312528278296763527266828907487342102002206806408616944398694810398049626860321901229014612541564249969665358849039818103044159048535403863928440335143886672949700153798350 R = Zmod(n) c_val = R(c) e_val = R(e) n_minus_1 = n - 1 factors = factor(n_minus_1) print ("Factorization of n-1:" , factors)d = discrete_log(c_val, e_val) print ("Discrete logarithm d:" , d)flag = long_to_bytes(d) print ("Flag:" , flag)
H&NCTF{7ecf4c8c-e6a5-45c7-b7de-2fecc31d8511}
哈基coke: 逆向猫脸变换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import cv2import numpy as npdef arnold_decode (encrypted_image_path, shuffle_times, a, b ): encrypted_image = cv2.imread(encrypted_image_path) h, w = encrypted_image.shape[:2 ] N = h decoded_image = np.zeros_like(encrypted_image) for _ in range (shuffle_times): temp_image = np.copy(decoded_image) for x_prime in range (N): for y_prime in range (N): x = (( (a*b + 1 ) * x_prime - b * y_prime ) % N + N) % N y = (( -a * x_prime + y_prime ) % N + N) % N temp_image[x, y, :] = encrypted_image[x_prime, y_prime, :] decoded_image = temp_image encrypted_image = np.copy(decoded_image) return decoded_image if __name__ == "__main__" : decrypted_image = arnold_decode('en_flag.png' , 6 , 9 , 1 ) cv2.imwrite('decrypted_coke.png' , decrypted_image, [int (cv2.IMWRITE_PNG_COMPRESSION), 0 ])
H&NCTF{haji_coke_you_win}
数据处理: 先求解intflag:
1 2 3 4 5 6 7 8 9 10 11 12 n = 0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 m = 0x61125b33959f20cbab41c14ff5b1825eb553e7d076098a003acd729163343437d6cbaa0271a61e69c1f3d054dfc85d4e47e568598ffe6a80642fff9d4ad8ff67 c = 0x39141869cbba69234a5619bb0766439c2e9f5b15a9009715ed3ce38a190466c6c0fbb274274ec3ae7d25a447938db11dd340038ad553bfd470496463974dcec7 R = Integers(n) m = R(m) c = R(c) from sage.groups.generic import discrete_logflag = discrete_log(c, m) print ("flag:" , flag)
得到flag值为
1 3282248010524512146638712359816289396373430161050484501341123570760619381019795910712610762203934445754701
接下来爆破映射表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import itertoolsfrom Crypto.Util.number import *lowercase = '0123456789' intflag = '3282248010524512146638712359816289396373430161050484501341123570760619381019795910712610762203934445754701' pattern = "7***4****5" used_digits = {4 , 5 , 7 } available_digits = [d for d in range (10 ) if d not in used_digits] for perm in itertools.permutations(available_digits): result = [] idx = 0 for ch in pattern: if ch == '*' : result.append(str (perm[idx])) idx += 1 else : result.append(ch) uppercase = "" .join(result) table = '' .maketrans(uppercase, lowercase) flag = intflag.translate(table) bflag = long_to_bytes(int (flag)) if bflag.startswith(b"H&NCTF{" ): print (bflag)
H&NCTF{cut_cut_rrioajtfijrwegeriogjiireigji}
Reverse: F**K: 查看主函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int __fastcall main (int argc, const char **argv, const char **envp) { char __dst[65 ]; char __s_[65 ]; char __b_[100 ]; char __b[100 ]; memset (__b, 0 , sizeof (__b)); memset (__b_, 0 , sizeof (__b_)); strcpy (__dst, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ); func1 ((__int64)__dst, __s_); if ( fgets (__b, 0x64 , __stdinp) ) { __b[strcspn (__b, "\n" )] = 0 ; func2 (__b, (__int64)__b_, (__int64)__s_); } while ( !byte_100008292 ) func3 ((__int64)__b_); if ( !memcmp (num1, num2, 16 * num3) ) printf ("\nCongratulations!\n" ); else printf ("\nSomething Wrong.\n" ); return 0 ; }
其中func1是按时间生成随机数种子打乱base64表,func2是标准base64编码,func3是对明文的每4字节进行md5哈希后再进行一步简单的加密:
1 2 for ( n16 = 0 ; n16 < 16 ; ++n16 )num2[16 * num3 + n16] = (7 * (num2[16 * num3 + n16] ^ (n16 + 6 )) + 0x1234 * (n16 % 0xF )) % 256 ;
提取密文,先进行哈希爆破:
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 import hashlibfrom itertools import productfrom multiprocessing import Pool, cpu_countfrom tqdm import tqdmcustom_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def generate_all_b64_blocks (table ): return ('' .join(p) for p in product(table, repeat=4 )) def transform (md5_hash ): result = [] for n16 in range (16 ): val = (7 * ((md5_hash[n16] ^ (n16 + 6 )) & 0xFF ) + 0x1234 * (n16 % 0xF )) % 256 result.append(val) return bytes (result) def process_block (block ): encoded_block = block.encode() md5_hash = hashlib.md5(encoded_block).digest() transformed = transform(md5_hash) return (transformed, block) def build_rainbow_table (custom_table ): blocks = list (generate_all_b64_blocks(custom_table)) total = len (blocks) print (f"[*] Generating rainbow table with {total} entries..." ) pool = Pool(cpu_count()) rainbow_dict = {} with tqdm(total=total, desc="Building Rainbow Table" , unit="block" ) as pbar: for result in pool.imap_unordered(process_block, blocks, chunksize=10000 ): key, value = result rainbow_dict[key] = value pbar.update() pool.close() pool.join() print ("Rainbow table built." ) return rainbow_dict def decrypt_num1 (num1_data, rainbow_table ): b64_string = "" block_count = len (num1_data) // 16 for i in range (block_count): start = i * 16 end = start + 16 block = num1_data[start:end] if block in rainbow_table: b64_string += rainbow_table[block] else : print (f"[!] Block {i} not found in rainbow table." ) b64_string += "????" return b64_string if __name__ == "__main__" : rainbow_table = build_rainbow_table(custom_table) num1 = bytes .fromhex("8E681BB44AFA6C03C884467B469BE7BFE7F132B5DF3916FE3B8D902088D6BC040D5001699DE9EBEEEA63FE189D75014C59B1FF9363D8CE60FD211E4A5025F5F8968C3ABFD11318BD93C11088EAD50A7FD54A12DE52F0B1158938B76CB3374F8B795DA8FAD7ED6F1FF5F1C01B54BCF774DB4556CDC4E2A693FB097EF2235C915F9300E5F9278AADC17E18C6224BD7A6CA2F100A32105E59BEAE243E082A4DD1F56AFC5D84EAEB1B278352A0BB9DF40AA95530F1701653771B2C99179A70E24090C3B1EA924B3515145A30BF56306CF0304D5B097C74989E8872666C5C38A5760BA8EE7BF1B3AD582DBFA7331183467F1D1C9D861AC6D6B299CCC782ABED3A6B12A6F8BF1C3BEBDAE0610815018B2443CA154B1D9188BC5F9261370AA2D3309651AAA5D9505B823AAA1F77FF9CA64F2328E780088BE5CE1DFC0B6866BC5FFA44C25F0F0C861462D2F4A2E8CC9B274828AE5B76E8BCE03D8B844C29C8927FDC1EA680FF783FE1394BD0CCE013F7367CDD5C9602DCF9E9DD1887E9AC4326B3DF68C2FE3010B066DD046AFFD2FAE186EFA104BCADD1CE58A85D9D37517FCFF102BB4BB4161BBFE37105D41B5832E8592CCEE3D40B9E02F4A1343EF28FA39F7F24346E29246BFE8AB3E26C37E4EA7944F9A5285DCEAE7923862F9A00456E9F8711D10FE674BD7BE5812681EDD22A23E76F7D40D608EA781278B84F85E96C8B6DE78C14218A923FBA2D7968BEA20992D5B3FE7C4E5217E2AAEAECBD021D1C80AAF333BB65CBE51D96448F4E11696DA570984DBCD57CBF2C56131FF436B464FF520544023E15A0096912D151FF8A5A5BD0694B07678AE78F16A6401560E3DC628411085141B47ABA5A473429E776B1DAE3A829B679DAA5E2244113224667CB8DF684EC0EA8DE0E01AABFBC3AD00F8E658452BF43" ) print (len (num1)) b64_string = decrypt_num1(num1, rainbow_table) print ("Recovered Base64 String:" , b64_string)
得到base64:
1 YIfUIfUWfUW7UW7XW7XM7XMkXMk7Mk7rk7rr7rrTrrTcrTcmTcmncmnOmnOYnOYLOYLPYLPdLPd6Pd63d63v63vg3vg5vg5Sg5S55S5SS5S75S7BS7Bo7BojBojdojdOjdO/dO/lO/lr/lr/lr/1r/1d/1dt1dt6????
但是这个base64很明显是一位一位增加的(也就是源程序在进行哈希时一次只右移了一个字节),提取得到真正的base64密文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 message = bytearray (b"YIfUIfUWfUW7UW7XW7XM7XMkXMk7Mk7rk7rr7rrTrrTcrTcmTcmncmnOmnOYnOYLOYLPYLPdLPd6Pd63d63v63vg3vg5vg5Sg5S55S5SS5S75S7BS7Bo7BojBojdojdOjdO/dO/lO/lr/lr/lr/1r/1d/1dt1dt6????" ) output = bytearray (b"" ) for i in range (0 , len (message), 4 ): if i == 0 : output += message[i:i+4 ] else : output.append(message[i+3 ]) output[-1 ] = b"=" [0 ] print (output)gen = bytearray () for i in range (len (output)-3 ): gen += output[i:i+4 ] print (message)print (gen)
得到密文为YIfUW7XMk7rrTcmnOYLPd63vg5S5S7BojdO/lr/1dt6= ,接下来编写脚本爆破解密:
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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 #include <iostream> #include <string> #include <vector> #include <thread> #include <mutex> #include <atomic> #include <cmath> #include <iomanip> #include <sstream> using namespace std;const string target_b64 = "YIfUW7XMk7rrTcmnOYLPd63vg5S5S7BojdO/lr/1dt6=" ;const string known_prefix = "H&NCTF" ;const size_t num_threads = thread::hardware_concurrency ();const uint64_t start_seed = 1700000000 ;const uint64_t end_seed = 1800000000 ;mutex result_mutex; atomic<bool > found (false ) ;uint64_t seed;void mac_srand (uint64_t s) { seed = s; } int mac_rand () { if (seed == 0 ) seed = 123459876 ; uint64_t hi = seed / 127773 ; uint64_t lo = seed % 127773 ; int64_t x = 16807 * lo - 2836 * hi; if (x < 0 ) x += 0x7fffffff ; return ((seed = x) % ((long )RAND_MAX + 1 )); } string custom_base64_encode (const string& data, const string& table) { string encoded; int i = 0 ; int j = 0 ; unsigned char char_array_3[3 ]; unsigned char char_array_4[4 ]; for (auto c : data) { char_array_3[i++] = c; if (i == 3 ) { char_array_4[0 ] = (char_array_3[0 ] & 0xfc ) >> 2 ; char_array_4[1 ] = ((char_array_3[0 ] & 0x03 ) << 4 ) + ((char_array_3[1 ] & 0xf0 ) >> 4 ); char_array_4[2 ] = ((char_array_3[1 ] & 0x0f ) << 2 ) + ((char_array_3[2 ] & 0xc0 ) >> 6 ); char_array_4[3 ] = char_array_3[2 ] & 0x3f ; for (i = 0 ; (i <4 ) ; i++) encoded += table[char_array_4[i]]; i = 0 ; } } if (i) { for (j = i; j < 3 ; j++) char_array_3[j] = '\0' ; char_array_4[0 ] = (char_array_3[0 ] & 0xfc ) >> 2 ; char_array_4[1 ] = ((char_array_3[0 ] & 0x03 ) << 4 ) + ((char_array_3[1 ] & 0xf0 ) >> 4 ); char_array_4[2 ] = ((char_array_3[1 ] & 0x0f ) << 2 ) + ((char_array_3[2 ] & 0xc0 ) >> 6 ); for (j = 0 ; (j < i + 1 ); j++) encoded += table[char_array_4[j]]; while ((i++ < 3 )) encoded += '=' ; } return encoded; } string generate_custom_table (uint64_t seed_candidate) { string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/" ; mac_srand (seed_candidate); int v7 = mac_rand () % 64 ; int len = base64_chars.size (); for (int i = 0 ; i < 59 ; ++i) { int v5 = i + v7; if (v5 >= len) continue ; swap (base64_chars[i], base64_chars[v5]); } return base64_chars; } bool check_seed (uint64_t seed_candidate) { string custom_table = generate_custom_table (seed_candidate); string encoded = custom_base64_encode (known_prefix, custom_table); if (target_b64.f ind(encoded) == 0 ) { lock_guard<mutex> lock (result_mutex) ; cout << "[+] Found matching seed: " << seed_candidate << endl; cout << "[+] Custom Base64 Table:" << endl << custom_table << endl; cout << "[+] Encoded prefix: " << encoded << endl; found = true ; return true ; } return false ; } void worker (uint64_t start, uint64_t end) { for (uint64_t i = start; i <= end && !found; ++i) { check_seed (i); if (i % 100000 == 0 ) { cout << "\r[*] Scanning: " << fixed << (double )(i - start_seed) / (end_seed - start_seed) * 100 << "%" ; cout.flush (); } } } int main () { cout << "[*] Starting brute-force from " << start_seed << " to " << end_seed << endl; cout << "[*] Target Base64: " << target_b64 << endl; cout << "[*] Known prefix: " << known_prefix << endl; cout << "[*] Using " << num_threads << " threads" << endl; vector<thread> workers; uint64_t range = (end_seed - start_seed) / num_threads; for (size_t t = 0 ; t < num_threads; ++t) { uint64_t start = start_seed + t * range; uint64_t end = (t == num_threads - 1 ) ? end_seed : start + range - 1 ; workers.emplace_back (worker, start, end); } for (auto & w : workers) { w.join (); } if (!found) { cout << "[-] No matching seed found." << endl; } return 0 ; }
得到表:GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/EFABCD H&NCTF{Ye5h!!!I_Lik333_bur9~^o^}
HNDRIVER: 查看用户态程序:
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 int __fastcall main (int argc, const char **argv, const char **envp) { void *v3; void *v4; void *v6; __int64 v7; _BYTE v8[32 ]; _BYTE v9[40 ]; _BYTE dst_[760 ]; if ( argc == 2 ) { qmemcpy ( dst_, L" _ _ _ _ _ __ ______ _____ _ _ ______ _ \n" "| | | || \\ | || |/ /| ____|| __ \\ | \\ | || ____|| | \n" "| |__| || \\| || ' / | |__ | |__) || \\| || |__ | | \n" "| __ || . ` || < | __| | _ / | . ` || __| | | \n" "| | | || |\\ || . \\ | |____ | | \\ \\ | |\\ || |____ | |____ \n" "|_| |_||_| \\_||_|\\_\\|______||_| \\_\\|_| \\_||______||______|\n" , 0x2DEu ); v6 = (void *)sub_140004110 ((__int64)&qword_14003F320, (__int64)dst_); _CallMemberFunction0(v6, (void (__fastcall *)(void *))sub_1400044C0); sub_140003550 (v8, argv[1 ]); v7 = std::shared_ptr<__ExceptionPtr>::operator =(v9, v8); if ( (unsigned __int8)sub_140003210 (v7) ) sub_140004110 ((__int64)&qword_14003F320, (__int64)L"[R3] Backup path sent to driver successfully.\n" ); else sub_140004110 ((__int64)&qword_14003F320, (__int64)L"[R3] Failed to send backup info to driver.\n" ); std::wstring::~wstring (v8); return 0 ; } else { v3 = (void *)sub_140004110 ( (__int64)&qword_14003F320, (__int64)L"here's gift for you: A+cRZ+ofCz4yqqnHClujCqD5DF3RCW8Ltlxju5NzCqdHY/Pcu5nXul+c0l+buBCLGRXjoMp" "cG5ZM0lmFGRmFG5oFCW+cIYpi" ); _CallMemberFunction0(v3, (void (__fastcall *)(void *))sub_1400044C0); v4 = (void *)sub_140004110 ((__int64)&qword_14003F450, (__int64)L"Usage: coom.exe <BackUpFileName>" ); _CallMemberFunction0(v4, (void (__fastcall *)(void *))sub_1400044C0); return 1 ; } }
发现了一个base64密文,后续分析出整个程序只是把用户输入传给了驱动,分析驱动文件,发现了一个无xref的构造函数:
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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 __int64 __fastcall sub_140002010 (__int64 a1, __int64 a2) { NTSTATUS v3; NTSTATUS v4; NTSTATUS v5; PFLT_CONTEXT Context; int v7; unsigned int n0x100000; SIZE_T NumberOfBytes; ULONG BytesRead; PVOID Buffer_1; PFILE_OBJECT FileObject; PFILE_OBJECT FileObject_; union _LARGE_INTEGER FileSize; PVOID P; void *FileHandle; HANDLE FileHandle_; ULONG Length[2 ]; SIZE_T NumberOfBytes_1; int v20; HANDLE n4; PVOID Buffer; unsigned __int64 v23; struct _OBJECT_ATTRIBUTES ObjectAttributes; struct _OBJECT_ATTRIBUTES buf_; __int64 v26; struct _IO_STATUS_BLOCK IoStatusBlock; struct _IO_STATUS_BLOCK buf__1; __m128 v29[5 ]; Context = 0 ; if ( KeGetCurrentIrql () < 2u ) { n4 = PsGetCurrentProcessId (); if ( (unsigned int )n4 > 4 && FltGetStreamHandleContext (*(PFLT_INSTANCE *)(a2 + 24 ), *(PFILE_OBJECT *)(a2 + 32 ), &Context) >= 0 && Context ) { if ( *(_QWORD *)Context && RtlPrefixUnicodeString (&String1, *(PCUNICODE_STRING *)Context, 1u ) ) *((_DWORD *)Context + 13 ) = 2 ; *((_BYTE *)Context + 50 ) = 1 ; if ( p_Length ) { if ( *(_QWORD *)Context && RtlSuffixUnicodeString (p_Length, *(PCUNICODE_STRING *)Context, 1u ) ) { DbgPrintEx (0x4Du , 0 , "try backup %wZ \n" , *(_QWORD *)Context); FsRtlGetFileSize (*(PFILE_OBJECT *)(a2 + 32 ), &FileSize); DbgPrintEx (0x4Du , 0 , "file size: %llu\n" , FileSize.QuadPart); if ( !FileSize.QuadPart ) return 0 ; FileHandle = 0 ; FileObject = 0 ; memset (&ObjectAttributes, 0 , sizeof (ObjectAttributes)); ObjectAttributes.Length = 48 ; ObjectAttributes.RootDirectory = 0 ; ObjectAttributes.Attributes = 576 ; ObjectAttributes.ObjectName = *(PUNICODE_STRING *)Context; ObjectAttributes.SecurityDescriptor = 0 ; ObjectAttributes.SecurityQualityOfService = 0 ; memset (&IoStatusBlock, 0 , sizeof (IoStatusBlock)); FileHandle_ = 0 ; FileObject_ = 0 ; memset (&buf_, 0 , sizeof (buf_)); buf_.Length = 48 ; buf_.RootDirectory = 0 ; buf_.Attributes = 576 ; buf_.ObjectName = (PUNICODE_STRING)::P; buf_.SecurityDescriptor = 0 ; buf_.SecurityQualityOfService = 0 ; memset (&buf__1, 0 , sizeof (buf__1)); v26 = 0 ; n0x100000 = 0x100000 ; BytesRead = 0 ; v20 = 0 ; Buffer_1 = 0 ; NumberOfBytes = FileSize.QuadPart; Buffer_1 = ExAllocatePool (PagedPool, FileSize.QuadPart); if ( Buffer_1 ) { v3 = FltCreateFileEx ( *(PFLT_FILTER *)(a2 + 8 ), *(PFLT_INSTANCE *)(a2 + 24 ), &FileHandle, &FileObject, 0x80000000 , &ObjectAttributes, &IoStatusBlock, 0 , 0x80u , 3u , 1u , 0x24u , 0 , 0 , 0x800u ); if ( v3 >= 0 ) { v4 = FltCreateFileEx ( *(PFLT_FILTER *)(a2 + 8 ), *(PFLT_INSTANCE *)(a2 + 24 ), &FileHandle_, &FileObject_, 0x40000000u , &buf_, &buf__1, 0 , 0x80u , 0 , 5u , 0x24u , 0 , 0 , 0 ); if ( v4 >= 0 ) { while ( NumberOfBytes ) { Buffer = Buffer_1; if ( n0x100000 >= NumberOfBytes ) *(_QWORD *)Length = NumberOfBytes; else *(_QWORD *)Length = n0x100000; v5 = FltReadFile (*(PFLT_INSTANCE *)(a2 + 24 ), FileObject, 0 , Length[0 ], Buffer, 0 , &BytesRead, 0 , 0 ); if ( v5 < 0 ) { _mm_lfence(); DbgPrintEx (0x4Du , 0 , "[%s:%d] status: 0x%x\n" , "MINIFILTER::PostWriteOperation" , 334 , v5); break ; } sub_140001894 (v29); P = 0 ; v23 = 0 ; if ( n0x100000 >= NumberOfBytes ) NumberOfBytes_1 = NumberOfBytes; else NumberOfBytes_1 = n0x100000; v7 = sub_1400010C0 ((__int64)Buffer_1, NumberOfBytes_1, &P, &v23, (__int64)v29); if ( v7 < 0 ) { _mm_lfence(); DbgPrintEx (0x4Du , 0 , "[%s:%d] status: 0x%x\n" , "MINIFILTER::PostWriteOperation" , 342 , v7); break ; } v7 = FltWriteFile (*(PFLT_INSTANCE *)(a2 + 24 ), FileObject_, 0 , BytesRead, P, 0 , 0 , 0 , 0 ); ExFreePoolWithTag (P, 0 ); if ( v7 < 0 ) { DbgPrintEx (0x4Du , 0 , "[%s:%d] status: 0x%x\n" , "MINIFILTER::PostWriteOperation" , 347 , v7); break ; } NumberOfBytes -= BytesRead; } } else { _mm_lfence(); DbgPrintEx (0x4Du , 0 , "[%s:%d] status: 0x%x\n" , "MINIFILTER::PostWriteOperation" , 329 , v4); } } else { _mm_lfence(); DbgPrintEx (0x4Du , 0 , "[%s:%d] status: 0x%x\n" , "MINIFILTER::PostWriteOperation" , 324 , v3); } } if ( Buffer_1 ) ExFreePoolWithTag (Buffer_1, 0 ); if ( FileHandle ) FltClose (FileHandle); if ( FileHandle_ ) FltClose (FileHandle_); if ( FileObject ) ObfDereferenceObject (FileObject); if ( FileObject_ ) ObfDereferenceObject (FileObject_); } } } } if ( Context ) FltReleaseContext (Context); return 0 ; }
其中v7 = sub_1400010C0((__int64)Buffer_1, NumberOfBytes_1, &P, &v23, (__int64)v29);
是一个接受自定义base64表的base64编码函数,base64表v29由上文的sub_140001894(v29);
生成,查看这个函数:
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 __int64 __fastcall sub_140001894 (__m128 *a1) { __int64 n64; __int8 v2; int n63; unsigned int i; unsigned int i_4; __m128 *v6; v6 = (__m128 *)sub_1400010A4 (); sub_140003640 (a1, v6, 0x40u ); i = 20250603 ; for ( n63 = 63 ; n63 > 0 ; --n63 ) { i = sub_140001964 (i); i_4 = i % (n63 + 1 ); v2 = a1->m128_i8[n63]; a1->m128_i8[n63] = a1->m128_i8[i_4]; a1->m128_i8[i_4] = v2; } n64 = 64 ; a1[4 ].m128_i8[0 ] = 0 ; return n64; } _BYTE *sub_1400010A4 () { return sub_1400019E4 ((__int64)byte_1400042D0); } _BYTE *__fastcall sub_1400019E4 (__int64 a1) { unsigned __int64 n0x41; for ( n0x41 = 0 ; n0x41 < 0x41 ; ++n0x41 ) { _mm_lfence(); byte_140005060[n0x41] = sub_140001A4C (*(_BYTE *)(n0x41 + a1), n0x41); } return byte_140005060; } __int64 __fastcall sub_140001A4C (char a1, unsigned __int64 n0x41) { return (char )n0x41 ^ byte_1400042C0[n0x41 % 8 ] ^ (unsigned int )a1; } int __fastcall sub_140001964 (__int64 n20250603) { return (0x41C64E6D * n20250603 + 114514 ) & 0x7FFFFFFF ; }
提取密文和密钥进行同构:
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 byte_1400042C0 = bytes ([0x51 , 0x23 , 0x97 , 0xE8 , 0xDC , 0xBA , 0x45 , 0x67 ,]) byte_1400042D0 = bytes ([ 0x7A , 0x14 , 0xDE , 0xD3 , 0xB0 , 0xC9 , 0x0D , 0x17 , 0x1C , 0x45 , 0xD0 , 0x86 , 0x94 , 0xDD , 0x13 , 0x39 , 0x14 , 0x61 , 0xD7 , 0xC8 , 0xB9 , 0x9F , 0x37 , 0x2A , 0x10 , 0x4F , 0xFD , 0x94 , 0x81 , 0x96 , 0x6C , 0x3E , 0x03 , 0x56 , 0x8C , 0xFF , 0xB7 , 0xEC , 0x21 , 0x39 , 0x2E , 0x70 , 0xD6 , 0xF1 , 0xB3 , 0xF1 , 0x27 , 0x2B , 0x4E , 0x55 , 0xD1 , 0x91 , 0xBE , 0xC6 , 0x23 , 0x32 , 0x08 , 0x62 , 0xC3 , 0xBF , 0x8D , 0xCF , 0x12 , 0x6D ]) def xor_encrypt (a1: int , n0x41: int ) -> int : key = byte_1400042C0[n0x41 % 8 ] return (n0x41 ^ key ^ a1) & 0xFF def generate_encoded_table (initial_data ): encoded_table = bytearray (0x40 ) for i in range (0x40 ): a1 = initial_data[i] encoded_table[i] = xor_encrypt(a1, i) return encoded_table def lcg (seed ): while True : seed = (0x41C64E6D * seed + 114514 ) & 0x7FFFFFFF yield seed def shuffle_table (seed ): prng = lcg(seed) table = list (generate_encoded_table(byte_1400042D0))[:64 ] for i in range (63 , 0 , -1 ): j = next (prng) % (i + 1 ) table[i], table[j] = table[j], table[i] return bytes (table) custom_base64_table = shuffle_table(20250603 ) print ("Custom Base64 Table:" )print (custom_base64_table)
得到自定义base64表:idhR+nWSPOU0CGIrNmAqVZlYuo2sDt7yg6MBXF1aw4Kv9LHJkjb5p8/zxcefQ3ET 解密得到flag:HNCTF{3z_M1n1f1173r_C0mmun1c4710n_b9c1daa9-a2b3-491f-975b-de44e76e0a99} ,改为H&NCTF{3z_M1n1f1173r_C0mmun1c4710n_b9c1daa9-a2b3-491f-975b-de44e76e0a99} 即可
justgame: damctf 2025原题Is it data or data? ,只改了密文:
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 void __fastcall __noreturn main (int a1, char **a2, char **a3) { int n7; const char *target; char *s1; char n100; const char *p_n15_1; char *p_n15; __int64 n9; unsigned __int64 i; char s1_[264 ]; unsigned __int64 v12; v12 = __readfsqword(0x28u ); __printf_chk(1 , "please input number/n" , a3); n7 = ::n7; while ( 1 ) { if ( n7 == 7 ) { p_n15 = s2; n9 = ::n9; i = 15 ; if ( s2 != (char *)&n15 ) i = n15; if ( ::n9 + 1LL > i ) { std::string::_M_mutate(&s2, ::n9, 0 , 0 , 1 ); p_n15 = s2; } p_n15[n9] = 'g' ; ::n9 = n9 + 1 ; s2[n9 + 1 ] = 0 ; } else { while ( !sub_555555402020 () ) ; } target = "dfrghumrxuqh|" ; s1 = s1_; n7 = ::n7 + 1 ; n100 = 'd' ; ++::n7; do { ++s1; ++target; *(s1 - 1 ) = n100 - 3 ; n100 = *target; } while ( *target ); p_n15_1 = s2; *s1 = 0 ; if ( !strcmp (s1_, p_n15_1) ) sub_555555403720 (); } }
main函数仍为等待输入检验和目标值是否相同,以及在第15步会自动插入一个字符g,查看输入接收函数:
1 2 3 4 5 6 7 8 9 输入值/控制函数 作用 1 将用户输入的另一个数字作为 ASCII 值插入到字符串尾部 4 写入字符“f” 5 对最后一个字符-1 6 对最后一个字符+3 7 写入字符“a” 11 将最后一个字符改为“t” 13 删除最后一个字符 第8步 写入字符“g”
密文dfrghumrxuqh| 有一个自解密,调试得到acoderjourney ,构造payload就完了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 target = b"acoderjourney" payload = "[" for i in range (len (target)): cand = ord ("a" ) payload += "'7', " while not cand == target[i]: if cand < target[i]: cand += 3 payload += "'6', " else : cand -= 1 payload += "'5', " print (bytes ([cand])) payload += "]" print (payload)
得到payload['7', '7', '6', '5', '7', '6', '6', '6', '6', '6', '5', '7', '6', '7', '6', '6', '5', '5', '7', '6', '6', '6', '6', '6', '6', '5', '7', '6', '6', '6', '7', '6', '6', '6', '6', '6', '5', '7', '6', '6', '6', '6', '6', '6', '6', '5', '7', '6', '6', '6', '6', '6', '6', '5', '7', '6', '6', '6', '6', '6', '5', '5', '7', '6', '6', '5', '5', '7', '6', '6', '6', '6', '6', '6', '6', '6']
,注意还要插入一个13来删掉字母g,编写脚本提交payload:
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 from pwn import *host = "27.25.151.198" port = 38214 payloads = ['7' , '7' , '6' , '5' , '7' , '6' , '6' , '13' , '6' , '6' , '6' , '5' , '7' , '6' , '7' , '6' , '6' , '5' , '5' , '7' , '6' , '6' , '6' , '6' , '6' , '6' , '5' , '7' , '6' , '6' , '6' , '7' , '6' , '6' , '6' , '6' , '6' , '5' ,'7' , '6' , '6' , '6' , '6' , '6' , '6' , '6' , '5' , '7' , '6' , '6' , '6' , '6' , '6' , '6' , '5' , '7' , '6' , '6' , '6' , '6' , '6' , '5' , '5' , '7' , '6' , '6' , '5' , '5' , '7' , '6' , '6' , '6' , '6' , '6' , '6' , '6' , '6' ] conn = remote(host, port) initial = conn.recvuntil(b">" , timeout=5 ) for i, payload in enumerate (payloads): print (f"[+] Sending: {payload} " ) conn.sendline(payload.encode()) if i < len (payloads) - 1 : response = conn.recvuntil(b">" , timeout=5 ) else : final_output = conn.recvall(timeout=5 ).decode() print ("\n[+] Final output: " ) print (final_output) break conn.close()
flag{586cee19-8bf4-4027-8bf3-1fd6fa74be92}
xxxR01d: 查看mainctivity:
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 package com.aaron.xxxr01d;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View.OnClickListener;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;import com.aaron.nativelib.NativeLib;import java.nio.charset.StandardCharsets;public class MainActivity extends Activity { private static final byte [] FIXED_IV = null ; private static byte [] FIXED_KEY = null ; private static final String TAG = "MainActivity" ; private Button buttonCheck; private EditText editTextUserInput; private NativeLib nativeInterface; System.loadLibrary("Z1Y4" ); MainActivity.FIXED_KEY = new byte []{84 , 104 , 105 , 0x73 , 73 , 0x73 , 65 , 49 , 54 , 66 , 0x79 , 0x74 , 101 , 75 , 101 , 0x79 }; MainActivity.FIXED_IV = new byte []{49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 0x30 , 65 , 66 , 67 , 68 , 69 , 70 }; private void C73ck19unt () { String s = this .editTextUserInput.getText().toString(); if (s.trim().isEmpty()) { Toast.makeText(this , "不能为空哦!" , 0 ).show(); this .editTextUserInput.setError("此字段不能为空" ); return ; } if (s.trim().length() < 16 ) { Toast.makeText(this , "输入内容长度不能小于16位!" , 0 ).show(); this .editTextUserInput.setError("长度至少为16位" ); return ; } byte [] arr_b = s.getBytes(StandardCharsets.UTF_8); byte [] arr_b1 = this .nativeInterface.Tungtungtungsahur(MainActivity.FIXED_KEY, MainActivity.FIXED_IV, arr_b); if (arr_b1 == null ) { Toast.makeText(this , "加密失败!" , 0 ).show(); this .editTextUserInput.setError("加密失败" ); return ; } Log.d("MainActivity" , "加密后数据 (bytes hex): " + MainActivity.bytesToHex(arr_b1)); if (this .nativeInterface.validateEncryptedData(arr_b, arr_b1)) { Toast.makeText(this , "校验成功! flag-> " + s, 1 ).show(); this .editTextUserInput.setError(null ); return ; } Toast.makeText(this , "校验失败! 输入与解密结果不匹配。" , 1 ).show(); this .editTextUserInput.setError("校验失败" ); } public static String bytesToHex (byte [] arr_b) { if (arr_b == null ) { return "null" ; } StringBuilder stringBuilder0 = new StringBuilder (); for (int v = 0 ; v < arr_b.length; ++v) { stringBuilder0.append(String.format("%02X" , ((byte )arr_b[v]))); } return stringBuilder0.toString(); } @Override protected void onCreate (Bundle bundle0) { super .onCreate(bundle0); this .setContentView(layout.activity_main); this .buttonCheck = (Button) this .findViewById(id.buttonCheck); this .editTextUserInput = (EditText) this .findViewById(id.editTextUserInput); MainActivity.FIXED_KEY = new byte []{87 , 101 , 49 , 99 , 0x30 , 109 , 101 , 0x5F , 52 , 0x5F , 72 , 38 , 78 , 67 , 84 , 70 }; this .nativeInterface = new NativeLib (); this .buttonCheck.setOnClickListener(v -> { MainActivity.this .C73ck19unt(); }); } class com .aaron.xxxr01d.MainActivity.1 implements View .OnClickListener { final MainActivity this $0 ; @Override public void onClick (View view0) { MainActivity.access$000 (MainActivity.this ); } } } }
发现初始化了一个密钥:ThisIsA16ByteKey 和一个iv:1234567890ABCDEF ,但是oncreate中又用了We1c0me_4_H&NCTF 作为key,加密函数为Tungtungtungsahur
,验证函数为validateEncryptedData
,均为native方法,查看so层,发现有很多花指令,编写脚本去除:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from idc import *from ida_bytes import *start_addr = 0x13480 end_addr = 0x16310 pattern = [0x74 , 0x01 , 0x00 , 0x0F , 0xBE ] length = len (pattern) found_count = 0 for addr in range (start_addr, end_addr - length + 1 ): bytes_here = [get_wide_byte(addr + i) for i in range (length)] if bytes_here == pattern: original_byte = get_wide_byte(addr + 2 ) patch_byte(addr + 2 , 0x90 ) found_count += 1
去除后反编译函数发现是一个复杂的加密算法,查看validateEncryptedData:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 __int64 __fastcall Java_com_aaron_nativelib_NativeLib_validateEncryptedData ( __int64 a1, __int64 a2, __int64 a3, __int64 a4) { unsigned int v4; __int64 v8; const __m128i *v9; __m128i v10; v8 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL ))(a1, a3, 0LL ); (*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1368LL ))(a1, a3); v9 = (const __m128i *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL ))(a1, a4, 0LL ); (*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1368LL ))(a1, a4); v10 = _mm_or_si128( _mm_xor_si128(_mm_loadu_si128(v9), *(__m128i *)byte_7910), _mm_xor_si128(_mm_loadu_si128(v9 + 1 ), *(__m128i *)byte_79A0)); LOBYTE (v4) = _mm_testz_si128(v10, v10); (*(void (__fastcall **)(__int64, __int64, __int64, __int64))(*(_QWORD *)a1 + 1536LL ))(a1, a3, v8, 2LL ); (*(void (__fastcall **)(__int64, __int64, const __m128i *, __int64))(*(_QWORD *)a1 + 1536LL ))(a1, a4, v9, 2LL ); return v4; }
发现存储了两部分的密文:7910
和79A0
,提取得到3205ACCD2A7471916010989C15288D8E18EC2A88F1351C46D9E38DFFF293426F ,接着寻找加密函数,发现是动态注册的,找到地址:
1 2 3 4 5 6 7 8 .data.rel.ro: 0000000000036CE0 off_36CE0 dq offset aTungtungtungsa.data.rel.ro: 0000000000036CE0 .data.rel.ro: 0000000000036CE0 .data.rel.ro: 0000000000036CE8 dq offset aBBBB .data.rel.ro: 0000000000036CF0 dq offset sub_12A10.data.rel.ro: 0000000000036CF8 dq offset aTralalerotrala .data.rel.ro: 0000000000036D00 dq offset aBBBB .data.rel.ro: 0000000000036D08 dq offset sub_12C70
查看这两个函数,发现其分别是加密和解密函数:
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 __int64 __fastcall sub_12A10 (__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 n) { int v9; __int64 v10; const void *src; __int64 v12; unsigned int v13; __int64 v14; void *ptr_1; __int64 n_1; __int64 v18; __int64 v19; __int64 v20; __int64 v21; void *ptr; __int64 v23[8 ]; v23[1 ] = __readfsqword(0x28u ); v21 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL ))(a1, a3, 0LL ); v20 = a3; v9 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1368LL ))(a1, a3); v10 = (*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL ))(a1, a4, 0LL ); v19 = a4; LODWORD (a4) = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1368LL ))(a1, a4); src = (const void *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL ))(a1, n, 0LL ); n_1 = n; LODWORD (n) = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1368LL ))(a1, n); v18 = v10; sub_16830 (v23, v21, v9, v10, (int )a4); ptr = 0LL ; __android_log_print(4LL , "NativeLib" , "Input data length: %zu" , (int )n); v12 = sub_16C50 (v23, src, &ptr, (int )n); v13 = v12; if ( !v12 || !ptr ) { __android_log_print(6LL , "NativeLib" , "Encryption failed. Encode returned %zu, cEncryptedData is %p" , v12, ptr); ptr_1 = ptr; v14 = 0LL ; if ( !ptr ) goto LABEL_7; LABEL_6: j_j__free (ptr_1); goto LABEL_7; } __android_log_print(4LL , "NativeLib" , "Encryption successful. Encrypted data length: %zu" , v12); v14 = (*(__int64 (__fastcall **)(__int64, _QWORD))(*(_QWORD *)a1 + 1408LL ))(a1, v13); if ( v14 ) (*(void (__fastcall **)(__int64, __int64, _QWORD, _QWORD, void *))(*(_QWORD *)a1 + 1664LL ))(a1, v14, 0LL , v13, ptr); else __android_log_print(6LL , "NativeLib" , "Encrypt: Failed to allocate NewByteArray for result." ); ptr_1 = ptr; if ( ptr ) goto LABEL_6; LABEL_7: (*(void (__fastcall **)(__int64, __int64, __int64, __int64))(*(_QWORD *)a1 + 1536LL ))(a1, v20, v21, 2LL ); (*(void (__fastcall **)(__int64, __int64, __int64, __int64))(*(_QWORD *)a1 + 1536LL ))(a1, v19, v18, 2LL ); (*(void (__fastcall **)(__int64, __int64, const void *, __int64))(*(_QWORD *)a1 + 1536LL ))(a1, n_1, src, 2LL ); sub_16BF0 (v23); return v14; }
按说把加密函数hook成解密函数就行,但是这题的init_array有反hook:
1 2 3 4 5 6 .init_array: 0000000000039010 _init_array segment qword public 'DATA' use64 .init_array: 0000000000039010 assume cs :_init_array.init_array: 0000000000039010 .init_array: 0000000000039010 dq offset sub_133E0.init_array: 0000000000039018 dq offset sub_13480.init_array: 0000000000039018 _init_array ends
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned __int64 sub_133E0 () { pthread_t newthread_[3 ]; newthread_[1 ] = __readfsqword(0x28u ); __android_log_print(4LL , "CHECK" , "START CHECK MAPS" ); sub_13030 (); __android_log_print(4LL , "CHECK" , "START PTHREAD" ); if ( pthread_create (newthread_, 0LL , start_routine, 0LL ) ) { perror ("Failed to create thread" ); exit (1 ); } return __readfsqword(0x28u ); }
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 __int64 sub_13030 () { FILE *stream_1; FILE *stream; char *p_needle; unsigned int v3; bool v4; char haystack_[512 ]; unsigned __int64 v7; v7 = __readfsqword(0x28u ); stream_1 = fopen ("/proc/self/maps" , "r" ); if ( stream_1 ) { stream = stream_1; if ( fgets (haystack_, 512 , stream_1) ) { p_needle = needle; v3 = 0 ; do { if ( needle[0 ] ) { do { if ( strstr (haystack_, p_needle) ) { __android_log_print(4LL , "CHECK" , "Find:%s" , p_needle); LOBYTE (v3) = 1 ; } v4 = p_needle[512 ] == 0 ; p_needle += 512 ; } while ( !v4 ); } p_needle = needle; } while ( fgets (haystack_, 512 , stream) ); } else { v3 = 0 ; } fclose (stream); } else { return 0 ; } return v3; }
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 void __fastcall __noreturn start_routine (void *a1) { char *inp_1; int fd_1; int fd_2; int fd_3; struct in_addr *inp; FILE *stream_1; FILE *stream; int fd_4; int fd; struct sockaddr addr; char s[568 ]; inp_1 = &addr.sa_data[2 ]; *(_DWORD *)&addr.sa_data[10 ] = 0 ; *(_QWORD *)&addr.sa_data[2 ] = 0LL ; *(_DWORD *)&addr.sa_family = -1570177022 ; inet_aton ("127.0.0.1" , (struct in_addr *)&addr.sa_data[2 ]); fd_1 = socket (2 , 1 , 0 ); if ( connect (fd_1, &addr, 0x10u ) ) { fd = fd_1; *(_WORD *)addr.sa_data = -30115 ; inet_aton ("127.0.0.1" , (struct in_addr *)&addr.sa_data[2 ]); fd_3 = socket (2 , 1 , 0 ); if ( !connect (fd_3, &addr, 0x10u ) ) { LABEL_4: fd_2 = fd; goto LABEL_5; } while ( 1 ) { inp = (struct in_addr *)inp_1; stream_1 = fopen ("/proc/self/maps" , "r" ); if ( stream_1 ) { stream = stream_1; while ( fgets (s, 512 , stream) ) { if ( strstr (s, "frida" ) || strstr (s, "gadget" ) || strstr (s, "agent" ) || strstr (s, "/data/local/tmp/" ) || strstr (s, "-64.so" ) || strstr (s, "-32.so" ) ) { goto LABEL_6; } } fclose (stream); } sleep (1u ); inp_1 = (char *)inp; inp[2 ].s_addr = 0 ; *(_QWORD *)&inp->s_addr = 0LL ; *(_DWORD *)&addr.sa_family = -1570177022 ; inet_aton ("127.0.0.1" , inp); fd = socket (2 , 1 , 0 ); if ( !connect (fd, &addr, 0x10u ) ) break ; *(_WORD *)addr.sa_data = -30115 ; inet_aton ("127.0.0.1" , inp); fd_4 = socket (2 , 1 , 0 ); if ( !connect (fd_4, &addr, 0x10u ) ) goto LABEL_4; } fd_1 = fd; } __android_log_print(4LL , "CHECK" , "FIND PORT" ); fd_2 = fd_1; LABEL_5: close (fd_2); LABEL_6: exit (0 ); }
尝试绕过并调用tralalero tralala解密:
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 function hexToBytes (hex ) { for (var bytes = [], c = 0 ; c < hex.length ; c += 2 ) bytes.push (parseInt (hex.substr (c, 2 ), 16 )); return bytes; } function bytesToHex (bytes ) { return Array .prototype .map .call (new Uint8Array (bytes), function (b ) { return ('0' + b.toString (16 )).slice (-2 ); }).join ('' ).toUpperCase (); } var cipherHex = "3205ACCD2A7471916010989C15288D8E18EC2A88F1351C46D9E38DFFF293426F" ;var cipherBytes = hexToBytes (cipherHex);var libName = "libZ1Y4.so" ;Module .findExportByName (libName, "sub_13030" ) && Interceptor .attach (Module .findExportByName (libName, "sub_13030" ), { onLeave : function (retval ) { retval.replace (0 ); } }); Module .findExportByName (libName, "start_routine" ) && Interceptor .replace (Module .findExportByName (libName, "start_routine" ), new NativeCallback (function (arg0 ) { return ; }, 'void' , ['pointer' ])); var fgetsPtr = Module .findExportByName ("libc.so" , "fgets" );if (fgetsPtr) { Interceptor .attach (fgetsPtr, { onEnter : function (args ) { this .buf = args[0 ]; this .size = args[1 ]; this .stream = args[2 ]; }, onLeave : function (retval ) { if (retval.toInt32 () !== 0 ) { var bufStr = Memory .readUtf8String (this .buf ); if (bufStr && ( bufStr.includes ("frida" ) || bufStr.includes ("gadget" ) || bufStr.includes ("agent" ) || bufStr.includes ("/data/local/tmp/" ) || bufStr.includes ("-64.so" ) || bufStr.includes ("-32.so" ) )) { Memory .writeUtf8String (this .buf , "sth" ); } } } }); } var connectPtr = Module .findExportByName ("libc.so" , "connect" );if (connectPtr) { Interceptor .attach (connectPtr, { onEnter : function (args ) { var addr = args[1 ]; var port = Memory .readU16 (addr.add (2 )); if (port === 0x69A6 || port === 0x6A6A ) { this .block = true ; } }, onLeave : function (retval ) { if (this .block ) { retval.replace (-1 ); } } }); } Java .perform (function ( ) { var EditText = Java .use ("android.widget.EditText" ); var SpannableStringBuilder = Java .use ("android.text.SpannableStringBuilder" ); EditText .getText .overload ().implementation = function ( ) { var fakeInput = Java .use ("java.lang.String" ).$new("sth" ); return SpannableStringBuilder .$new .overload ('java.lang.CharSequence' ).call (SpannableStringBuilder , fakeInput); }; EditText .toString .overload ().implementation = function ( ) { return Java .use ("java.lang.String" ).$new("sth" ); }; }); Java .perform (function ( ) { var MainActivity = Java .use ("com.aaron.xxxr01d.MainActivity" ); var Toast = Java .use ("android.widget.Toast" ); MainActivity .C73ck19unt .implementation = function ( ) { console .log ("[+] Hooked C73ck19unt(), using fixed ciphertext as input" ); var arr_b = cipherBytes; var nativeInterface = this .nativeInterface .value ; var decryptedArr = nativeInterface.Tralalerotralala .overload ('[B' , '[B' , '[B' ).call (nativeInterface, MainActivity .FIXED_KEY .value , MainActivity .FIXED_IV .value , arr_b ); if (decryptedArr == null ) { Toast .makeText (this , "解密失败" , Toast .LENGTH_SHORT ).show (); return ; } var decryptedFlag = Java .use ("java.lang.String" ).$new(decryptedArr); console .log ("[+] 解密后的 flag 是:" , decryptedFlag); console .log ("[DEBUG] 解密结果 (hex):" , bytesToHex (decryptedArr)); Toast .makeText (this , "flag: " + decryptedFlag, Toast .LENGTH_LONG ).show (); if (this .nativeInterface .value .validateEncryptedData (arr_b, decryptedArr)) { Toast .makeText (this , "校验成功!" , Toast .LENGTH_SHORT ).show (); } else { Toast .makeText (this , "校验失败!" , Toast .LENGTH_SHORT ).show (); } }; });
H&NCTF{Xxx_1s_And_F0r_Android^^}
签到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 __int64 __fastcall main (int a1, char **a2, char **a3) { void *ptr; unsigned int v5; char s[40 ]; int v7; unsigned int v8; const char *MySecretKey123; int i; MySecretKey123 = "MySecretKey123!" ; fgets (s, 38 , stdin); v8 = strlen (s); v5 = sub_11B9 (MySecretKey123); v7 = sub_1452 (s, v8, v5, &ptr); putchar (10 ); for ( i = 0 ; i < v7; ++i ) { if ( *((_BYTE *)ptr + i) != byte_4080[i] ) { puts ("wrong" ); free (ptr); return 0xFFFFFFFFLL ; } } puts ("right" ); free (ptr); return 0 ; }
查看sub_11B9:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 __int64 __fastcall sub_11B9 (const char *MySecretKey123) { size_t v1; unsigned int v3; char v4; __int16 v5; char v6; v1 = strlen (MySecretKey123); SHA256 (MySecretKey123, v1, &v4); LOBYTE (v3) = v4 | 1 ; *(_WORD *)((char *)&v3 + 1 ) = v5 & 0xFEFE ; HIBYTE (v3) = v6 | 1 ; return v3; }
是取了硬编码密钥字符串生成了一个新的密钥,动态调试得到密钥51h, 16h, 34h, FBh ,查看加密函数:
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 __int64 __fastcall sub_1452 (const void *p_s, int n, int a3, __int64 p_ptr) { _BYTE v7[2 ]; _BYTE v8[2 ]; unsigned __int8 v9[2 ]; unsigned __int8 v10[2 ]; void *dest; int size; int n4; int size_1; n4 = 4 ; size = 4 * ((n + 3 ) / 4 ); *(_QWORD *)p_ptr = malloc (size + 4 ); if ( !*(_QWORD *)p_ptr ) return 0xFFFFFFFFLL ; **(_BYTE **)p_ptr = HIBYTE (n); *(_BYTE *)(*(_QWORD *)p_ptr + 1LL ) = BYTE2 (n); *(_BYTE *)(*(_QWORD *)p_ptr + 2LL ) = BYTE1 (n); *(_BYTE *)(*(_QWORD *)p_ptr + 3LL ) = n; dest = malloc (size); memcpy (dest, p_s, n); memset ((char *)dest + n, 0 , size - n); for ( size_1 = 0 ; size_1 < size; size_1 += n4 ) { v10[0 ] = *((_BYTE *)dest + size_1); v10[1 ] = *((_BYTE *)dest + size_1 + 1 ); v9[0 ] = *((_BYTE *)dest + size_1 + 2 ); v9[1 ] = *((_BYTE *)dest + size_1 + 3 ); sub_13AC (a3, v10, v8); sub_13AC (a3, v9, v7); *(_BYTE *)(size_1 + 4 + *(_QWORD *)p_ptr) = v8[0 ]; *(_BYTE *)(*(_QWORD *)p_ptr + size_1 + 4 + 1LL ) = v8[1 ]; *(_BYTE *)(*(_QWORD *)p_ptr + size_1 + 4 + 2LL ) = v7[0 ]; *(_BYTE *)(*(_QWORD *)p_ptr + size_1 + 4 + 3LL ) = v7[1 ]; } free (dest); return (unsigned int )(size + 4 ); }
发现是对明文的每两个字节和密钥加密,查看sub_13AC:
1 2 3 4 5 6 7 8 9 _BYTE *__fastcall sub_13AC (int a1, unsigned __int8 *a2, _BYTE *a3) { _BYTE *result; *a3 = *a2 * a1 + BYTE1 (a1) * a2[1 ]; result = a3 + 1 ; a3[1 ] = (*a2 * BYTE2 (a1) + HIBYTE (a1) * a2[1 ]) % 256 ; return result; }
是一个矩阵运算,提取密文编写脚本解密:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def decrypt_block (out0, out1, k0=81 , k1=22 , k2=52 , k3=251 ): for x in range (256 ): for y in range (256 ): o0 = (x * k0 + y * k1) % 256 o1 = (x * k2 + y * k3) % 256 if o0 == out0 and o1 == out1: return (x, y) return None cipher = bytearray .fromhex("000000250CE2708998B2BBE494A095AC389222F80E7B761A66C803052E7DA1043DC062FE6667028781F40000" ) for i in range (0 , len (cipher), 2 ): plain = decrypt_block(cipher[i], cipher[i+1 ]) if plain: print (f"{chr (plain[0 ])} {chr (plain[1 ])} " ,end="" )
H&NCTF{840584fb08a26f01c471054628e451}