REVERSE-PRACTICE-BUUCTF-14
- [FlareOn3]Challenge1
- [GUET-CTF2019]number_game
- [GWCTF 2019]re3
- [网鼎杯 2020 青龙组]singal
[FlareOn3]Challenge1
exe程序,运行后提示输入密码,输入错误退出程序,无壳,ida分析
main函数逻辑清晰,读取输入,对输入进行变表base64编码,验证编码结果
之前遇到过变表base64的题目,见REVERSE-PRACTICE-BUUCTF-7或REVERSE-PRACTICE-BUUCTF-10或base64原理及其编解码的python实现
这道题用一个工具,加密解密小玩具,来解,非常方便
[GUET-CTF2019]number_game
elf文件,无壳,ida分析
main函数,获取输入,检验输入长度是否为10且均为0~4的数字,先后经过先序遍历和中序遍历改变输入中各个字符的位置,再顺序地放入数独“#”位置,最后检验数独
sub_400758函数和sub_400807函数先后经过先序遍历和中序遍历改变输入中各个字符的位置,本人一直想不通,于是动调,当输入为0123456789时,sub_400807函数调用结束后的v7为7381940526,于是可以知道输入中字符位置变换的规律
变换前的下标:0 1 2 3 4 5 6 7 8 9
变换后的下标:7 3 8 1 9 4 0 5 2 6
再解数独,在sub_400917函数中可知为5x5的数独
写代码换成正确的位置即可得到flag
由于输入长度只有10位,且均为0~4的数字,也可以写脚本爆破得到flag
[GWCTF 2019]re3
elf文件,无壳,ida分析
main函数,读取输入,检验输入长度是否为32,有一段SMC,自修改代码
ida静态分析,先写idapython脚本完成smc
smc执行前,地址0x402219处是一大段数据
smc的idapython脚本
from idaapi import *
from idautils import *
start_addr = 0x402219
key = 0x99
for i in range(start_addr,start_addr+224):PatchByte(i,Byte(i)^key)
smc执行完成后,按c转换成代码
在地址0x402219处右键->Edit function,将函数结束地址修改为retn指令所在地址,完成后F5反汇编
用插件Findcrypt发现sub_402219是对输入的AES加密,密钥为unk_603170,密文为res
远程调试elf,得到密钥unk_603170
写AES解密脚本即可得到flag
[网鼎杯 2020 青龙组]singal
exe程序,运行后提示输入string,无壳,ida分析
main函数,分析可知是vm的题目,dword_403040中的数据作为opcode传入vm_operad函数中
进入vm_operad函数,分析可知
opcode为10时,读取输入,长度为15
opcode为1时,v4被赋值
opcode为7时,v4和下一个opcode比较,于是7后面的opcode为密文
其余的opcode为input的相关运算
int __cdecl vm_operad(int *opcode, int a2)
{int result; // eaxchar input[100]; // [esp+13h] [ebp-E5h]char v4[100]; // [esp+77h] [ebp-81h]char v5; // [esp+DBh] [ebp-1Dh]int v6; // [esp+DCh] [ebp-1Ch]int v7; // [esp+E0h] [ebp-18h]int v8; // [esp+E4h] [ebp-14h]int v9; // [esp+E8h] [ebp-10h]int opcode_index; // [esp+ECh] [ebp-Ch]opcode_index = 0;v9 = 0;v8 = 0;v7 = 0;v6 = 0;while ( 1 ){result = opcode_index;if ( opcode_index >= a2 )return result;switch ( opcode[opcode_index] ){case 1: // 每当opcode为1时,v4被赋值v4[v7] = v5;++opcode_index;++v7;++v9;break;case 2: // 每当opcode为2时,v5被赋值为下一个opcode与input的和v5 = opcode[opcode_index + 1] + input[v9];// 由于运算使用了下一个opcode,所以opcode_index加2opcode_index += 2;break;case 3:v5 = input[v9] - LOBYTE(opcode[opcode_index + 1]);// 每当opcode为3时,v5被赋值为input与下一个opcode的差opcode_index += 2; // 由于运算使用了下一个opcode,所以opcode_index加2break;case 4:v5 = opcode[opcode_index + 1] ^ input[v9];// 每当opcode为4时,v5被赋值为input与下一个opcode异或的值opcode_index += 2; // 由于运算使用了下一个opcode,所以opcode_index加2break;case 5:v5 = opcode[opcode_index + 1] * input[v9];// 每当opcode为5时,v5被赋值为input与下一个opcode乘积的值opcode_index += 2; // 由于运算使用了下一个opcode,所以opcode_index加2break;case 6: // 每当opcode为6时,跳到下一个opcode++opcode_index;break;case 7: // 每当opcode为7时,验证v4和下一个opcode是否相等,意味着opcode等于7的下一个opcode为密文if ( v4[v8] != opcode[opcode_index + 1] ){printf("what a shame...");exit(0);}++v8;opcode_index += 2; // 由于验证使用了下一个opcode,所以opcode_index加2break;case 8: // 每当opcode为8时,input的值修改input[v6] = v5;++opcode_index;++v6;break;case 10: // opcode的第一个值为10,所以先读取输入,长度为15read(input);++opcode_index;break;case 11: // 每当opcode为11时,v5被赋值为input与数字1的差v5 = input[v9] - 1;++opcode_index;break;case 12: // 每当opcode为12时,v5被赋值为input与数字1的和v5 = input[v9] + 1;++opcode_index;break;default:continue;}}
}
提取出114个opcode,手动进行分类,并按照操作码得到input[0~14]的变换过程
写逆运算脚本即可得到flag
此题目也可以用angr一把梭