看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

WriteUp 1年前 (2022) admin
522 0 0
看雪 2022 KCTF秋季赛 已于11月15日中午12点正式开始!比赛延续上一届的模式并进行优化,对每道题设置了难度值、火力值、精致度等多类积分,用规则引导题目的难度和趣味度。大家请注意:签到题(https://ctf.pediy.com/game-season_fight-216.htm)将持续开放,整个比赛期间均可提交答案,获得积分哦~

第八题《商贸往来》答题时间已截止,据统计:共5支战队成功提交flag

看雪2022 KCTF 秋季赛 | 第八题设计思路及解析



他们分别是:
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析


下面一起看看该赛题的设计思路和相关解析吧~



出题团队简介


第八题《商贸往来》出题方 KM_Zero 战队

战队成员id:伯爵的信仰、SwiZ
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析


赛题设计思路


题目是windows一类crackme,隐藏执行七段加密验证的shellcode。shellcode是通过特殊写法并嵌入混淆无效代码,然后vc编译后抠出来的。

团队名称:KM_Zero

成员看雪ID:伯爵的信仰、SwiZ

团长QQ:760732725

参赛题目以及答案(见附件)https://bbs.pediy.com/thread-275132.htm


故事背景


勇敢的骑士经过激烈的战斗终于杀死了东方恶龙,但是在恶龙的藏宝库里并没有看见公主的身影,经过仔细搜寻,在藏宝库中发现了一扇暗门。破开暗门后,找到了一张暗黄的羊皮卷和七个爬满青苔的石盒。

羊皮卷上写道:真正的勇士敢于直面难解的谜题!解开所有石盒的秘密,就会得到你想要的!出发吧,勇士!

题目流程


程序在正常运行后,会弹出验证码输入窗口,输入验证码进行验证,当输入验证码正确则出现验证成功提示,否则出现验证失败提示。

程序包括两个部分,一是窗口部分,一个是加密验证部分。在窗口收到输入的验证码并提交后,将验证码和加密过程的ShellCode脱壳并注入新进程进行加密验证并返回结果,窗口进程获取验证进程返回结果。

加密验证ShellCode三个方面的设计:一个是代码混淆设计,二是反调式设计,三是Key的加密和验证。在代码混淆的设计中,使用插入无效代码进行混淆处理,解题者应当想办法在其中找到有效代码;在反调试设计包括判断是否有存在硬件断点,是否存在软件断点INT3,是否存在调试窗口,
BeingDebugged是否为1等,解题者应当想办法绕过反调试。

在对输入进行加密和验证的过程中,包括字典,异或,移位、分段置换等方式,解题者应当找到解密过程,最后是验证前面加密后的结果。(注:ShellCode有七段,前六段都是单纯的对称加密,最后一段是对称加密和最终验证,算法都很简单,绝对可以逆推。)

解题思路


题目核心加密算法,全部由编译成为了shellcode。

shellcode运行手法模拟木马常用手法,挂起进程注入,最后拿到程序结果判断成功与否。

首先要,确定KEY的长度,就是128位,程序逆向即可。接着,提取出七段shellcode,提取点就是在Decompress这个API运行后可以找到,接口都是如下:
BOOL FUNC(HMODULE kernel32,PFN_LoadLibraryA, PFN_GetProcAddress, UCHAR* input);

七段组合在一起的文件格式如下:
4字节长度 + SHELLCODE + 4字节长度 + SHELLCODE + 4字节长度 + SHELLCODE + 4字节长度 + SHELLCODE + 4字节长度 + SHELLCODE + 4字节长度 + SHELLCODE + 4字节长度 + SHELLCODE

Shellcode都是非常简单的单字节操作加密,算法基本就是乱序字典转义和变异异或,所以找到字典和密钥很重要。

由于算法简单,我们为了提高难度,采取多端重复无效代码插入的方法,所有函数都写成内联,这样一把梭,这是之前的C代码:
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
这是编译后的二进制:
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
由于关闭了编译器的优化选项,让VC编译器帮我们完成了C语言级的代码膨胀,就是要让解题者看着不舒服。

但如果解题者,会模式识别,可以剔除无效代码,找出其中有效代码的特征,那字典和密钥就很好找了。

如果无法模式识别,也可以一步步的调试,反正都是单字节处理的,但也会遇到一定反调试和虚拟机识别的对抗。



赛题解析


本赛题解析由看雪论坛会员 in1t 给出:


看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

个人论坛主页:https://bbs.pediy.com/user-home-828011.htm




引子


本题我愿称之为 “非常6+1”,一题更比 6 题强,下图是题目概况:

看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
运行 32 位程序 crackme.exe,弹出一个经典 CM 界面,如下图所示。点击 check 按钮时,若输入的序列号错误,程序退出。
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析




管中窥豹


该程序没有 main 函数,在 sub_402D4C 里首先通过 PEB 找到 kernel32 模块,拿到两个关键函数 GetProcAddress 和 LoadLibraryA。借助它们找到本函数内所需的所有导入函数,并将一众函数指针保存到自定义结构体中:

struct custom{    int kernel32_hModule;    HMODULE (__stdcall *LoadLibraryA)(LPCSTR lpLibFileName);    FARPROC (__stdcall *GetProcAddress)(HMODULE hModule, LPCSTR lpProcName);    // LocalAlloc    // ExitProcess    // ...};

之后创建了一个事件 poision132(估计是用来防止多开的)与主对话框:
看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
其中 GetModuleHandleW 的后四个参数实为 DialogBoxParamA 的参数,因为汇编里它们四个在调用 GetModuleHandleW 前就入栈了,IDA 没能分辨出来。

对话框对应的函数是这样处理 check 按下的:
else if ( v22 == 273 && a3 == 1002 ){  v40 = custom;  v21 = (*(custom + 92))(a1, 1001);           // GetDlgItem  if ( v21 )  {    for ( i = 0; i < 0x100; ++i )      input[i] = 0;    input_len = (*(v40 + 96))(v21);           // GetWindowTextLengthA    if ( input_len <= 210 && input_len >= 1 )    {      (*(v40 + 100))(v21, input, 255);        // GetWindowTextA      input_len = (*(v40 + 24))(input);       // lstrlenA      if ( input_len >= 1 && input_len <= 128 )      {        for ( j = input_len; j < 128; ++j )        {          if ( j % 2 )            input[j] = 32;          else            input[j] = 127;        }        v39 = 1;      }      else      {        v39 = 0;      }    }    else    {      v39 = 0;    }  }  else  {    v39 = 0;  }  if ( v39 )  {    v48 = custom;    if ( (*(custom + 56))(0, crackme_exe_path, 255) )// GetModuleFileNameW    {      v27 = 104;      v28 = 0;      v29 = -72;      v30 = 0;      v31 = -1;      v32 = -48;      v19 = 0;      v36 = 0;      v8[17] = -1;      for ( k = 0; k < 0x44; ++k )        *(v8 + k) = 0;      v8[0] = 68;      for ( m = 0; m < 0x10; ++m )        *(&v42 + m) = 0;      if ( (*(v48 + 28))(crackme_exe_path, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, v8, &v42) )// CreateProcessW      {        v6[0] = 65543;        if ( !(*(v48 + 32))(v43, v6) )        // GetThreadContext          goto LABEL_69;        v19 = v6[44];        v36 = (*(v48 + 44))(v42, 0, 128, 4096, 4);// VirtualAllocEx        if ( !v36 )          goto LABEL_69;        for ( n = 0; n < 0x80; n += v17 )        {          v17 = 0;          if ( !(*(v48 + 48))(v42, n + v36, &input[n], 128 - n, &v17) )// WriteProcessMemory          {            v16 = 0;            goto LABEL_45;          }        }        v16 = 1;        if ( !v16 )          goto LABEL_69;LABEL_45:        v20 = (*(v48 + 20))(0);               // GetModuleHandleW        v10 = *(v20 + 60) + v20;        v9 = v19 - *(v10 + 40);        v11 = sub_B31000 - v20;        v28 = v36;        v30 = sub_B31000 + v9 - v20;        for ( ii = 0; ii < 0xC; ii += v15 )        {          v15 = 0;          if ( !(*(v48 + 48))(v42, ii + v19, &v27 + ii, 12 - ii, &v15) )// WriteProcessMemory          {            v14 = 0;            goto LABEL_52;          }        }        v14 = 1;LABEL_52:        if ( v14 )        {          (*(v48 + 36))(v43);                 // ResumeThread          v35 = 2;          if ( (*(v48 + 60))(v42, 30000) )    // WaitForSingleObject          {            (*(v48 + 64))(v42, 2);            // TerminateProcess          }          else          {            v18 = 0;            if ( (*(v48 + 52))(v42, &v18) && v18 != 2 )// GetExitCodeProcess              v35 = v18;          }          if ( v35 == 2 )          {            strcpy(v25, "Unknown Error");            (*(v48 + 88))(0, v25, v25, 0);    // MessageBoxA            (*(v48 + 16))(0);                 // ExitProcess            v13 = 0;          }          else          {            if ( v43 )            {              (*(v48 + 64))(v42, 2);          // TerminateProcess              (*(v48 + 40))(v43);             // CloseHandle            }            if ( v42 )              (*(v48 + 40))(v42);             // CloseHandle            if ( v35 != 1 )              (*(v48 + 16))(0);               // ExitProcess            v13 = v35;          }          v33 = v13;        }        else        {LABEL_69:          v34 = 2;          strcpy(v24, "Unknown Error");          (*(v48 + 88))(0, v24, v24, 0);      // MessageBoxA          (*(v48 + 16))(0);                   // ExitProcess          v12 = 0;          v33 = 0;        }      }      else      {        v33 = -1;      }    }    else    {      v33 = -1;    }    if ( v33 )    {      strcpy(v23, "SUCCESS!");      strcpy(v26, "INFO");      (*(custom + 88))(0, v23, v26, 0);       // MessageBoxA    }  }  (*(custom + 16))(0);                        // ExitProcess}

其行为主要是:
  1. 获得输入框内容,判断输入长度在 1 ~ 128 之间

  2. 新建一个子进程 crackme.exe 并挂起

  3. 在子进程中申请一片空间 A 并写入输入内容

  4. 将子进程 start 函数开头改为直接调用 sub_401000,并传入 A 的地址

  5. 启动子进程,等待子进程执行结束,获取其退出码,若为 1 则正确。


故父进程只起到检查作用,只需关心子进程的核心逻辑 sub_401000。

该函数形似 sub_402D4C,都是先找到一众函数地址,然后再执行自己的逻辑:
  1. 通过 Decompress 函数解压释放代码

  2. 调用前 6 个加密函数对输入进行加密

  3. 调用最后一个函数对输入进行最后一次加密的同时校验输入

    看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

因此,本题的题意应该就是分析这 7 种加密方式再写脚本还原了。





一叶障目


一开始我用 x32dbg 启动父进程,用 IDA 附加子进程。在跟进第一个加密函数后,天真的我按下了 F5,当听到电脑的风扇声时,我意识到问题没有那么简单。同时,我发现对同一组输入,输出的加密结果竟然每次都不一样,那八成是存在调试器检测了。


反正也反编译不出来,所以我交换了一下,用 IDA 与 x32dbg 来分别调试父、子进程,方便用插件隐藏调试器。这次对于同一组输入,输出果然固定了下来。

在汇编层面进行一些分析后,我发现本题的每个加密函数中都存在非常多的无用代码,应该是用来做代码膨胀的,这直接导致 IDA 需要分析大量无用逻辑来生成伪代码。虽然一直等下去有可能能跑出来,但是我怕还没看到伪代码就该下一题了,于是就采用选择明文攻击及对输入下硬件访问断点(简称连蒙带猜)来找到和还原加密逻辑。




山重水复


第一次加密

1、找到 128 以内的质数作为下标数组,访问输入,对对应下标的字符进行查表替换;

2、与已知数组逐字节异或。


Python 描述:
def enc1(plain: bytes) -> bytes:    tbl = bytes.fromhex("b466efcad9ebb6423614b123b5abd400b0bb96e430a87e5e872daa0147a03dd2dae185f5ff0cfdad07aff2c8739446fce77f1570baf3085f0aa38d1fe6056dc44d31881799c6b26b831c80db6927fac22eec4b2f62a4d339bc6a4a8633bf929144b9cdacf6976ce9906554747609e249f0f9a2139a1632f4823f6f290b5a22b39c4e68d0c1e041ae6428d5048f9f78cc1d180d675be5487b19edd7dd55590e258e2c4012601e10bdc571f851c721c01b45e83ba1f76e2b8cb7d60fde35892a1a7d95d1723ca53411b8525c75ee9bf1fba96179c9203ec337817ccb5798dcbe243a586302d8ea4f43849d064c9efee3a7a68a0356938b7ace385326cfdf775d50")     pp = b"0123456789ABCDEF" * 8    ee = bytes.fromhex("3031157034f3365f3839418843994546307f32703435365f38394142439945b23031323334f3363738a34188434445b23031323334f336373839418543994546303132703435365f38a34142434445b2303132703435363738a3414243444546307f323334f3365f3839418843994546307f32333435363738394142434445b2")    idxs = []    for i in range(len(ee)):        if ee[i] != pp[i]:            idxs.append(i)     cc = list(pp)    cipher = list(plain)    for idx in idxs:        cc[idx] = tbl[pp[idx]]        cipher[idx] = tbl[plain[idx]]     rr = bytes.fromhex("0b3a177e1fc61b4a70304b8958ad0560115632373b7020731c1a5b55059e658c232e150c31cf3507288173885e5c76ba2200731d2ab0130124117b8501a47d0f3c250b7470021c4617a5434146434ebf2122256d2b101f1c17967a7f00030c096326535653985b2e47bac803d60ed8e597d281868bf4f3f0ebe6a2a7aaabb449")    for i in range(len(cc)):        cipher[i] ^= cc[i] ^ rr[i]     return bytes(cipher)


第二次加密

  1. 逐字节查表替换

  2. 前后 64 字节位置调换

  3. 按 32 字节划分为 4 组,1、3 组位置交换

  4. 与已知数组逐字节异或

  5. 2、4 组位置交换

  6. 与已知数组逐字节异或


Python 描述:
def enc2(plain: bytes) -> bytes:    tbl = bytes.fromhex("38393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637716e66655e61696b5d64676f5c606a7063685b6d5f626c8788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff72737475767778797a7b7c7d7e7f80818283848586")     pp = bytes.fromhex("5a69aef84e591d6a7a3a3ef22d6b781f40e463b16a23265316102e227058189d727d445b60503301225206f32b2f0bab7353224a7b2f15072e1b0efe746200706d765af221511a661d763636333033ae707174eb7a43191a1d450f08757071763294020102075d0e4db0bd78a3c8a59ac660d0d1daa7f5f6e1ecd7d0dfd8c958")    cc = list(pp)    cipher = list(plain)     for i in range(len(plain)):        cc[i] = tbl[pp[i]]        cipher[i] = tbl[plain[i]]     for i in range(0x40):        cc[i], cc[0x80-i-1] = cc[0x80-i-1], cc[i]        cipher[i], cipher[0x80-i-1] = cipher[0x80-i-1], cipher[i]     for i in range(0x20):        cc[i], cc[0x40+i] = cc[0x40+i], cc[i]        cipher[i], cipher[0x40+i] = cipher[0x40+i], cipher[i]     rr = bytes.fromhex("680567cabc021234390d1ed230593e9cc250360d412f2e553e2e386c711999bb8f646b9c500264121a186ac43ec4222d93415f434746dd02370b7402256ad50455bf8f97818915916565a6f4fad443f8868aefa6a5fcf10159470c16141db429977f1142703e7e7940727203b2288f6a2ff41971056669f0183c5c4013ae0458")    for i in range(len(cc)):        cipher[i] ^= cc[i] ^ rr[i]     cc = list(rr)    for i in range(0x20):        cc[0x20+i], cc[0x60+i] = cc[0x60+i], cc[0x20+i]        cipher[0x20+i], cipher[0x60+i] = cipher[0x60+i], cipher[0x20+i]     rr = bytes.fromhex("f6b0c1702bb6869da6bcba77bde9b92f51db99a5efa3a6d4b8b2bcc0c781342b14dd80ccd0a3e8c0e7c0f79a198205f8b46e99fe90e4ca51afb5e498ca74df8488615077606bf6758083411c1369ac087036514d500731f9b5a9b3efeee95eda7ddf9a71a7c3a6d1deddac03f60de8e65f8c918c97970fd1e3dea2d5db962af9")    for i in range(len(cc)):        cipher[i] ^= cc[i] ^ rr[i]     return bytes(cipher)


第三次加密

  1. 相邻字节两两交换位置

  2. 与已知数组逐字节错位异或

  3. 前后 64 字节位置调换

  4. 逐字节加 0x80


Python 描述:
def enc3(plain: bytes) -> bytes:    tbl = bytes.fromhex("c4189868c047179a23f21a2f75ffd837eb1c7d1861cdf1b74914c18590e1616d7bd4f82307d23443325c3c1aeddff337e4ae04e36d2506bd4ea1f351bd3f8ca1974465b774ac094bdb56552fe61bfc579daa9b47710921084a979b12601de096438c39b57b9bfe9e0e196ceb9a9632a14c5c7e3291bd9017d0db023a8f002eb2")    tbl = list(tbl)    cipher = list(plain)     for i in range(0, len(cipher), 2):        cipher[i], cipher[i+1] = cipher[i+1], cipher[i]     for i in range(len(tbl)):        tbl[i] ^= cipher[(0x75 + i) % 128]     for i in range(0x40):        tbl[i], tbl[0x80-i-1] = tbl[0x80-i-1], tbl[i]     for i in range(len(tbl)):        tbl[i] = (tbl[i] + 0x80) & 0xFF     return bytes(tbl)


第四次加密

与已知数组逐字节加

Python 描述:
def enc4(plain: bytes) -> bytes:    cipher = list(plain)     pp1 = bytes.fromhex("a53f0c50366abda69abc3ecf6f580d6be2286767b4b24360f7cdf44e9c88f5931131d090a4b71fd9e1e0957144ed5f7dbc2ceceece0a5291bf6db45b822f9587c5954189bf76d9d7bf713f0aa36ceeb414f393794788f7754208923f11de80144e78c441def8bb74decb3a47247b014001990f031fb08b788c3512a3361749d3")    cc1 = bytes.fromhex("a642115b4c77f1b5fdba36cb428a409f17f2309fdea03474e6d0f7519f8bf8961434d393a7ba22dce4e3987447f05876b525e5e7c7034b8ab866ad547b289082c0903c84ba71d4d2ba6c3a059e67f5bb1bfa9a804e8ffe7c490f994618e587144e78c441def8bb74decb3a472483094809a1170b27b89380943d1aab3e0c3ec8")    dis = []    for i in range(len(pp1)):        dis.append((cc1[i] - pp1[i]) & 0xFF)     for i in range(len(cipher)):        cipher[i] += dis[i]        cipher[i] &= 0xFF     return bytes(cipher)


第五次加密

  1. 前 64 字节倒序排列

  2. 逐字节查表替换

  3. 与已知数组逐字节异或


Python 描述:
def enc5(plain: bytes) -> bytes:    cipher = []     for i in range(0x30, -1, -0x10):        for j in range(0xF, -1, -1):            cipher.append(plain[i+j])    cipher += list(plain[0x40:])    tbl = bytes.fromhex("d5a04a5de8a503a8dd2d980a4d8f901234023d5545b73f2cbabc07c70186df532961253e09217b85b60895300bb09e6bdbf9af51844950d210ebac2b48c56a74cdfd1d314bd6f3b381367e9c582f4c6c0513635a419b387c6519d047c4927a6fefb15783271a71d8fa2ae142a28ceaff166400561e374328ab06c9c3d3a614aeb5e6969a9d5f2615f223a7a34e8772ec208059a440f70ce7bb33ede2ee0f8eb20e3c916824754660a17932b8738b76d11fda8d22f00dc011c2e0665ed79ffe88dc7d8aca4404d417c61b94f654694f6de5be3b6ecbf18278f4def5bd2ece67c835ad7752c17fb99339f8e418bfd9cfb489cc5c1cfcaa3a7062fb5be3a99997e9")     for i in range(0x80):        cipher[i] = tbl[cipher[i]]     pp = bytes.fromhex("9620b6c3418b71c2a79c5d17937f210d436589b31ebb52c12e256660a46e84450c62a3b21370e5b91e840e67b2db5c2cb2cda71df65066990dcc285847021d46dc20489d6664cb3b66a2aca58ed8aa5ec75bedb54cec97d3361233f3ba7f15454cab44fd67625e1e67f6acb3099a2d812d3c2c0a85c2a4b540c507b86a4d6ac6")    cc = bytes.fromhex("1e76d27f22bf0ba060c447584fa9b895954c4a32bdc046d036cd250181466efc2f002f48486ec74947015bcc5249878f274d2cfb1ab6a9b9a362ca9cff8f586ac125846ea7c2acf7c225d7662ca5b39e5387ca66b8687d953a4d09b09436291f92e9a38ace7ce6cdd1ba7464e488dc9118143ec808ec822e4438538100d068b3")    for i in range(len(cipher)):        cipher[i] ^= pp[i] ^ cc[i]    return bytes(cipher)


第六次加密

与已知数组逐字节异或

Python 描述:
def enc6(plain: bytes) -> bytes:    p = bytes.fromhex("cf00fa1ac6497c69840b0c74e6d8c2ad50e23957b7b581e678aac75be30e61d895f5d38858be754f4dcf567f9f1cf0cc89fed6aee83cf43e20c8c8fadbc6f2cbd16eca9559b795199d171848be19fa92a30f49b7dc3ec2f54599d28e29a38546bc459e3b969792d0fb06532ac838a4c8fd72055c12d398bbd1f5ad497a5a831b")    c = bytes.fromhex("5a125abad541706a858bf87f4354c5ab409259b7e73511f6589a87ab23de41f885c5d2855fbf7a4d44df76ef7fec30fc005fe6ee885cb41e60e8880a1b16d2ebe11efa65eabc97298c061940be6770a8132f0907acaedbc505dbf2e563b7ad50eec6ee66e6f7b240eb0603bad868f498ed52956c62f3a8cbe1863b497658ae6b")    tbl = [p[i] ^ c[i] for i in range(len(p))]     return bytes([tbl[i] ^ plain[i] for i in range(len(plain))])


第七次加密及校验

  1. 逐字节查表替换 × 7

  2. 加密结束,与目标数组比较


Python 描述:
def enc7(plain: bytes) -> bytes:    tbl1 = bytes.fromhex("588d51e8d4d9a5c53094c6bab325c238a4c8aaf74e2e603b3ab2a975a12316fb64e42478322631434bbcb8ac639b7e871b42b59e612a866e4cd2355ad6e9b91db77fa8537bd7de799a745f0bf001ade24a3d98651e6cd8dbabe5442221cb507615565c2972896d418c8e19e1919df2063f1a7d978a8392dc77f936cf48a0c78214e3963984b188284611afa352fe10f457f1e0ca547ca27049a72db6d090da7a67c3bd1cf6f52b9308691359624feaced3eceb5ed10e04204d0f8bbbc9bf03059ccc40dfef853e3cf3ffd50a272fee8f6a1ffd955d817355dde75b6f18c4ae450d689fc012edcd0c33f8fc2c003747bee634a666b0b49971806b07fac1170209")    tbl2 = bytes.fromhex("35b3a22a08d483add97fb6c277933814aa1341488d79eca0f6529df8a64d30fa6124b557658ef9f0d1c3c41acd252f676aa44c17686682c7cc6343e987dd8cb2ea3f7cfc6d115db8b1bd562cc186b044056236a8399427ae7b80df73e5e233e3d3cba7859b9991d0006921e66e9806372b8bf42e95a3cf0c7119530410c96b3aee1b88e12397d6f2d54fe4be4270ffe8de5c7db9f126a9843bced2bcc6d83d501c81db2d6045493e587692c512fb9c54ba070290ef477e75af18bb51f5209a9631349f599e46bfa1ca89014a5a1f1e5bf78fb7d7fe5ee01528e7dcda725f55030d64744b29c04e78ab40226f1d09f3a516c8fd6cac8a0fed0a3c327a0b0eebb4")    tbl3 = bytes.fromhex("7f9458886cfd2dafe88538c7287bebeeb925894fd2053a0cde8ffccb65ad45c959e12b2f4372c381db8eab010677075e74bc03628c543d3718e279840a307e2e83f95746d15b1a1f116820a76495710b5627a350bbc102e035b582d333ca4a0eef26f413b786acc83ed8759d233b934ca15ab6a0048b8a551922be61e69690c099c69ed7603241f087e9aa175f7c4b1b0d40ffb34269e410faa9d62cf5edbad9480f4ef378d014f7a66eaea870669a52801d34366b5da4ecdf0891ce9c4992a221cc2a6df8bd514d6ae55ce767b0f2f12412989b29b4b197fbc453cdda44e3dd1e6f31dc3cc576ead5f673c28d3f1ccf477aa50039fe7db215d4b863bf9f1609")    tbl4 = bytes.fromhex("4f8a646ddee94796105c220be55999d3319533ff1821ab3d2d78291e7758844db99ff851357d2b666ec2b15e2cdc5fd742ae98371fe2439cd94bcadfec9761398334676345366270243f877e467ca203d47394a41da6bb1add8cebd6d8be93feb419a832002a0a11c94ced28f3fc8ea0a1f675c0ce0754fbb512ea914e9dc7746014ac8f385ac152501c539bf1c4e36a4a41b2e8e19071b3cc0c57f9d2ada523bd7615effdbcf5af85dae068f4c8fa69d5256b273b891655db9ee448b69aee80f7170406b0921bcfbf6f86727bf05db713ba050f01e6aa2fc35b7a026ccda32ea9c53c568882e78d3e8120cb0d79f244653a260830a7b84940d07fc6d18b0e09")    tbl5 = b'x1dxbcs,xdbx81xe3x04x06Ux8dFxfcQx83x00Gx14v-Cxf8xc6x10lx11x1cxc0x96Vx99xbdxd7xebxa8txc4=x0cixeaxfd.jxbfx947!x0fo(x1fxa7x1bg6x13xd9xffxe8d0?xf4q+x8c\p'm@2xa0z#xcctx871xc1DNx80&cx07x12Wx91<`h|wxb7x84xaex9cxc9xc2xd0xc3x97xf1B%y$xc8x02xca/xe4xf0Orx92x0bxfbxa3nxe7x1eTxc7xa4Jxe0xd5x86xb1xf7x8bYxd2x89xf5x08En:x93xd4xd6}xf9{x85x9dxfexd3"L;~xbexabxb0xb9xa9x88xa6xdaxbbxf6x1ax05xdex18>x8exb4xfaxbaxb3xb8xb6kx0eK5xcbxecxdcx17x8f xf2*ebx82xf3xa2xcf^xb5xe6xcex9ffxe9PRxef[x9exddx8axe2Iauxed8xd8xd1xe1)x15x98ZSx01xa5Mxcdxc5x90]r9x7f4xdf_x16x03xeexa1xadx9bxafxb23Xxe5x19HAxaax95xacx9ax'    tbl6 = b'Vxd6xxdcx8fsxcfx8ex0bx14xaaxfa?=xc0xf1)xdex8b:txb1nx8cHxc5xc4Rxdd.xd76yDx7fxe1xc1x99xd4x9axd8hxa52WCx17xa2xdfx87x82xf8xe6xe7xb5x004xb2xc9rXxaeKxadxc3xc7x88x91nx051dz>Mbx06mxa4xc6xe3Bjixa3xccxb0x92axbfx8a!Qx07xa7-&Ax10x08x19xe2x1ckrxcaxb4x18x94UxdaJxf3Y^"x0cExbexfbG@}xd0]*xf2x0eO/xcdx90x03x97xff<x968x15x80xecx0fxc2x1bxb3xebx9b~Ixaf{gx9d$x86tfxf7\;xd3xfdx04ox1fxd9xb8xa8evx8dx95xbbxb9lxb7xeex01xf4 `x1auxf65x89%xbdx11x83,xbcFxd5xcbxab(w'xd2xc8xe0xb6Nxd1x98xe9xcepxe4P+xe8xa1#xa6xfexedSx9cxa9x127Z[x81xbaLTxf9x9exdb3xeax939x1dx02qcx16x85x1ex9f|xacxf0xf5x84xefxa00xe5x13_xfc'    tbl7 = b' ;xecxbexb6x87x85x9fSxefWxe0xc3x01xf8:xe7AKr)x0blx04yxfcx18xb4gxcexf0x0eZ4x9exf6x94xacXoxf3xc42x8aUx9cx07xb0exe3x9bRQxc7xddcxa9x12x0fx95x80Yx1axcf{0xa8xab1/ax16x1cMi"pxcbu<xd3tx91xf1Oxa5xe2xfdxbdx11m6+xaaxd5x90xe4x8dxdaxb8xa2xafx1dx98x1fx88*Ix82xx99xebGfxca?xa6J&xdfDx7fx13xa4xb1x02zxc9x9dx00xd6^xbaCxc2x03xff(5xbcdqxdcxa3[xd4xeex83xf7F-k}xaebxd9xa7,xc8rxa1x8ejxe8`nxb2@%hxf4xb7xb3wxc18xc0xd8xd1xfexb9'xbfE]=x19xf5xcc9xdex81x9a3xc5sx15\Px89Nxa0!$xdbxbbVx17_xadxf2x8cx8fx96x97xe5Tx86tx1bBx10Hxb5xf9~|x05xd7>x067xe6x93xfaxe9vnx1exedx0c.xcdxfbx08xeax84x92xd0xc6Lxe1#xd2x14x8b'     cipher = list(plain)    tbls = [tbl1, tbl2, tbl3, tbl4, tbl5, tbl6, tbl7]    for tbl in tbls:        for i in range(len(cipher)):            cipher[i] = tbl[cipher[i]]    return bytes(cipher) target = bytes([121, 23, 66, 107, 59, 80, 122, 227, 70, 208, 222, 78, 36, 167, 138, 106, 105, 208, 2, 6, 240, 39, 36, 189, 192, 187, 227, 30, 9, 163, 151, 48, 60, 182, 235, 104, 144, 9, 208, 234, 17, 242, 196, 96, 165, 203, 195, 252, 69, 251, 92, 83, 192, 128, 58, 153, 89, 111, 47, 84, 74, 217, 14, 106, 52, 222, 210, 236, 175, 74, 11, 164, 138, 182, 250, 147, 31, 4, 16, 68, 238, 228, 214, 158, 244, 69, 18, 77, 55, 60, 240, 17, 248, 200, 68, 118, 99, 16, 115, 45, 104, 215, 157, 47, 195, 132, 42, 182, 204, 181, 55, 97, 79, 15, 22, 188, 187, 140, 83, 39, 238, 119, 170, 31, 156, 194, 24, 222]) assert enc7(enc6(enc5(enc4(enc3(enc2(enc1(user_input))))))) == target





抛砖引玉


第七个函数比较小,当时一不小心反编译出来,就花了点时间写去混淆的 IDAPython 脚本。原理是基于模式匹配,识别出其中的 13 种无用代码的模式(对应附件 patterns.txt),再将它们 nop 掉。

脚本在该函数上效果很好,运行后只因本只剩下核心逻辑,代码如下(运行前要先创建加密函数,拿到函数的起始、结束地址):
import re def nop(ea: int, size: int):    for i in range(size):        patch_byte(ea + i, 0x90) def printable(hex_str: str):    return 32 <= int(hex_str, 16) < 127 def deobfus(start: int, end: int):    cur = start    while cur < end:        asm1 = generate_disasm_line(cur, 0)        asm2 = generate_disasm_line(cur+get_item_size(cur), 0)         m1 = re.match(r"mov +[ebp+var_(.+)], *1000h", asm1)        m2 = re.match(r"lea +eax, *[ebp+var_(.+)]", asm2)        if m1 and m2 and m1.group(1) == m2.group(1):            print(f"found pattern-1 at: {hex(cur)}")            asm3 = generate_disasm_line(cur + 0xd4 - 6, 0)            if re.match(r"mov +[ebp+var_(.+)], *eax", asm3):                nop(cur, 0xd4)                cur += 0xd4                continue         m1 = re.match(r"mov +[ebp+var_(.+)], *(.{2})h", asm1)        m2 = re.match(r"mov +[ebp+var_(.+)], *(.{2})h", asm2)        if m1 and m2 and int(m1.group(1), 16) - int(m2.group(1), 16) == 1 and printable(m1.group(2)) and printable(m2.group(2)):            if get_bytes(cur + 0x1d7 - 2, 2) == b'xebxc7':                print(f"found pattern-2 at: {hex(cur)}")                nop(cur, 0x1d7)                cur += 0x1d7                continue            elif get_bytes(cur + 0x1bb - 2, 2) == b'xebxc7':                print(f"found pattern-2 at: {hex(cur)}")                nop(cur, 0x1bb)                cur += 0x1bb                continue            elif get_bytes(cur + 0x12d - 5, 5) == b'xE9x5AxFFxFFxFF':                print(f"found pattern-11 at: {hex(cur)}")                nop(cur, 0x12d)                cur += 0x12d                continue         m1 = re.match(r"and +[ebp+var_(.+)], *0", asm1)        m2 = re.match(r"lea +eax, *[ebp+var_(.+)]", asm2)        if m1 and m2 and m1.group(1) == m2.group(1):            asm3 = generate_disasm_line(cur + 0xb5 - 7, 0)            if re.match(r"or +[ebp+var_(.+)], *0FFFFFFFFh", asm3):                print(f"found pattern-3 at: {hex(cur)}")                nop(cur, 0xb5)                cur += 0xb5                continue         m1 = re.match(r"and +[ebp+var_.+], *0", asm1)        m2 = re.match(r"and +[ebp+var_.+], *0", asm2)        asm3 = generate_disasm_line(cur + 0x73, 0)        m3 = re.match(r"cmp +[ebp+var_.+], *0Ah", asm3)        if m1 and m2 and m3:            if get_bytes(cur + 0x15c - 5, 5) == b"xE9x0AxFFxFFxFF":                print(f"found pattern-4 at: {hex(cur)}")                nop(cur, 0x15c)                cur += 0x15c                continue         m1 = re.match(r"and +[ebp+var_.+], *0", asm1)        m2 = re.match(r"mov +[ebp+var_.+], *1", asm2)        asm3 = generate_disasm_line(cur + 0x90, 0)        m3 = re.match(r"cmp +[ebp+var_.+], *5", asm3)        if m1 and m2 and m3:            if get_bytes(cur + 0x192 - 5, 5) == b"xE9xF1xFExFFxFF":                print(f"found pattern-5 at: {hex(cur)}")                nop(cur, 0x192)                cur += 0x192                continue         if m1 and m2 and get_bytes(cur + 0x98, 2) == b"x72xC1":            asm3 = generate_disasm_line(cur + 0xb5 - 0xa, 0)            if re.match(r"mov +[ebp+var_.+], *1", asm3):                print(f"found pattern-12 at: {hex(cur)}")                nop(cur, 0xb5)                cur += 0xb5                continue         m1 = re.match(r"mov +[ebp+var_.+], *0Ah", asm1)        m2 = re.match(r"mov +[ebp+var_.+], *2", asm2)        asm3 = generate_disasm_line(cur + 0x7a, 0)        m3 = re.match(r"cmp +[ebp+var_.+], *9", asm3)        if m1 and m2 and m3:            if get_bytes(cur + 0x109 - 5, 5) == b"xE9x64xFFxFFxFF":                print(f"found pattern-6 at: {hex(cur)}")                nop(cur, 0x109)                cur += 0x109                continue         m1 = re.match(r"mov +[ebp+var_.+], *1", asm1)        m2 = re.match(r"mov +[ebp+var_.+], *3", asm2)        asm3 = generate_disasm_line(cur + 0x92, 0)        if m1 and m2 and "jg" in asm3:            if get_bytes(cur + 0x14f - 5, 5) == b"xE9x37xFFxFFxFF":                print(f"found pattern-7 at: {hex(cur)}")                nop(cur, 0x14f)                cur += 0x14f                continue         m1 = re.match(r"mov +[ebp+var_.+], *1", asm1)        m2 = re.match(r"mov +[ebp+var_.+], *5", asm2)        asm3 = generate_disasm_line(cur + 0xc8, 0)        m3 = re.match(r"mov +[ebp+var_.+], *2", asm3)        if m1 and m2 and m3:            if get_bytes(cur + 0x198 - 5, 5) == b"xE9xFExFExFFxFF":                print(f"found pattern-8 at: {hex(cur)}")                nop(cur, 0x198)                cur += 0x198                continue         m1 = re.match(r"mov +[ebp+var_.+], *20h", asm1)        m2 = re.match(r"mov +[ebp+var_.+], *6Dh", asm2)        asm3 = generate_disasm_line(cur + 0x5c, 0)        m3 = re.match(r"cmp +[ebp+var_.+], *3", asm3)        if m1 and m2 and m3:            if get_bytes(cur + 0xf7 - 5, 5) == b"xE9x58xFFxFFxFF":                print(f"found pattern-9 at: {hex(cur)}")                nop(cur, 0xf7)                cur += 0xf7                continue         m1 = re.match(r"and +[ebp+var_.+], *0", asm1)        if m1 and get_bytes(cur + 0xe, 2) == b"xEBx0D" and get_bytes(cur + 0xc6, 5) == b"xB9x00x04x00x00":            asm3 = generate_disasm_line(cur + 0x136 - 6, 0)            if re.match(r"mov +[ebp+var_.+], *eax", asm3):                print(f"found pattern-10 at: {hex(cur)}")                nop(cur, 0x136)                cur += 0x136                continue         m1 = re.match(r"mov +[ebp+var_.+], *3", asm1)        m2 = re.match(r"mov +[ebp+var_.+], *2", asm2)        if m1 and m2 and get_bytes(cur + 0x4c, 2) == b'x73x34':            if get_bytes(cur + 0x82 - 2, 2) == b"xEBxBE":                print(f"found pattern-13 at: {hex(cur)}")                nop(cur, 0x82)                cur += 0x82                continue         cur += get_item_size(cur) start_ea = 0x028ADA1Cend_ea = 0x028E5FC1 deobfus(start_ea, end_ea)del_items(start_ea, 0, end_ea)create_insn(start_ea)add_func(start_ea)

不过在其他 6 个函数上用的效果不是很好,因为其他函数中还有许多 call 无用 API 的模式没有考虑。它们肯定都可以通过模式识别进行定位并去除,考虑到未知的工作量,赛中就没有再写下去了。脚本放在这里,希望能起到抛砖引玉的作用。




柳暗花明


将每个加密函数的逆函数写出来再逐个调用就得到正确的序列号了,代码如下:
def dec1(cipher: bytes) -> bytes:    plain = list(cipher)    tbl = bytes.fromhex("b466efcad9ebb6423614b123b5abd400b0bb96e430a87e5e872daa0147a03dd2dae185f5ff0cfdad07aff2c8739446fce77f1570baf3085f0aa38d1fe6056dc44d31881799c6b26b831c80db6927fac22eec4b2f62a4d339bc6a4a8633bf929144b9cdacf6976ce9906554747609e249f0f9a2139a1632f4823f6f290b5a22b39c4e68d0c1e041ae6428d5048f9f78cc1d180d675be5487b19edd7dd55590e258e2c4012601e10bdc571f851c721c01b45e83ba1f76e2b8cb7d60fde35892a1a7d95d1723ca53411b8525c75ee9bf1fba96179c9203ec337817ccb5798dcbe243a586302d8ea4f43849d064c9efee3a7a68a0356938b7ace385326cfdf775d50")     pp = b"0123456789ABCDEF" * 8    ee = bytes.fromhex("3031157034f3365f3839418843994546307f32703435365f38394142439945b23031323334f3363738a34188434445b23031323334f336373839418543994546303132703435365f38a34142434445b2303132703435363738a3414243444546307f323334f3365f3839418843994546307f32333435363738394142434445b2")    idxs = []    for i in range(len(ee)):        if ee[i] != pp[i]:            idxs.append(i)     cc = list(pp)    for idx in idxs:        cc[idx] = tbl[pp[idx]]     rr = bytes.fromhex("0b3a177e1fc61b4a70304b8958ad0560115632373b7020731c1a5b55059e658c232e150c31cf3507288173885e5c76ba2200731d2ab0130124117b8501a47d0f3c250b7470021c4617a5434146434ebf2122256d2b101f1c17967a7f00030c096326535653985b2e47bac803d60ed8e597d281868bf4f3f0ebe6a2a7aaabb449")    for i in range(len(cc)):        plain[i] ^= cc[i] ^ rr[i]     for idx in idxs:        plain[idx] = tbl.index(plain[idx])     return bytes(plain) def dec2(cipher: bytes) -> bytes:    plain = list(cipher)    tbl = bytes.fromhex("38393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637716e66655e61696b5d64676f5c606a7063685b6d5f626c8788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff72737475767778797a7b7c7d7e7f80818283848586")     rr = bytes.fromhex("f6b0c1702bb6869da6bcba77bde9b92f51db99a5efa3a6d4b8b2bcc0c781342b14dd80ccd0a3e8c0e7c0f79a198205f8b46e99fe90e4ca51afb5e498ca74df8488615077606bf6758083411c1369ac087036514d500731f9b5a9b3efeee95eda7ddf9a71a7c3a6d1deddac03f60de8e65f8c918c97970fd1e3dea2d5db962af9")    cc = list(bytes.fromhex("680567cabc021234390d1ed230593e9cc250360d412f2e553e2e386c711999bb8f646b9c500264121a186ac43ec4222d93415f434746dd02370b7402256ad50455bf8f97818915916565a6f4fad443f8868aefa6a5fcf10159470c16141db429977f1142703e7e7940727203b2288f6a2ff41971056669f0183c5c4013ae0458"))    for i in range(0x20):        cc[0x20+i], cc[0x60+i] = cc[0x60+i], cc[0x20+i]     for i in range(len(plain)):        plain[i] ^= cc[i] ^ rr[i]    for i in range(0x20):        plain[0x20+i], plain[0x60+i] = plain[0x60+i], plain[0x20+i]     rr = bytes.fromhex("680567cabc021234390d1ed230593e9cc250360d412f2e553e2e386c711999bb8f646b9c500264121a186ac43ec4222d93415f434746dd02370b7402256ad50455bf8f97818915916565a6f4fad443f8868aefa6a5fcf10159470c16141db429977f1142703e7e7940727203b2288f6a2ff41971056669f0183c5c4013ae0458")    pp = bytes.fromhex("5a69aef84e591d6a7a3a3ef22d6b781f40e463b16a23265316102e227058189d727d445b60503301225206f32b2f0bab7353224a7b2f15072e1b0efe746200706d765af221511a661d763636333033ae707174eb7a43191a1d450f08757071763294020102075d0e4db0bd78a3c8a59ac660d0d1daa7f5f6e1ecd7d0dfd8c958")    cc = list(pp)     for i in range(len(plain)):        cc[i] = tbl[pp[i]]    for i in range(0x40):        cc[i], cc[0x80-i-1] = cc[0x80-i-1], cc[i]    for i in range(0x20):        cc[i], cc[0x40+i] = cc[0x40+i], cc[i]     for i in range(len(cc)):        plain[i] ^= cc[i] ^ rr[i]     for i in range(0x20):        plain[i], plain[0x40+i] = plain[0x40+i], plain[i]    for i in range(0x40):        plain[i], plain[0x80-i-1] = plain[0x80-i-1], plain[i]     for i in range(len(plain)):        plain[i] = tbl.index(plain[i])     return bytes(plain) def dec3(cipher: bytes) -> bytes:    plain = list(cipher)    tbl = bytes.fromhex("c4189868c047179a23f21a2f75ffd837eb1c7d1861cdf1b74914c18590e1616d7bd4f82307d23443325c3c1aeddff337e4ae04e36d2506bd4ea1f351bd3f8ca1974465b774ac094bdb56552fe61bfc579daa9b47710921084a979b12601de096438c39b57b9bfe9e0e196ceb9a9632a14c5c7e3291bd9017d0db023a8f002eb2")     for i in range(len(plain)):        plain[i] = (plain[i] - 0x80) & 0xFF     for i in range(0x40):        plain[i], plain[0x80-i-1] = plain[0x80-i-1], plain[i]     ret = [0] * 128    for i in range(len(tbl)):        ret[(0x75 + i) % 128] = plain[i] ^ tbl[i]     for i in range(0, len(ret), 2):        ret[i], ret[i+1] = ret[i+1], ret[i]     return bytes(ret) def dec4(cipher: bytes) -> bytes:    plain = list(cipher)     pp1 = bytes.fromhex("a53f0c50366abda69abc3ecf6f580d6be2286767b4b24360f7cdf44e9c88f5931131d090a4b71fd9e1e0957144ed5f7dbc2ceceece0a5291bf6db45b822f9587c5954189bf76d9d7bf713f0aa36ceeb414f393794788f7754208923f11de80144e78c441def8bb74decb3a47247b014001990f031fb08b788c3512a3361749d3")    cc1 = bytes.fromhex("a642115b4c77f1b5fdba36cb428a409f17f2309fdea03474e6d0f7519f8bf8961434d393a7ba22dce4e3987447f05876b525e5e7c7034b8ab866ad547b289082c0903c84ba71d4d2ba6c3a059e67f5bb1bfa9a804e8ffe7c490f994618e587144e78c441def8bb74decb3a472483094809a1170b27b89380943d1aab3e0c3ec8")    dis = []    for i in range(len(pp1)):        dis.append((cc1[i] - pp1[i]) & 0xFF)     for i in range(len(plain)):        plain[i] -= dis[i]        plain[i] &= 0xFF     return bytes(plain) def dec5(cipher: bytes) -> bytes:    plain = list(cipher)    tbl = bytes.fromhex("d5a04a5de8a503a8dd2d980a4d8f901234023d5545b73f2cbabc07c70186df532961253e09217b85b60895300bb09e6bdbf9af51844950d210ebac2b48c56a74cdfd1d314bd6f3b381367e9c582f4c6c0513635a419b387c6519d047c4927a6fefb15783271a71d8fa2ae142a28ceaff166400561e374328ab06c9c3d3a614aeb5e6969a9d5f2615f223a7a34e8772ec208059a440f70ce7bb33ede2ee0f8eb20e3c916824754660a17932b8738b76d11fda8d22f00dc011c2e0665ed79ffe88dc7d8aca4404d417c61b94f654694f6de5be3b6ecbf18278f4def5bd2ece67c835ad7752c17fb99339f8e418bfd9cfb489cc5c1cfcaa3a7062fb5be3a99997e9")     pp = bytes.fromhex("9620b6c3418b71c2a79c5d17937f210d436589b31ebb52c12e256660a46e84450c62a3b21370e5b91e840e67b2db5c2cb2cda71df65066990dcc285847021d46dc20489d6664cb3b66a2aca58ed8aa5ec75bedb54cec97d3361233f3ba7f15454cab44fd67625e1e67f6acb3099a2d812d3c2c0a85c2a4b540c507b86a4d6ac6")    cc = bytes.fromhex("1e76d27f22bf0ba060c447584fa9b895954c4a32bdc046d036cd250181466efc2f002f48486ec74947015bcc5249878f274d2cfb1ab6a9b9a362ca9cff8f586ac125846ea7c2acf7c225d7662ca5b39e5387ca66b8687d953a4d09b09436291f92e9a38ace7ce6cdd1ba7464e488dc9118143ec808ec822e4438538100d068b3")    for i in range(len(cipher)):        plain[i] ^= pp[i] ^ cc[i]     for i in range(0x80):        plain[i] = tbl.index(plain[i])     ret = []    for i in range(0x30, -1, -0x10):        for j in range(0xF, -1, -1):            ret.append(plain[i+j])    ret += plain[0x40:]    return bytes(ret) def dec6(plain: bytes) -> bytes:    p = bytes.fromhex("cf00fa1ac6497c69840b0c74e6d8c2ad50e23957b7b581e678aac75be30e61d895f5d38858be754f4dcf567f9f1cf0cc89fed6aee83cf43e20c8c8fadbc6f2cbd16eca9559b795199d171848be19fa92a30f49b7dc3ec2f54599d28e29a38546bc459e3b969792d0fb06532ac838a4c8fd72055c12d398bbd1f5ad497a5a831b")    c = bytes.fromhex("5a125abad541706a858bf87f4354c5ab409259b7e73511f6589a87ab23de41f885c5d2855fbf7a4d44df76ef7fec30fc005fe6ee885cb41e60e8880a1b16d2ebe11efa65eabc97298c061940be6770a8132f0907acaedbc505dbf2e563b7ad50eec6ee66e6f7b240eb0603bad868f498ed52956c62f3a8cbe1863b497658ae6b")    tbl = [p[i] ^ c[i] for i in range(len(p))]     return bytes([tbl[i] ^ plain[i] for i in range(len(plain))]) def dec7(cipher: bytes) -> bytes:    tbl1 = bytes.fromhex("588d51e8d4d9a5c53094c6bab325c238a4c8aaf74e2e603b3ab2a975a12316fb64e42478322631434bbcb8ac639b7e871b42b59e612a866e4cd2355ad6e9b91db77fa8537bd7de799a745f0bf001ade24a3d98651e6cd8dbabe5442221cb507615565c2972896d418c8e19e1919df2063f1a7d978a8392dc77f936cf48a0c78214e3963984b188284611afa352fe10f457f1e0ca547ca27049a72db6d090da7a67c3bd1cf6f52b9308691359624feaced3eceb5ed10e04204d0f8bbbc9bf03059ccc40dfef853e3cf3ffd50a272fee8f6a1ffd955d817355dde75b6f18c4ae450d689fc012edcd0c33f8fc2c003747bee634a666b0b49971806b07fac1170209")    tbl2 = bytes.fromhex("35b3a22a08d483add97fb6c277933814aa1341488d79eca0f6529df8a64d30fa6124b557658ef9f0d1c3c41acd252f676aa44c17686682c7cc6343e987dd8cb2ea3f7cfc6d115db8b1bd562cc186b044056236a8399427ae7b80df73e5e233e3d3cba7859b9991d0006921e66e9806372b8bf42e95a3cf0c7119530410c96b3aee1b88e12397d6f2d54fe4be4270ffe8de5c7db9f126a9843bced2bcc6d83d501c81db2d6045493e587692c512fb9c54ba070290ef477e75af18bb51f5209a9631349f599e46bfa1ca89014a5a1f1e5bf78fb7d7fe5ee01528e7dcda725f55030d64744b29c04e78ab40226f1d09f3a516c8fd6cac8a0fed0a3c327a0b0eebb4")    tbl3 = bytes.fromhex("7f9458886cfd2dafe88538c7287bebeeb925894fd2053a0cde8ffccb65ad45c959e12b2f4372c381db8eab010677075e74bc03628c543d3718e279840a307e2e83f95746d15b1a1f116820a76495710b5627a350bbc102e035b582d333ca4a0eef26f413b786acc83ed8759d233b934ca15ab6a0048b8a551922be61e69690c099c69ed7603241f087e9aa175f7c4b1b0d40ffb34269e410faa9d62cf5edbad9480f4ef378d014f7a66eaea870669a52801d34366b5da4ecdf0891ce9c4992a221cc2a6df8bd514d6ae55ce767b0f2f12412989b29b4b197fbc453cdda44e3dd1e6f31dc3cc576ead5f673c28d3f1ccf477aa50039fe7db215d4b863bf9f1609")    tbl4 = bytes.fromhex("4f8a646ddee94796105c220be55999d3319533ff1821ab3d2d78291e7758844db99ff851357d2b666ec2b15e2cdc5fd742ae98371fe2439cd94bcadfec9761398334676345366270243f877e467ca203d47394a41da6bb1add8cebd6d8be93feb419a832002a0a11c94ced28f3fc8ea0a1f675c0ce0754fbb512ea914e9dc7746014ac8f385ac152501c539bf1c4e36a4a41b2e8e19071b3cc0c57f9d2ada523bd7615effdbcf5af85dae068f4c8fa69d5256b273b891655db9ee448b69aee80f7170406b0921bcfbf6f86727bf05db713ba050f01e6aa2fc35b7a026ccda32ea9c53c568882e78d3e8120cb0d79f244653a260830a7b84940d07fc6d18b0e09")    tbl5 = b'x1dxbcs,xdbx81xe3x04x06Ux8dFxfcQx83x00Gx14v-Cxf8xc6x10lx11x1cxc0x96Vx99xbdxd7xebxa8txc4=x0cixeaxfd.jxbfx947!x0fo(x1fxa7x1bg6x13xd9xffxe8d0?xf4q+x8c\p'm@2xa0z#xcctx871xc1DNx80&cx07x12Wx91<`h|wxb7x84xaex9cxc9xc2xd0xc3x97xf1B%y$xc8x02xca/xe4xf0Orx92x0bxfbxa3nxe7x1eTxc7xa4Jxe0xd5x86xb1xf7x8bYxd2x89xf5x08En:x93xd4xd6}xf9{x85x9dxfexd3"L;~xbexabxb0xb9xa9x88xa6xdaxbbxf6x1ax05xdex18>x8exb4xfaxbaxb3xb8xb6kx0eK5xcbxecxdcx17x8f xf2*ebx82xf3xa2xcf^xb5xe6xcex9ffxe9PRxef[x9exddx8axe2Iauxed8xd8xd1xe1)x15x98ZSx01xa5Mxcdxc5x90]r9x7f4xdf_x16x03xeexa1xadx9bxafxb23Xxe5x19HAxaax95xacx9ax'    tbl6 = b'Vxd6xxdcx8fsxcfx8ex0bx14xaaxfa?=xc0xf1)xdex8b:txb1nx8cHxc5xc4Rxdd.xd76yDx7fxe1xc1x99xd4x9axd8hxa52WCx17xa2xdfx87x82xf8xe6xe7xb5x004xb2xc9rXxaeKxadxc3xc7x88x91nx051dz>Mbx06mxa4xc6xe3Bjixa3xccxb0x92axbfx8a!Qx07xa7-&Ax10x08x19xe2x1ckrxcaxb4x18x94UxdaJxf3Y^"x0cExbexfbG@}xd0]*xf2x0eO/xcdx90x03x97xff<x968x15x80xecx0fxc2x1bxb3xebx9b~Ixaf{gx9d$x86tfxf7\;xd3xfdx04ox1fxd9xb8xa8evx8dx95xbbxb9lxb7xeex01xf4 `x1auxf65x89%xbdx11x83,xbcFxd5xcbxab(w'xd2xc8xe0xb6Nxd1x98xe9xcepxe4P+xe8xa1#xa6xfexedSx9cxa9x127Z[x81xbaLTxf9x9exdb3xeax939x1dx02qcx16x85x1ex9f|xacxf0xf5x84xefxa00xe5x13_xfc'    tbl7 = b' ;xecxbexb6x87x85x9fSxefWxe0xc3x01xf8:xe7AKr)x0blx04yxfcx18xb4gxcexf0x0eZ4x9exf6x94xacXoxf3xc42x8aUx9cx07xb0exe3x9bRQxc7xddcxa9x12x0fx95x80Yx1axcf{0xa8xab1/ax16x1cMi"pxcbu<xd3tx91xf1Oxa5xe2xfdxbdx11m6+xaaxd5x90xe4x8dxdaxb8xa2xafx1dx98x1fx88*Ix82xx99xebGfxca?xa6J&xdfDx7fx13xa4xb1x02zxc9x9dx00xd6^xbaCxc2x03xff(5xbcdqxdcxa3[xd4xeex83xf7F-k}xaebxd9xa7,xc8rxa1x8ejxe8`nxb2@%hxf4xb7xb3wxc18xc0xd8xd1xfexb9'xbfE]=x19xf5xcc9xdex81x9a3xc5sx15\Px89Nxa0!$xdbxbbVx17_xadxf2x8cx8fx96x97xe5Tx86tx1bBx10Hxb5xf9~|x05xd7>x067xe6x93xfaxe9vnx1exedx0c.xcdxfbx08xeax84x92xd0xc6Lxe1#xd2x14x8b'     plain = list(cipher)    tbls = [tbl1, tbl2, tbl3, tbl4, tbl5, tbl6, tbl7]    for tbl in tbls[::-1]:        for i in range(len(plain)):            plain[i] = tbl.index(plain[i])    return bytes(plain)  target = bytes([121, 23, 66, 107, 59, 80, 122, 227, 70, 208, 222, 78, 36, 167, 138, 106, 105, 208, 2, 6, 240, 39, 36, 189, 192, 187, 227, 30, 9, 163, 151, 48, 60, 182, 235, 104, 144, 9, 208, 234, 17, 242, 196, 96, 165, 203, 195, 252, 69, 251, 92, 83, 192, 128, 58, 153, 89, 111, 47, 84, 74, 217, 14, 106, 52, 222, 210, 236, 175, 74, 11, 164, 138, 182, 250, 147, 31, 4, 16, 68, 238, 228, 214, 158, 244, 69, 18, 77, 55, 60, 240, 17, 248, 200, 68, 118, 99, 16, 115, 45, 104, 215, 157, 47, 195, 132, 42, 182, 204, 181, 55, 97, 79, 15, 22, 188, 187, 140, 83, 39, 238, 119, 170, 31, 156, 194, 24, 222]) decs = [dec1, dec2, dec3, dec4, dec5, dec6, dec7]for func in decs[::-1]:    target = func(target)print(target.decode())

得到一串神奇 flag:
:*D#O+_I3;`}0NfP-=2/+Y"_>A8S6]L|4G;UHiA5mnol^k;#OhW2!UEJf0"7?Dt5m{CqE*AZr~$1(zW@TBXKL&2r?+3kwxC90O'%&PyVo~)'Q%Z@6b}REKF[cgFe/-?I



看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
第九题《乡绅压迫》正在进行

https://ctf.pediy.com/game-season_fight-224.htm


看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

欢迎参赛或围观看雪2022 KCTF 秋季赛 | 第八题设计思路及解析


看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
– End –


看雪2022 KCTF 秋季赛 | 第八题设计思路及解析


看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

球分享

看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

球点赞

看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

球在看



看雪2022 KCTF 秋季赛 | 第八题设计思路及解析
“阅读原文查看详情!

原文始发于微信公众号(看雪学苑):看雪2022 KCTF 秋季赛 | 第八题设计思路及解析

版权声明:admin 发表于 2022年12月5日 下午6:02。
转载请注明:看雪2022 KCTF 秋季赛 | 第八题设计思路及解析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...