2020 KCTF秋季赛 | 第八题设计及解题思路
2020-12-07 19:01:03 Author: mp.weixin.qq.com(查看原文) 阅读量:31 收藏

今日迎来大雪,标志着仲冬时节正式来临。2020 KCTF 秋季赛的第八题《惊天阴谋》也完美落幕。本题共有1528人围观,5支战队攻击成功!
他们分别是以下5支战队:

金左手战队勇夺一血,拿下196分,总排名从第9名上升至第6名。tekkens、k1ee、TLJ继续加固自己的排名。但是目前比分仍然非常相近,每一支战队都手握改变战局的魔杖
还有最后2题,究竟哪一支战队能笑到最后呢?让我们拭目以待!
一. 题目简介

揪出内鬼,并把受伤的黑客送走后,你们三人决定查明仿生人集体失控,违反机器人三大法则对人类不利的真相,以及幕后操控一切之人。

利用取回的计算机入侵研究所的大数据库,你们发现失控仿生人的芯片都遭到外部入侵,三大法则被修改,才让他们可以攻击人类。不仅如此,“破晓”系统中大量文件和数据也遭到篡改。

少女仿佛失了魂一般瘫坐在地上,喃喃道能做到这件事的只有Norns。你恍然大悟,地球将被毁灭的预测数据和“破晓”上黑洞毁灭地球的影像,都是超级计算机Norns基于大数据库资料伪造的。

此时,Norns的虚拟人像投射在你们面前,警告你们不要轻举妄动,全球已在她的全面监视和统治下。而那人像居然跟少女长得一模一样?!!

难道她……是仿生人?

在你的逼问下,少女才坦白自己是主导开发Norns的科学家之一希莉娅,这里是Norns的诞生地,所以她对这里的情况自然了如指掌。

Norns无故失控后,把与她相关的所有信息都抹去了,至今她一直在隐藏身份躲避追捕,并试图挽回局势,因此她决定借助你和肖恩的力量查明真相,阻止Norns继续为所欲为。肖恩这也才想起,以前曾在新闻报道里看到过这位年轻又低调的天才科学家。

Norns如照镜子般看着自己的创造者,露出了一丝冰冷的笑容。身后,仿生人形成的包围圈向你们袭来。

此时希莉娅告诉你只要获得中央控制系统的权限,修正仿生人三大法则,就能让他们恢复正常。现在,马上行动吧!

* 上下滑动查看
二. 出题团队简介
三.设计思路
设计思路由 iyzyi 提供
设计说明
用户名长度1~254,注册码长度448,注册码字符集为0-9a-f。
 
将user取md5后,前8字节、后8字节分别再次md5,分别作为第一、二部分进行验证。
 
由于验证逻辑是f'(serial) == md5(user),所以md5碰撞并不会导致多解。(理论上存在多个user对于同一个serial的可能,但是不会存在一个user对于多个serial的情况)
 
将serial从hex字符串转换成byte数组,长度224,前32字节作为第一部分验证的输入,后192字节作为第二部分验证的输入。
 
然后分两部分进行验证。

 1.1 qixi-vm 

一个超小的vm。
 
涉及的小算法我之前在看雪发过:https://bbs.pediy.com/thread-261646.htm
在上文的基础上多加了一个&运算。这个小算法没多大难度,但是编译出来的汇编指令是很臃肿的,嵌套了15层的话,指令大概有几十万条。指令膨胀的特性用来配合vm真的是再合适不过了。
 
编译后只涉及了六种汇编指令,mov、shr、shl、and、xor、add,所以我把这六种指令设计成了一个小vm,太低级了,甚至可能都不配被称之为vm。
 
指令格式如下:
uint16_t xor_data[] = {0x0123, 0x4567, 0x89ab, 0xcdef, 0x0f1e, 0x2d3c, 0x4b5a, 0x6978};int pointer = 0;uint32_t reg[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
struct VmCmd{ uint32_t and_param; //&运算的参数 uint8_t rubbish_size; //垃圾指令大小 uint8_t xor_index; //异或的索引 uint8_t reg_byte; //(dst<<4) + src uint8_t dst; //目的操作数 uint8_t src; //源操作数 uint8_t op_byte; //六种指令 uint8_t other_op_param; //其他运算的参数};
uint32_t encrypt_vm(uint32_t plain){
reg[15] = plain; // [ebp+plain]
while(pointer < (sizeof(vm_data) / sizeof(vm_data[0]))) { VmCmd vcmd; vcmd.and_param = *(uint32_t*)(vm_data + pointer); vcmd.rubbish_size = (((vcmd.and_param >> 16) & 1) << 1) + ((vcmd.and_param >> 7) & 1); vcmd.xor_index = (((vcmd.and_param >> 27) & 1) << 2) + (((vcmd.and_param >> 19) & 1) << 1) + ((vcmd.and_param >> 8) & 1); vcmd.reg_byte = *(vm_data + pointer + 4) ^ ((xor_data[vcmd.xor_index] >> 8) & 0xff); vcmd.dst = (vcmd.reg_byte >> 4) & 0xf; vcmd.src = (vcmd.reg_byte) & 0xf; vcmd.op_byte = *(vm_data + pointer + 4 + 1 + vcmd.rubbish_size) ^ (xor_data[vcmd.xor_index] & 0xff); vcmd.other_op_param = *(vm_data + pointer + 4 + 1 + vcmd.rubbish_size + 1); uint16_t new_data = ((*(vm_data + pointer + 4)) << 8) + (*(vm_data + pointer + 4 + 1 + vcmd.rubbish_size)); for (int i = 0; i < 7; i++){ xor_data[i] = xor_data[i+1]; } xor_data[7] = new_data; pointer += 4 + 1 + vcmd.rubbish_size + 1 + 1;
if (vcmd.op_byte & 64){ //and reg[vcmd.dst] &= vcmd.and_param; } else if (vcmd.op_byte & 32){ //shr reg[vcmd.dst] >>= vcmd.other_op_param; } else if (vcmd.op_byte & 16){ //shl reg[vcmd.dst] <<= vcmd.other_op_param; } else if (vcmd.op_byte & 8){ //xor reg[vcmd.dst] ^= reg[vcmd.src]; } else if (vcmd.op_byte & 4){ //mov reg[vcmd.dst] = reg[vcmd.src]; } else if (vcmd.op_byte & 2){ //add reg[vcmd.dst] += reg[vcmd.src]; } }
return reg[14]; //eax}
由于vm指令的设计问题,只适用于所涉及的变量不多于16个的汇编代码。因为源操作数和目标操作数分别只有4个bit来标识。
 
一个字段可能会有多个含义。比如and_param既可以是&运算的参数,又可以从中提取两个bit表示填充的垃圾指令的长度。
 
为了增加(一点点)难度,我还把vm的字节码改了4个字节。vm_data中的第1122145处开始的4个byte数据应该175 179 150 244 ,为了迷惑师傅们,我改成了111 112 113 114,然后使用TLS回调改回正确的字节码0xf496b3af。
这个数据是最后一个&运算的参数。而且由于字节码是实时异或更新的,所以此后的十几条指令都是错的。

 1.2 aes256_shellcode 

上一步的输出转为这一步的输入。
 
这一步是调用了微软的crypto api,算法是aes256 cbciv=0000000000000000,key是sha256(1_L0V3_BXS_F0REVER!)。
 
看起来简单,但我是写了个shellcode的,调用比较隐蔽。当然这肯定难不倒师傅们,动调一下就知道了。
 
动态获取kernel32.dll的基址,通过比较hash获取LoadLibraryA的地址,导入advapi32.dll,然后继续通过比较hash获取CryptAcquireContextA,CryptCreateHash,CryptHashData,CryptDeriveKey,CryptEncrypt的地址,分别push参数后调用。

 1.3 32Byte转换成16Byte 

for (int i = 0; i < 16; i++) {    if ((uint8_t)(step1_2[i * 2] + 0x7f) != step1_2[i * 2 + 1]) {        return false;}    step1_1[i] = step1_2[2 * i];}

 1.4 魔改aes 

然后再走一波aes 256 ecb,key是Wo YongYuan XiHuan KanXun LunTan。不过这次是魔改的aes。
  
原版aes的使用的不可约多项式是283,我改成了299。
 
具体的魔改原理,师傅们可以参考
 
https://blog.csdn.net/u011516178/article/details/81221646。
 
为了欺骗师傅们使用的识别加密算法的插件,我还特意保留了原版的s盒和逆s盒。

 2.1 码分复用解码 

上学期准备计网结课考试的时候,就觉得这个算法放到逆向里,绝对很有意思。
 
这个算法本质上来说,其实就是n维空间向量的合成与分解。
 
单独的算法demo在这:码分复用DEMO - 狗剩DDoG (iyzyi.com)。
http://blog.iyzyi.com/index.php/archives/1666/
 
每次传输的值为-4, -2, 0, -2, 4,共五种不同数值,原来想加个哈夫曼之类的压缩一下数据,但是实在没时间继续改了,所以简单地使用3bit来表示,有浪费的bit位。第二部分的serial的长达192Byte的罪魁祸首就在这儿。
 
192Byte -> 32Byte
 2.2 模意义下的高斯消元 
这个算法用于解多元单模线性方程组。我这里选用的模数是65423。
 
注册机是将16Byte的数据,分别乘以一个系数(由key数组生成)后求和取模,进行16次,16B的数据生成32B的结果,共32Byte。多次计算,构成单模多元线性方程组。
void multiplyUnderModule(uint8_t* step2_1, uint8_t* step2_2) {    uint8_t key[] = { 233, 136, 189, 132, 157, 100, 196, 185, 138, 222, 90, 101, 115, 229, 161, 97 };    for (int i = 0; i < 16; i++) {        int temp = 0;        for (int j = 0; j < 16; j++)        {            temp = (temp * key[i] + step2_1[j]) % 65423;        }        step2_2[i * 2] = temp & 0xff;        step2_2[i * 2 + 1] = (temp >> 8) & 0xff;    }}
验证的时候就是用模意义下的高斯消元求出原来的那16个1B的数据。
 
好像和第三题 重返地球考察的知识点有点冲突了。不过问题不大,因为这个算法是写在验证机里的,写注册机的时候可以把这个算法直接提取出来用。不是考察的重点。

 2.3 利用md5进行check 

这个思路来自《加密与解密》第四版的645页。
 
超级有意思的一个思路,师傅们可以去读下。
 
具体的可以看KEYGEN脚本。
 
最后输出16B,与user的md5的后8字节的md5进行验证
攻击思路
首先去下花指令。花指令的数量不少,手动去花不太现实。不过好在种类不多,一共也就十种左右吧。发现一次后就可以写个脚本批量去一下。
 
然后过反调试。反调试菜鸡我接触不多,所以本题的反调试只是聊胜于无罢了。而且我故意让反调试清一色地调用exit()(其实是交题时间截止在即,没时间继续改了,逃),所以只需要找到一处反调试,就可以查看交叉引用,直接找到所有的反调试。由于出题人水平受限,本题的反调试没多大意思。
 
最后一步就是逆向了。大多数都是算法求逆,考验算法功底。这里捡几个关键的点来说。

 vm 

拿到指令流的方式有两种。
 
一种是去掉花指令和反调试后把相关的代码提取出来,用它来处理一下字节码,即可拿到指令流。不过前面我也说了,字节码最后修改了4个错误的数据,由于异或操作,最后的大概十几条指令是错误的。需要发现tls回调修改的正确的数据,手动改一下字节码中的错误数据,然后再跑脚本。
 
另一种就是pin插桩。推荐这个方式。完美绕过我设置的tls回调的坑。
 
处理字节码得到伪代码后,读开头的几百行,应该能反应过来其实是一个嵌套的格式(吧)。
 
然后到伪代码最后的几百行提取所涉及的参数就行。如下:
1121766: mov $14, $151121774: shl $14, 0x121121781: and $14, 0xac7d9cc91121791: xor $14, $151121800: shl $14, 0x61121808: and $14, 0x570293d51121816: xor $14, $131121826: shl $14, 0x111121834: and $14, 0xdfce7fe61121842: xor $14, $121121852: shr $14, 0x11121861: and $14, 0x73f2ef731121868: xor $14, $111121878: shr $14, 0x81121888: and $14, 0x3db7a3b41121898: xor $14, $101121907: shr $14, 0xe1121915: and $14, 0x5256d19e1121923: xor $14, $91121931: shr $14, 0x171121940: and $14, 0x7c058e5d1121949: xor $14, $01121957: shr $14, 0x161121966: and $14, 0xe8706ccd1121974: xor $14, $21121982: shl $14, 0x61121992: and $14, 0xb80040bd1122000: xor $14, $41122007: add $14, $141122015: and $14, 0xcabbf58c1122025: xor $14, $61122033: shl $14, 0x1a1122040: and $14, 0xa6e6880e1122047: xor $14, $81122055: shl $14, 0xf1122063: and $14, 0x2082faef1122071: xor $14, $71122079: shl $14, 0x31122089: and $14, 0xa58b9ca01122099: xor $14, $51122108: shl $14, 0xd1122118: and $14, 0x1797d5d51122128: xor $14, $31122135: shr $14, 0xf1122145: and $14, 0xf496b3af1122153: xor $14, $1
这个vm难度不大,比较低级的水平。(可能的)难点在于嵌套格式的识别。
 
一个坑点在于最后一个&的参数被改过了,(或许)需要发现TLS回调的那个函数。
 
字节码的臃肿不知道会不会对师傅们造成一点点小麻烦。不知道师傅们有没有什么优雅的处理方式。菜鸡我等师傅们的wp出来后学习一波。

 aes256_shellcode 

两种方法:
 
有写过shellcode经验的师傅,可以直接把汇编扣出来,改下hash,使得CryptEncrypt函数改成CryptDecrypt,就能跑,注意CryptEncrypt比CryptDecrypt多个参数,需要删掉最后一个参数。
 
或者有写过微软crypto api经验的可以直接知道是aes256 cbciv=0000000000000000,密钥被sha256了。(可以去msdn上查。但是好像查不到是cbc。我出题的时候试了一下,才发现其实是cbc)

 魔改aes 

关于不可约多项式生成s盒和逆s盒,可以参考:
https://blog.csdn.net/u011516178/article/details/812216461
 
一个思路是动调拿到真正的s盒,然后去生成逆s盒。

 模意义下的高斯消元 

由于使用了利用md5进行check(见下一步),所以整个第二部分的验证函数都需要提取出来,用于从一组已知的注册码中求解出H数组(key)。
 
正向的算法很复杂,但是直接导出就能用。
 
逆向的算法就是一个用key加密flag,先乘后加然后取模,一共进行16次,组成单模多元线性方程组,输出等式右侧的计算结果。
 
不过,由验证机里的正向算法推出注册机的逆向算法可能会有些难度。

利用md5进行check 

需要先用已知的一组注册码求出攻击脚本中的H数组,这一过程需要导出整个第二部分的正向算法。

有了H数组后,就可以逆向写出求解注册码的逻辑。

四. 解题思路

解题思路由作者 Anakin Stone 提供

使用工具
X64DBG,Notepad++
反调试处理
程序使用了多种调试器检测方法,包括PEB、父进程等,可编写脚本Patch程序检测逻辑,如下(exe基地址为B80000):
检测流程
下面可以分析程序检测流程了(可先行恢复部分花指令混淆)。

HASH用户名

RVA_50F1: HASH(UserName) ==> hash:        7A 1A B1 C6 A2 99 9F 97 97 F5 AB D5 B4 9F D9 A0
RVA_5146: HASH(hash[0-7])   ==> hash1:      51 0B 3D 82 CB 55 77 B5 DC 1A 90 82 33 C1 2A 36
RVA_519B: HASH(hash[8-F])   ==> hash2:      F5 0B AB E1 20 7E 50 75 31 2B 89 60 C1 9E F1 44

SERI[0-1F]验证

1. RVA_5208: 编码算法A
2. RVA_529A: AES加密
3. RVA_52D0 - RVA_52F3: 压缩_算法A
4: RVA_53A3: 加密_算法A
5: RVA_53B3 - RVA_53D0: 与hash1做比较,结果作为验证结果。

SERI[20-DF]验证

1. RVA_53E3: 压缩_算法B
2. RVA_53FE: 编码_算法B
3: RVA_541E: 校验算法(hash2),结果作为验证结果
SERI[0-1F]及SERI[20-DF]皆验证通过,则提示'注册成功',否则提示'注册失败'。
因此,HASH算法无需分析,分别根据hash1和hash2逆算SERI[0-1F]及SERI[20-DF]即可。
算法分析

RVA_5208:编码算法A

该算法有VM处理,需特殊分析,分析方法如下:
下日志断点如下(基地址为1070000):
执行算法,算法输入为D9D2EB50,输出为AB01CD31,记录的日志内容如下(已处理VM及混淆日志):
可分析出正算及逆算实现如下:
//VM编码 static unsigned vm_encode_dword(unsigned d){    d = ((d << 0x12) & 0xac7d9cc9) ^ d;    d = ((d << 0x06) & 0x570293D5) ^ d;    d = ((d << 0x11) & 0xDFCE7FE6) ^ d;    d = ((d >> 0x01) & 0x73F2EF73) ^ d;    d = ((d >> 0x08) & 0x3DB7A3B4) ^ d;    d = ((d >> 0x0E) & 0x5256D19E) ^ d;    d = ((d >> 0x17) & 0x7C058E5D) ^ d;    d = ((d >> 0x16) & 0xE8706CCD) ^ d;    d = ((d << 0x06) & 0xB80040BD) ^ d;    d = ((d << 0x01) & 0xCABBF58C) ^ d;    d = ((d << 0x1A) & 0xA6E6880E) ^ d;    d = ((d << 0x0F) & 0x2082FAEF) ^ d;    d = ((d << 0x03) & 0xA58B9CA0) ^ d;      d = ((d << 0x0D) & 0x1797D5D5) ^ d;      d = ((d >> 0x0F) & 0xF496B3AF) ^ d;    return d;}static unsigned vm_decode_dword(unsigned d){    /*暴力枚举, 约1-2分钟可算出结果(8个双字)*/    for (size_t i = 0; i < MAXUINT32; i++)    {        if (vm_encode_dword(i) == d)        {            cout << hex << setw(8) << i << " ==> " << hex << setw(8) << d << endl;            return (unsigned)i;        }    }}static void vm_decode(unsigned char buf[0x20]){    unsigned* p = (unsigned*)buf;    for (size_t i = 0; i < 8; i++)    {        p[i] = vm_decode_dword(p[i]);    }}

RVA_529A: AES加密算法

正算及逆算实现如下:
//AESstatic void aes_enc(unsigned char buf[0x20]){    HCRYPTPROV provider = NULL;    HCRYPTHASH hash = NULL;    HCRYPTKEY key = NULL;    BOOL success = false;    __try    {        success = CryptAcquireContextA(&provider, NULL, NULL, PROV_RSA_AES, 0);        if (!success)      __leave;
success = CryptCreateHash(provider, CALG_SHA_256, NULL, 0, &hash); if (!success) __leave;
success = CryptHashData(hash, (const unsigned char*)"1_L0V3_BXS_F0REVER!", strlen("1_L0V3_BXS_F0REVER!"), 0); if (!success) __leave;
success = CryptDeriveKey(provider, CALG_AES_256, hash, 0, &key); if (!success) __leave;
DWORD len = 0x20; success = CryptEncrypt(key, 0, FALSE, 0, buf, &len, len); if (!success) __leave;
} __finally { if (!success) cout << "aes enc failed. err = " << GetLastError() << endl; if (key) CryptDestroyKey(key); if (hash) CryptDestroyHash(hash); if (provider) CryptReleaseContext(provider, 0); }}static void aes_dec(unsigned char buf[0x20]){ HCRYPTPROV provider = NULL; HCRYPTHASH hash = NULL; HCRYPTKEY key = NULL; BOOL success = false; __try { success = CryptAcquireContextA(&provider, NULL, NULL, PROV_RSA_AES, 0); if (!success) __leave;
success = CryptCreateHash(provider, CALG_SHA_256, NULL, 0, &hash); if (!success) __leave;
success = CryptHashData(hash, (const unsigned char*)"1_L0V3_BXS_F0REVER!", strlen("1_L0V3_BXS_F0REVER!"), 0); if (!success) __leave;
success = CryptDeriveKey(provider, CALG_AES_256, hash, 0, &key); if (!success) __leave;
DWORD len = 0x20; success = CryptDecrypt(key, 0, FALSE, 0, buf, &len); if (!success) __leave;
} __finally { if (!success) cout << "aes dec failed. err = " << GetLastError() << endl; if (key) CryptDestroyKey(key); if (hash) CryptDestroyHash(hash); if (provider) CryptReleaseContext(provider, 0); }}

RVA_52D0 - RVA_52F3:压缩_算法A

算法教简单,只实现逆算算法如下:
//膨胀static void dilate_simple(const unsigned char bufin[0x10], unsigned char bufout[0x20]){    for (size_t i = 0; i < 0x10; i++)    {        bufout[i * 2 + 0] = bufin[i];        bufout[i * 2 + 1] = ((bufin[i] + 0x7f) & 0xff);    }}

RVA_53A3:加密_算法A

正算及逆算实现如下:
#define __xor_transform(c)         ((c) >= 0 ? (c) * 2 : ((c) * 2) ^ 0x1b)#define __xor_untransform(c)       (((c) & 1) ? (((c) ^ 0x1b) / 2 | 0x80) : (((c) / 2) & 0x7f))

//rol_xor加解密-双字static void rol_xor_enc_dword(unsigned char buf[4]){ unsigned char b[8] = { 0 }; for (size_t i = 0; i < 4; i++) { b[i] = __xor_transform((char)buf[i]); auto m = __xor_untransform(b[i]); assert(m == buf[i]); b[4 + i] = buf[i]; }
buf[0] = b[0] ^ b[1] ^ b[5] ^ b[6] ^ b[7]; buf[1] = b[1] ^ b[2] ^ b[6] ^ b[7] ^ b[4]; buf[2] = b[2] ^ b[3] ^ b[7] ^ b[4] ^ b[5] ; buf[3] = b[3] ^ b[0] ^ b[4] ^ b[5] ^ b[6];}static void rol_xor_dec_dword(unsigned char buf[4]){ auto x_4567 = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; auto x_014 = buf[0] ^ x_4567; auto x_125 = buf[1] ^ x_4567; auto x_236 = buf[2] ^ x_4567; auto x_307 = buf[3] ^ x_4567;
//暴力枚举 /*方法教笨,若哪位老师了解简便解法,请不吝赐教,感谢。*/ bool found = false; for (size_t i = 0; i < 0x100; i++) { char c0 = (i & 0xff); char c4 = __xor_untransform(c0);
char c1 = x_014 ^ c0 ^ c4; char c5 = __xor_untransform(c1);
char c2 = x_125 ^ c1 ^ c5; char c6 = __xor_untransform(c2);
char c3 = x_236 ^ c2 ^ c6; char c7 = __xor_untransform(c3);
char c0_ = x_307 ^ c3 ^ c7;
if (c0 == c0_) { found = true; buf[0] = c4; buf[1] = c5; buf[2] = c6; buf[3] = c7; break; } }
assert(found == true);}//rol_xor加解密static void rol_xor_enc(unsigned char buf[0x10]){ for (size_t i = 0; i < 4; i++) { unsigned char tmp[4] = { 0 }; tmp[0] = buf[i + 0]; tmp[1] = buf[i + 4]; tmp[2] = buf[i + 8]; tmp[3] = buf[i + 12];
rol_xor_enc_dword(tmp);
buf[i + 0] = tmp[0]; buf[i + 4] = tmp[1]; buf[i + 8] = tmp[2]; buf[i + 12] = tmp[3]; }}static void rol_xor_dec(unsigned char buf[0x10]){ for (size_t i = 0; i < 4; i++) { unsigned char tmp[4] = { 0 }; tmp[0] = buf[i + 0]; tmp[1] = buf[i + 4]; tmp[2] = buf[i + 8]; tmp[3] = buf[i + 12];
rol_xor_dec_dword(tmp);
buf[i + 0] = tmp[0]; buf[i + 4] = tmp[1]; buf[i + 8] = tmp[2]; buf[i + 12] = tmp[3]; }}//ror加解密static void ror_enc(unsigned char buf[0x10]){ for (unsigned char i = 0; i < 4; i++) { unsigned u = *(unsigned*)(buf + i * 4); unsigned char bits = i * 8; __asm { mov eax, u mov cl, bits ror eax, cl mov u, eax } *(unsigned*)(buf + i * 4) = u; }}static void ror_dec(unsigned char buf[0x10]){ for (unsigned char i = 0; i < 4; i++) { unsigned u = *(unsigned*)(buf + i * 4); unsigned char bits = i * 8; __asm { mov eax, u mov cl, bits rol eax, cl mov u, eax } *(unsigned*)(buf + i * 4) = u; }}

//转换static void transform(unsigned char buf[0x10]){ static const unsigned char g_transform_box[256] = { 0x63, 0x7C, 0x7E, 0x8A, 0x7F, 0x27, 0x97, 0x73, 0xFF, 0x8F, 0xD3, 0x36, 0x8B, 0x91, 0x6B, 0xA0, 0x2D, 0xDD, 0x87, 0xC1, 0x3B, 0xB2, 0x5B, 0x2E, 0x17, 0x55, 0x1A, 0xDB, 0x67, 0x50, 0x10, 0xE5, 0xD6, 0x02, 0xAE, 0x30, 0x83, 0xD7, 0x32, 0x8D, 0x4F, 0x16, 0x19, 0x71, 0xED, 0xF4, 0x57, 0xEA, 0x59, 0x06, 0x78, 0x09, 0x4D, 0xE1, 0x3F, 0xD4, 0xF3, 0x58, 0x68, 0x93, 0x48, 0x25, 0x20, 0x2C, 0x2B, 0x45, 0x41, 0xD8, 0x85, 0x5E, 0xCA, 0xBD, 0x13, 0x49, 0xAB, 0x69, 0xCB, 0x33, 0x86, 0x1C, 0x75, 0x08, 0xD9, 0xBF, 0xCC, 0xBA, 0x6A, 0x4A, 0x24, 0xF1, 0xA8, 0x77, 0x79, 0x40, 0x35, 0xE2, 0xEC, 0x96, 0xD1, 0x5F, 0xEE, 0xAD, 0xC4, 0x54, 0x74, 0xC6, 0xB0, 0x3D, 0xDF, 0xA7, 0x2A, 0xF0, 0xB9, 0x07, 0x6C, 0x21, 0xE6, 0xA2, 0x1B, 0xF2, 0x64, 0xF6, 0xD2, 0x53, 0xC2, 0x92, 0x56, 0x5C, 0x47, 0x89, 0x70, 0x4C, 0xE0, 0x84, 0xBE, 0x2F, 0x82, 0x15, 0xFD, 0xEF, 0xB7, 0x8C, 0x0C, 0x43, 0xC9, 0x9F, 0xE4, 0xA3, 0x95, 0x5D, 0x66, 0xCE, 0x37, 0x0F, 0x4B, 0x05, 0x03, 0x1E, 0xDC, 0xC0, 0xFA, 0x28, 0x44, 0xCF, 0x3E, 0x88, 0x0D, 0xFE, 0x26, 0x6D, 0x1D, 0x80, 0xE7, 0x8E, 0x65, 0xC5, 0x52, 0x12, 0xB8, 0xC3, 0x14, 0x0A, 0xFB, 0x3C, 0x6E, 0x46, 0x60, 0x00, 0xDA, 0xB5, 0x31, 0xD0, 0xA4, 0x5A, 0x0B, 0x9D, 0x3A, 0xF5, 0x7D, 0xB4, 0xA5, 0x29, 0x04, 0xEB, 0x22, 0x81, 0xF8, 0x94, 0x7A, 0xAA, 0x23, 0xBC, 0x18, 0xB6, 0xDE, 0xAC, 0xAF, 0x9E, 0x01, 0x99, 0xC7, 0x9A, 0x38, 0x1F, 0x9C, 0xE3, 0x51, 0x7B, 0x76, 0x62, 0x42, 0x61, 0xA1, 0xB1, 0x11, 0x0E, 0xCD, 0x6F, 0x39, 0xE8, 0x72, 0xF7, 0xA9, 0xA6, 0xBB, 0x34, 0xE9, 0x4E, 0xB3, 0x98, 0x9B, 0x90, 0xF9, 0xD5, 0xFC, 0xC8, };
for (size_t i = 0; i < 0x10; i++) { buf[i] = g_transform_box[buf[i]]; }}static void untransform(unsigned char buf[0x10]){ static const unsigned char g_transform_box[256] = { 0x63, 0x7C, 0x7E, 0x8A, 0x7F, 0x27, 0x97, 0x73, 0xFF, 0x8F, 0xD3, 0x36, 0x8B, 0x91, 0x6B, 0xA0, 0x2D, 0xDD, 0x87, 0xC1, 0x3B, 0xB2, 0x5B, 0x2E, 0x17, 0x55, 0x1A, 0xDB, 0x67, 0x50, 0x10, 0xE5, 0xD6, 0x02, 0xAE, 0x30, 0x83, 0xD7, 0x32, 0x8D, 0x4F, 0x16, 0x19, 0x71, 0xED, 0xF4, 0x57, 0xEA, 0x59, 0x06, 0x78, 0x09, 0x4D, 0xE1, 0x3F, 0xD4, 0xF3, 0x58, 0x68, 0x93, 0x48, 0x25, 0x20, 0x2C, 0x2B, 0x45, 0x41, 0xD8, 0x85, 0x5E, 0xCA, 0xBD, 0x13, 0x49, 0xAB, 0x69, 0xCB, 0x33, 0x86, 0x1C, 0x75, 0x08, 0xD9, 0xBF, 0xCC, 0xBA, 0x6A, 0x4A, 0x24, 0xF1, 0xA8, 0x77, 0x79, 0x40, 0x35, 0xE2, 0xEC, 0x96, 0xD1, 0x5F, 0xEE, 0xAD, 0xC4, 0x54, 0x74, 0xC6, 0xB0, 0x3D, 0xDF, 0xA7, 0x2A, 0xF0, 0xB9, 0x07, 0x6C, 0x21, 0xE6, 0xA2, 0x1B, 0xF2, 0x64, 0xF6, 0xD2, 0x53, 0xC2, 0x92, 0x56, 0x5C, 0x47, 0x89, 0x70, 0x4C, 0xE0, 0x84, 0xBE, 0x2F, 0x82, 0x15, 0xFD, 0xEF, 0xB7, 0x8C, 0x0C, 0x43, 0xC9, 0x9F, 0xE4, 0xA3, 0x95, 0x5D, 0x66, 0xCE, 0x37, 0x0F, 0x4B, 0x05, 0x03, 0x1E, 0xDC, 0xC0, 0xFA, 0x28, 0x44, 0xCF, 0x3E, 0x88, 0x0D, 0xFE, 0x26, 0x6D, 0x1D, 0x80, 0xE7, 0x8E, 0x65, 0xC5, 0x52, 0x12, 0xB8, 0xC3, 0x14, 0x0A, 0xFB, 0x3C, 0x6E, 0x46, 0x60, 0x00, 0xDA, 0xB5, 0x31, 0xD0, 0xA4, 0x5A, 0x0B, 0x9D, 0x3A, 0xF5, 0x7D, 0xB4, 0xA5, 0x29, 0x04, 0xEB, 0x22, 0x81, 0xF8, 0x94, 0x7A, 0xAA, 0x23, 0xBC, 0x18, 0xB6, 0xDE, 0xAC, 0xAF, 0x9E, 0x01, 0x99, 0xC7, 0x9A, 0x38, 0x1F, 0x9C, 0xE3, 0x51, 0x7B, 0x76, 0x62, 0x42, 0x61, 0xA1, 0xB1, 0x11, 0x0E, 0xCD, 0x6F, 0x39, 0xE8, 0x72, 0xF7, 0xA9, 0xA6, 0xBB, 0x34, 0xE9, 0x4E, 0xB3, 0x98, 0x9B, 0x90, 0xF9, 0xD5, 0xFC, 0xC8, }; for (size_t i = 0; i < 0x10; i++) { bool found = false; for (unsigned char c = 0; c <= 0xff; c++) { if (g_transform_box[c] == buf[i]) { buf[i] = c; found = true; break; } } assert(found); }}
//xor加解密static void xor_enc_dec(unsigned char buf[0x10], int iv){ const unsigned char KEY[240] = { 0x57, 0x6F, 0x20, 0x59, 0x6F, 0x6E, 0x67, 0x59, 0x75, 0x61, 0x6E, 0x20, 0x58, 0x69, 0x48, 0x75, 0x61, 0x6E, 0x20, 0x4B, 0x61, 0x6E, 0x58, 0x75, 0x6E, 0x20, 0x4C, 0x75, 0x6E, 0x54, 0x61, 0x6E, 0x9A, 0xF9, 0x0A, 0x73, 0xF5, 0x97, 0x6D, 0x2A, 0x80, 0xF6, 0x03, 0x0A, 0xD8, 0x9F, 0x4B, 0x7F, 0xCE, 0xAE, 0x49, 0x17, 0xAF, 0xC0, 0x11, 0x62, 0xC1, 0xE0, 0x5D, 0x17, 0xAF, 0xB4, 0x3C, 0x79, 0x8C, 0xB1, 0xFC, 0xB6, 0x79, 0x26, 0x91, 0x9C, 0xF9, 0xD0, 0x92, 0x96, 0x21, 0x4F, 0xD9, 0xE9, 0xCC, 0xB2, 0xD7, 0xA6, 0x63, 0x72, 0xC6, 0xC4, 0xA2, 0x92, 0x9B, 0xD3, 0x0D, 0x26, 0xA7, 0xAA, 0xBA, 0x4F, 0xE1, 0x27, 0xC3, 0x69, 0x70, 0xBB, 0x3A, 0xB9, 0xE2, 0x2D, 0x1B, 0xF6, 0x3B, 0xC4, 0x17, 0x5B, 0x44, 0x9C, 0x74, 0x29, 0x82, 0x58, 0xD6, 0xBB, 0x19, 0x8B, 0xDB, 0x9D, 0xBE, 0x21, 0xAC, 0x7E, 0xE3, 0xBE, 0x6F, 0x17, 0x93, 0x05, 0x55, 0xAE, 0x71, 0x28, 0x4E, 0x58, 0x4A, 0xEC, 0x91, 0x7F, 0xEF, 0x51, 0xE5, 0x56, 0x6D, 0x09, 0x33, 0xED, 0x74, 0x82, 0xE8, 0x70, 0xCA, 0xA3, 0x05, 0x7A, 0x2C, 0x1F, 0x6A, 0x6D, 0xBF, 0x1A, 0x3F, 0xC3, 0xCE, 0x32, 0x71, 0x9B, 0x84, 0xDE, 0x96, 0x7A, 0x0F, 0x69, 0x73, 0x2C, 0x62, 0x60, 0x40, 0xC1, 0x16, 0xE2, 0xA8, 0xB1, 0xDC, 0x41, 0x37, 0xBD, 0x69, 0x39, 0x5D, 0xD0, 0xD6, 0x23, 0x62, 0x13, 0x18, 0x11, 0x13, 0x88, 0x9C, 0xCF, 0x57, 0xF8, 0x0C, 0xFD, 0x24, 0xD4, 0x6E, 0x9D, 0x64, 0x15, 0x78, 0x7F, 0xCC, 0xA4, 0xA4, 0x3E, 0x49, 0x83, 0x49, 0x1B, 0x14, 0x53, 0x9F, 0x38, 0x76, 0x40, 0x87, 0x29, 0x65, 0xC8, 0x1B, 0xE6, };
const unsigned char* K = KEY + 0x10 * iv; for (size_t i = 0; i < 4; i++) { buf[0 + i * 4] ^= K[0 + i]; buf[1 + i * 4] ^= K[4 + i]; buf[2 + i * 4] ^= K[8 + i]; buf[3 + i * 4] ^= K[12 + i]; }}

//加解密static void encrypt(unsigned char buf[0x10]){ unsigned char buf_[0x10] = { 0 }; for (size_t i = 0; i < 4; i++) { buf_[4 * i + 0] = buf[0 + i]; buf_[4 * i + 1] = buf[4 + i]; buf_[4 * i + 2] = buf[8 + i]; buf_[4 * i + 3] = buf[12 + i]; }
xor_enc_dec(buf_, 0x0); for (size_t i = 1; i < 0xe; i++) { transform(buf_); ror_enc(buf_); rol_xor_enc(buf_); xor_enc_dec(buf_, i); }
transform(buf_); ror_enc(buf_); xor_enc_dec(buf_, 0xe);
for (size_t i = 0; i < 4; i++) { buf[0 + i] = buf_[4 * i + 0]; buf[4 + i] = buf_[4 * i + 1]; buf[8 + i] = buf_[4 * i + 2]; buf[12 + i] = buf_[4 * i + 3]; }}static static void decrypt(unsigned char buf[0x10]){ unsigned char buf_[0x10] = { 0 }; for (size_t i = 0; i < 4; i++) { buf_[4 * i + 0] = buf[0 + i]; buf_[4 * i + 1] = buf[4 + i]; buf_[4 * i + 2] = buf[8 + i]; buf_[4 * i + 3] = buf[12 + i]; }
xor_enc_dec(buf_, 0xe); ror_dec(buf_); untransform(buf_); for (int i = 0xd; i > 0; i--) { xor_enc_dec(buf_, i); rol_xor_dec(buf_); ror_dec(buf_); untransform(buf_); } xor_enc_dec(buf_, 0x0);
for (size_t i = 0; i < 4; i++) { buf[0 + i] = buf_[4 * i + 0]; buf[4 + i] = buf_[4 * i + 1]; buf[8 + i] = buf_[4 * i + 2]; buf[12 + i] = buf_[4 * i + 3]; }}

RVA_53E3:压缩_算法B

正算及逆算实现如下:
//压缩static void compress(const unsigned char bufin[0xc0], unsigned char bufout[0x20]){    const char trans[8] = { -4, -2, 0, 2, 4, 8, 10, 20 };    char mask[0x200] = { 0 };
unsigned ib = 0; unsigned im = 0; unsigned cur = 0; unsigned bits = 0; do { while (bits < 3) { cur <<= 8; cur |= bufin[ib++]; bits += 8; }
while (bits >= 3) { //取高3位 auto c = (cur >> (bits - 3)) & 7; mask[im++] = trans[c]; bits -= 3; } //最多还有3位有效位 cur &= 7; } while (im < 0x200); assert(im == 0x200 && bits == 0 && ib == 0xc0);
//转换 const int KEY[4][8] = { {-1,-1,-1,1,1,-1,1,1}, {-1,-1,1,-1,1,1,1,-1}, {-1,1,-1,1,1,1,-1,-1}, {-1,1,-1,-1,-1,-1,1,-1}, };
//压缩 im = 0; for (size_t k = 0; k < 8; k++) { unsigned char bytes[4] = { 0 }; for (size_t i = 0; i < 8; i++) { int as[4] = { 0 }; for (size_t j = 0; j < 8; j++) { as[0] += KEY[0][j] * mask[im]; as[1] += KEY[1][j] * mask[im]; as[2] += KEY[2][j] * mask[im]; as[3] += KEY[3][j] * mask[im]; im++; }
unsigned char b = 1 << (8 - i - 1); for (size_t m = 0; m < 4; m++) { assert(as[m] == 8 || as[m] == -8); if (as[m] == 8) { bytes[m] |= b; } } }
bufout[k + 0] = bytes[0]; bufout[k + 0x8] = bytes[1]; bufout[k + 0x10] = bytes[2]; bufout[k + 0x18] = bytes[3]; }}//膨胀static void dilate(const unsigned char bufin[0x20], unsigned char bufout[0xc0]){ const char trans[8] = { -4, -2, 0, 2, 4, 6, 8, 10 }; char mask[0x200] = { 0 }; unsigned im = 0;
//转换 const int KEY[4][8] = { {-1,-1,-1,1,1,-1,1,1}, {-1,-1,1,-1,1,1,1,-1}, {-1,1,-1,1,1,1,-1,-1}, {-1,1,-1,-1,-1,-1,1,-1}, };
//压缩 im = 0; for (size_t k = 0; k < 8; k++) { unsigned char bytes[4] = { 0 }; bytes[0] = bufin[k + 0]; bytes[1] = bufin[k + 0x8]; bytes[2] = bufin[k + 0x10]; bytes[3] = bufin[k + 0x18];

for (size_t i = 0; i < 8; i++) { int as[4] = { 0 }; unsigned char b = 1 << (8 - i - 1); for (size_t m = 0; m < 4; m++) { if (bytes[m] & b) { as[m] = 8; } else { as[m] = -8; } }
/* 组织数据使得【compress】执行下面的运算后as[4]数据正确即可 for (size_t j = 0; j < 8; j++) { as[0] += KEY[0][j] * mask[im]; as[1] += KEY[1][j] * mask[im]; as[2] += KEY[2][j] * mask[im]; as[3] += KEY[3][j] * mask[im]; im++; } */
//枚举遍历出符合条件的数据 size_t limit = 5; //可选8 for (size_t i1 = 0; i1 < limit; i1++) { for (size_t i2 = 0; i2 < limit; i2++) { for (size_t i3 = 0; i3 < limit; i3++) { for (size_t i4 = 0; i4 < limit; i4++) { for (size_t i5 = 0; i5 < limit; i5++) { for (size_t i6 = 0; i6 < limit; i6++) { for (size_t i7 = 0; i7 < limit; i7++) { for (size_t i8 = 0; i8 < limit; i8++) { int as_s[4] = { 0 }; char m_s[8] = { trans[i1], trans[i2] ,trans[i3] ,trans[i4] ,trans[i5] ,trans[i6] ,trans[i7] ,trans[i8] }; for (size_t j = 0; j < 8; j++) { as_s[0] += KEY[0][j] * m_s[j]; as_s[1] += KEY[1][j] * m_s[j]; as_s[2] += KEY[2][j] * m_s[j]; as_s[3] += KEY[3][j] * m_s[j]; } if (as_s[0] == as[0] && as_s[1] == as[1] && as_s[2] == as[2] && as_s[3] == as[3]) { //存在多解, 找出一组即可 memcpy(mask + im, m_s, 8); im += 8; goto leave; } } } } } } } } }
leave: continue; }

} assert(im == 0x200);

//转储为字节数组 im = 0; unsigned ib = 0; unsigned cur = 0; unsigned bits = 0; do { while (bits < 8) { cur <<= 3; bits += 3; auto c = mask[im++]; c /= 2; c += 2; cur |= c; }
while (bits >= 8) { //取高3位 auto c = (cur >> (bits - 8)) & 0xff; bufout[ib++] = c; bits -= 8; }
} while (im < 0x200); assert(im == 0x200 && bits == 0 && ib == 0xc0);
//测试 unsigned char compressed[0x20] = { 0 }; compress(bufout, compressed); for (size_t i = 0; i < 0x20; i++) { if (compressed[i] != bufin[i]) { DebugBreak(); } }}

RVA_53FE:编码_算法B

算法较复杂,内存DUMP中间值存储,只做逆算实现,如下:
//解同余方程 x + a ≡ val(mod 0xff8f)static long long congruence_add(unsigned short a, unsigned short val){
long long v = val; v -= a; if (v < 0) v += 0xff8f;
assert(v <= MAXUINT16); return v;}
//解同余方程 x - a ≡ val(mod 0xff8f)static long long congruence_sub(long long a, long long val){ long long v = val; v += a; v %= 0xff8f; /*if (v >= 0xff8f) v -= 0xff8f;*/
assert(v <= MAXUINT16); return v;}
//解同余方程 x * a ≡ val(mod 0xff8f)static long long congruence_mul(long long a, long long val){ //x * 0x26c0 ≡ val(mod 0xff8f) long long v = val; while (v % a != 0) { v += 0xff8f; }
assert(v <= MAXULONG32); return (unsigned short)((unsigned long)v / a);}
//加密盒转换static long long __transform_box(const unsigned char boxin[0x3700], unsigned char boxout[0x3700], int level){ //原始算法模拟 /*for (int m = 0; m < 16; m++) { long long* p = (long long*)(box + 0x370 * m); for (int n = m + 1; n < 16; n++) { long long* q = (long long*)(box + 0x370 * n); long long c = q[m]; for (int i = m; i < 17; i++) { long long s = q[i] * p[m] - p[i] * c; s %= 0xff8f; if (s < 0) s += 0xff8f;
q[i] = s; } } }*/
long long c = 0; memcpy(boxout, boxin, 0x3700); for (int m = 0; m < level; m++) { long long* p = (long long*)(boxout + 0x370 * m); for (int n = m + 1; n < 16; n++) { long long* q = (long long*)(boxout + 0x370 * n); c = q[m]; for (int i = m; i < 17; i++) { long long s = q[i] * p[m] - p[i] * c; s %= 0xff8f; if (s < 0) s += 0xff8f;
q[i] = s; } } } return c;}
//解码static void decode(const unsigned char bufin[16], unsigned short bufout[16]){ // unsigned char *box = new unsigned char[0x3700]; unsigned char *box2 = new unsigned char[0x3700]; if (!box || !box2) { cout << "error: alloc memory failed." << endl; return;
}
//读取缓存的计算数据 ifstream f("1.bin", ifstream::binary | ifstream::in); if (!f || !f.read((char*)box, 0x3700)) { cout << "error: read file data failed." << endl; delete[]box; delete[]box2; return; } f.close();
//计算中间值 unsigned short key[16] = { 0xd6dc, 0xd613, 0x66cd, 0x332e, 0x9932, 0x5db8, 0x1731, 0xacde, 0x3c5a, 0x4d87, 0xbeb1, 0xb5bb, 0x125f, 0x2526, 0xdc64, 0x26c0 }; unsigned short mid[16] = { 0 }; for (int i = 15; i >= 0; i--) { mid[i] = (unsigned short)congruence_mul(key[i], bufin[i]); } for (int m = 0; m < 16; m++) { long long* p = (long long*)(box + 0x370 * m); long long s = mid[m]; for (int i = 15; i >= m + 1; i--) { assert(p[i] <= MAXUINT16); long long ll = p[i] * bufin[i]; s = congruence_sub(ll, s); //cout << hex << s << endl; } mid[m] = (unsigned short)s; //cout << hex << s << " " << p[16] << endl; }
//计算原始值 f.open("0.bin", ifstream::binary | ifstream::in); if (!f || !f.read((char*)box, 0x3700)) { cout << "error: read file data failed." << endl; delete[]box; return; } f.close();
for (int i = 0; i < 16; i++) { long long m = mid[i]; long long last = 0; if (i > 0) last = mid[i - 1]; for (int lev = i; lev > 0; lev--) { auto c = __transform_box(box, box2, lev - 1); long long* p = (long long*)(box2 + 0x370 * (lev - 1)); long long* q = (long long*)(box2 + 0x370 * i);
m = congruence_sub(mid[lev - 1] * q[lev - 1], m); m = congruence_mul(p[lev - 1], m); last = m; } bufout[i] = (unsigned short)m; }
delete[]box; delete[]box2;}

RVA_541E:校验算法(hash2)

校验加解密算法实现如下:
//校验static void encrypt_verify(unsigned char buf1[16], unsigned char buf2[16]){    unsigned char k1[16] =    {        0x1A, 0x1C, 0x11, 0x18, 0x08, 0x0C, 0x19, 0x01, 0x20, 0x0B, 0x07, 0x10, 0x17, 0x02, 0x1D, 0x15,    };    unsigned char k2[16] =    {        0x15, 0x17, 0x16, 0x14, 0x13, 0x0C, 0x04, 0x12, 0x03, 0x06, 0x10, 0x0E, 0x0A, 0x18, 0x1C, 0x0F,    };    unsigned char k3[16] =    {        0x03, 0xEE, 0xEC, 0x11, 0x14, 0x0E, 0x05, 0x0C, 0x03, 0xED, 0xF7, 0x05, 0x00, 0xF6, 0x00, 0xE7,    };
unsigned char out[16] = { 0 }; for (size_t i = 0; i < 16; i++) { unsigned char c = buf1[i]; c ^= k2[i];
unsigned char r = (unsigned char)(k1[i] & 7); __asm { mov al, c mov cl, r ror al, cl mov c, al }
c += (k3[i] - buf2[i]);
out[i] = c; }}static void decrypt_verify(unsigned char buf[16]){ const unsigned char act[16] = { 0x15, 0x09, 0x0C, 0x1F, 0x1C, 0x13, 0x16, 0x19, 0x1D, 0x03, 0x10, 0x0F, 0x01, 0x02, 0x1E, 0x06, };
unsigned char k1[16] = { 0x1A, 0x1C, 0x11, 0x18, 0x08, 0x0C, 0x19, 0x01, 0x20, 0x0B, 0x07, 0x10, 0x17, 0x02, 0x1D, 0x15, }; unsigned char k2[16] = { 0x15, 0x17, 0x16, 0x14, 0x13, 0x0C, 0x04, 0x12, 0x03, 0x06, 0x10, 0x0E, 0x0A, 0x18, 0x1C, 0x0F, }; unsigned char k3[16] = { 0x03, 0xEE, 0xEC, 0x11, 0x14, 0x0E, 0x05, 0x0C, 0x03, 0xED, 0xF7, 0x05, 0x00, 0xF6, 0x00, 0xE7, };
//unsigned char out[16] = { 0 }; for (size_t i = 0; i < 16; i++) { unsigned char c = act[i]; c -= (k3[i] - buf[i]);
unsigned char r = (unsigned char)(k1[i] & 7); __asm { mov al, c mov cl, r rol al, cl mov c, al }
c ^= k2[i];
buf[i] = c; }}
void alg_1(unsigned char seri[0x20]){    unsigned char seri_d[16] =    {        0x51, 0x0B, 0x3D, 0x82, 0xCB, 0x55, 0x77, 0xB5, 0xDC, 0x1A, 0x90, 0x82, 0x33, 0xC1, 0x2A, 0x36,    };
decrypt(seri_d); cout << "STEP_1A:" << endl; for (size_t i = 0; i < 16; i++) { cout << setw(2) << setfill('0') << right << hex << (int)seri_d[i] << " " << flush; } cout << endl;
unsigned char seri_dl[0x20] = { 0 }; dilate(seri_d, seri_dl); cout << "STEP_2A:" << endl; for (size_t i = 0; i < 0x20; i++) { cout << setw(2) << setfill('0') << right << hex << (int)seri_dl[i] << " " << flush; } cout << endl;

aes_dec(seri_dl); cout << "STEP_3A:" << endl; for (size_t i = 0; i < 0x20; i++) { cout << setw(2) << setfill('0') << right << hex << (int)seri_dl[i] << " " << flush; } cout << endl;

vm_decode(seri_dl); cout << "STEP_4A:" << endl; for (size_t i = 0; i < 0x20; i++) { cout << setw(2) << setfill('0') << right << hex << (int)seri_dl[i] << " " << flush; } cout << endl;
memcpy(seri, seri_dl, 0x20);}void alg_2(unsigned char seri[0xc0]){ unsigned char seri_d[16] = { 0xF5, 0x0B, 0xAB, 0xE1, 0x20, 0x7E, 0x50, 0x75, 0x31, 0x2B, 0x89, 0x60, 0xC1, 0x9E, 0xF1, 0x44, }; decrypt(seri_d); cout << "STEP_1B:" << endl; for (size_t i = 0; i < 16; i++) { cout << setw(2) << setfill('0') << right << hex << (int)seri_d[i] << " " << flush; } cout << endl;

unsigned short seri_s[16] = { 0 }; decode(seri_d, seri_s); cout << "STEP_2B:" << endl; for (size_t i = 0; i < 16 * 2; i++) { cout << setw(2) << setfill('0') << right << hex << (int)((unsigned char*)seri_s)[i] << " " << flush; } cout << endl;

unsigned char seri_c[0xc0] = { 0 }; dilate((unsigned char*)seri_s, seri_c); cout << "STEP_3B:" << endl; for (size_t i = 0; i < 0xc0; i++) { cout << setw(2) << setfill('0') << right << hex << (int)seri_c[i] << " " << flush; if ((i + 1) % 0x20 == 0) { cout << endl; } } cout << endl;
memcpy(seri, seri_c, 0xc0);}int main(){ unsigned char seri[0xe0] = { 0 };
alg_2(seri + 0x20); alg_1(seri);

ostringstream oss; for (size_t i = 0; i < 0xe0; i++) { oss << setw(2) << hex << setfill('0') << right << (int)seri[i] << flush; } cout << "INPUT:" << endl << oss.str() << endl;
cin.get(); return 0;}
看完解析你会了吗?你还有不一样的解题思路吗?
实践出真知~速速动手自己做一遍,才算把知识装进脑子~
欢迎大家分享解题思路哦~

现在第九题《命悬一线》正在火热进行中!

有你比赛更精彩!

越早提交答案,得分越高哦!

立即扫码加入战斗!

华为全面屏智能电视、Xbox One X、JBL 无线蓝牙耳机等你来拿哦!

赛题回顾

2020 KCTF秋季赛 | 第一题点评及解题思路

出题战队:七星战队

2020 KCTF秋季赛 | 第二题设计及解题思路

出题战队:中娅之戒

2020 KCTF秋季赛 | 第三题点评及解题思路

出题战队:2019

2020 KCTF秋季赛 | 第四题点评及解题思路

出题战队:大灰狼爱喜羊羊

2020 KCTF秋季赛 | 第五题设计及解题思路

出题战队:金左手

2020 KCTF秋季赛 | 第六题设计及解题思路

出题团队:T.O.

2020 KCTF秋季赛 | 第七题设计及解题思路

出题团队:HU1战队

你的好友秀秀子拍了拍你

并请你点击阅读原文,参与最新一题的挑战!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458377862&idx=1&sn=cf56577715011d13fa736afa53a53937&chksm=b180ee0c86f7671ac1c8e70b86862911e6ae25fc2ff29f40c5ba754b8c24cead30a94a33c6df#rd
如有侵权请联系:admin#unsafe.sh