MoeCTF2022 reverse
最近做的re题太难了 做下新生赛难度(Newstar week5难度)
Broken_hash
应该算是非预期
查看main
刚开始我是以moectf{moectf{}...}这种格式凑的88个字符
发现这个hash变换对于相同的字母会映射到相同的一个数字
所以有个简便思路:
我们输入
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=000000000000
然后动调拿到它变换后的表 做一个map映射即可得到flag
solution.c
#include<bits/stdc++.h>
using namespace std;
map<unsigned int,char>mp;
signed main(){
string s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=000000000000";
unsigned int a1[100] = {
// abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=000000000000
0xD7C1A410, 0x9E3919E7, 0x85BEF77E, 0x5251E8C2, 0xA2D74292, 0x669E1609, 0x5C1DFF11, 0xCB5C3FAD,
0xFECF7685, 0xB0F33A9A, 0x1833E8B1, 0x0B7D1395, 0x64744C9A, 0xDE321186, 0x047C2FF1, 0xEF53E254,
0x1902B329, 0x866CAF4F, 0x4A528AE0, 0x711FCBF7, 0xCEDB7952, 0x352B172C, 0xAFEA7FF6, 0x3175EDAB,
0xD035E914, 0x20D324AE, 0x06372812, 0x46927FA8, 0x73456320, 0x04E3F843, 0x75DCD570, 0x6941C8A4,
0x581D99E5, 0x7EF00E40, 0x7A260E4D, 0xC578F843, 0x17947C53, 0x00786C70, 0x9C19F0F3, 0x1D795AC9,
0x9F12424D, 0xAB021E08, 0x077ACB10, 0xD1D0F68E, 0x82ACF96A, 0x9F66DD04, 0x8AD9BAF7, 0xAAE6D8C9,
0x0E73CAEF, 0xBFC92893, 0xD5380C52, 0x81453D43, 0xBEA99D3B, 0xB16E48B3, 0x05C6054F, 0x36568AF5,
0xBEA2375F, 0x567375C3, 0xBF0FD0CB, 0x4BE9314A, 0x7F2A2EBE, 0xC87A7C7E, 0x94D2FB03, 0xC6059399,
0x17AA5D21, 0x359A6F74, 0xE3FC2922, 0x0D9C61FD, 0xB12D13F1, 0xC7F7B407, 0xE3F11899, 0x8BC73EC0,
0xFB8F95A7, 0xF51FD089, 0x59A10D75, 0x3C661DDC, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B,
0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B, 0xBEA99D3B
};
unsigned int a2[97] = {
0x64744C9A, 0x047C2FF1, 0xA2D74292, 0x85BEF77E, 0x711FCBF7, 0x669E1609, 0x6BBD9DB6, // moectf{
0x6941C8A4, 0xB16E48B3, 0xDE321186, 0x5251E8C2, 0xFB8F95A7, 0x711FCBF7, 0xCB5C3FAD, 0x36568AF5, 0xFB8F95A7,
0x82ACF96A, 0x75DCD570, 0x7EF00E40, 0xFB8F95A7, 0x4BE9314A, 0xCB5C3FAD, 0xA2D74292, 0xDE321186,
0xFB8F95A7, 0x46927FA8, 0xB16E48B3, 0xD7C1A410, 0x567375C3, 0x711FCBF7, 0xFB8F95A7, 0x9C19F0F3,
0xD035E914, 0xFB8F95A7, 0x6941C8A4, 0x0B7D1395, 0xD7C1A410, 0xC87A7C7E, 0xFB8F95A7, 0xD7C1A410,
0xDE321186, 0x5251E8C2, 0xFB8F95A7, 0xD5380C52, 0xBEA99D3B, 0xCEDB7952, 0xFB8F95A7, 0x73456320,
0xD7C1A410, 0xDE321186, 0xFB8F95A7, 0x581D99E5, 0xA2D74292, 0x711FCBF7, 0xFB8F95A7, 0x06372812,
0xFB8F95A7, 0x73456320, 0xCEDB7952, 0xEF53E254, 0xFB8F95A7, 0x9F12424D, 0x669E1609, 0xFB8F95A7,
0x9C19F0F3, 0xFECF7685, 0x0B7D1395, 0x1833E8B1, 0xFB8F95A7, 0x9F66DD04, 0xA2D74292, 0xD7C1A410,
0xFB8F95A7, 0x6941C8A4, 0x866CAF4F, 0x047C2FF1, 0x64744C9A, 0xFB8F95A7, 0xD5380C52, 0xCEDB7952,
0xDE321186, 0x81453D43, 0xCB5C3FAD, 0xB16E48B3, 0xC578F843, 0xCEDB7952, 0xDE321186,
0xE38C6F07 // }
};
for(int i=0;i<88;i++)
mp[a1[i]] = s[i];
string flag = "";
for(int i:a2){
flag += mp[i];
}
cout<<flag;
}
Fake_code
好题!新学习了逆向中的异常的处理方法
拿到题目直接IDA查看
逻辑特别简单
但我们发现dword_7FF6BF285000
的值静态看不出怎么变化的
动调查看却发现莫名其妙出现divide 0 的异常错误
查看题目附件给的pdf才发现这题考察点就在于这个异常处理
这涉及到SEH的知识点
我们用__try __except的时候 IDA是不会自动把except部分反汇编的
只有看汇编才能理清逻辑
点击查看这个抛出的异常块
可以看到这里出现了关键的dword_7FF6BF285000
值
这里手撸一下汇编是这种形式key = ((((((key*97)+101)))%233)^41);
开始分析到这里我就以为全部结束了 拿到key的初始值0x19就开始每次对key做变换 但得到乱码
这是因为忘记了只有有异常时 才会触发key的变化
所以还要分析前面异常产生的原因
看汇编 大概注释了一下
结合源代码知道这里的...
就是每次的v5 所以当v5>>7==0
时会触发异常(其实手动调一下就会发现不是每次都会触发)
solution.c
#include<bits/stdc++.h>
using namespace std;
map<unsigned int,char>mp;
signed main(){
unsigned char enc[51] = {
0x1E, 0x70, 0x7A, 0x6E, 0xEA, 0x83, 0x9E, 0xEF, 0x96, 0xE2, 0xB2, 0xD5, 0x99, 0xBB, 0xBB, 0x78,
0xB9, 0x3D, 0x6E, 0x38, 0x42, 0xC2, 0x86, 0xFF, 0x63, 0xBD, 0xFA, 0x79, 0xA3, 0x6D, 0x60, 0x94,
0xB3, 0x42, 0x11, 0xC3, 0x90, 0x89, 0xBD, 0xEF, 0xD4, 0x97, 0xF8, 0x7B, 0x8B, 0x0B, 0x2D, 0x75,
0x7E, 0xDD, 0xCB
};
unsigned char table[256] = {
0xAC, 0x04, 0x58, 0xB0, 0x45, 0x96, 0x9F, 0x2E, 0x41, 0x15, 0x18, 0x29, 0xB1, 0x33, 0xAA, 0x12,
0x0D, 0x89, 0xE6, 0xFA, 0xF3, 0xC4, 0xBD, 0xE7, 0x70, 0x8A, 0x94, 0xC1, 0x85, 0x9D, 0xA3, 0xF2,
0x3F, 0x82, 0x8E, 0xD7, 0x03, 0x93, 0x3D, 0x13, 0x05, 0x6B, 0x41, 0x03, 0x96, 0x76, 0xE3, 0xB1,
0x8A, 0x4A, 0x22, 0x55, 0xC4, 0x19, 0xF5, 0x55, 0xA6, 0x1F, 0x0E, 0x61, 0x27, 0xCB, 0x1F, 0x9E,
0x5A, 0x7A, 0xE3, 0x15, 0x40, 0x94, 0x47, 0xDE, 0x00, 0x01, 0x91, 0x66, 0xB7, 0xCD, 0x22, 0x64,
0xF5, 0xA5, 0x9C, 0x68, 0xA5, 0x52, 0x86, 0xBD, 0xB0, 0xDD, 0x76, 0x28, 0xAB, 0x16, 0x95, 0xC5,
0x26, 0x2C, 0xF6, 0x39, 0xBE, 0x00, 0xA5, 0xAD, 0xE3, 0x93, 0x9E, 0xE3, 0x05, 0xA0, 0xB0, 0x1D,
0xB0, 0x16, 0x0B, 0x5B, 0x33, 0x95, 0xA4, 0x09, 0x16, 0x87, 0x56, 0x1F, 0x83, 0x4E, 0x4A, 0x3C,
0x55, 0x36, 0x6F, 0xBB, 0x4C, 0x4B, 0x9D, 0xB1, 0xAE, 0xE5, 0x8E, 0xC8, 0xFB, 0x0E, 0x29, 0x8A,
0xBB, 0xFC, 0x20, 0x62, 0x04, 0x2D, 0x80, 0x61, 0xD6, 0xC1, 0xCC, 0x3B, 0x89, 0xC5, 0x8B, 0xD5,
0x26, 0x58, 0xD6, 0xB6, 0xA0, 0x50, 0x75, 0xAB, 0x17, 0x83, 0x7F, 0x37, 0x2B, 0xA0, 0x1D, 0x2C,
0xCF, 0xC7, 0xE0, 0xE5, 0x49, 0xC9, 0xFA, 0x6B, 0xC0, 0x98, 0x66, 0x99, 0x92, 0x00, 0x02, 0xD4,
0x75, 0x46, 0x22, 0x05, 0x35, 0xD1, 0x4B, 0xC5, 0xAD, 0xE0, 0x8E, 0x45, 0x3B, 0x50, 0x15, 0xB5,
0x2E, 0x85, 0x30, 0x89, 0x54, 0x12, 0xDE, 0xF1, 0x5A, 0xF0, 0x2B, 0xA7, 0x1B, 0x4A, 0x26, 0x5D,
0x98, 0xD4, 0xA1, 0xBE, 0xD1, 0x4D, 0x7E, 0x38, 0xDE, 0x0B, 0x0A, 0x54, 0xB8, 0x73, 0x6D, 0xAD,
0x8C, 0x1E, 0xD9, 0x31, 0x5F, 0x56, 0x7E, 0xBD, 0x48, 0x32, 0x98, 0x2E, 0x3E, 0xEB, 0xA2, 0x1D
};
int key=25,c=0;
for(int i=0;i<51;i++){
c = (127*c+102)%255;
if(c>>7==0){
key = ((((((key*97)+101)))%233)^41);
}
cout<<char(enc[i]^table[key]);
}
}
// moectf{Re4d_4ssemb1y_t0_g3t_the_m4gic_key_0f_Tr4ck}
EzRisc-V
新鲜的ricv-v指令集
在分析的时候可以找一些共有的指令 再查询一下一些关键指令大致能还原
中间的变换操作为 xor 57 &0xff
这里就是check 对a5(unk_50508)作变换后与flag是否相等 套了一个长度为38(39)的loop
s = [0x54, 0x56, 0x5C, 0x5A, 0x4D, 0x5F, 0x42, 0x4B, 0x08, 0x4A, 0x5A, 0x14, 0x4F, 0x66, 0x08, 0x4A, 0x66, 0x4A, 0x56, 0x09, 0x09, 0x56, 0x56, 0x66, 0x08, 0x57, 0x4D, 0x5C, 0x4B, 0x5C, 0x4A, 0x4D, 0x08, 0x57,0x00, 0x18, 0x18, 0x18,0x44]
print(len(s))
for c in s:
print(chr(((c)^57)&0xff),end='')
# moectf{r1sc-v_1s_so00oo_1nterest1n9!!!}
Art
不用UPX工具脱壳的话
这里用一下之前学到的新姿势
https://primelyw.github.io/2020/04/15/2018网鼎杯/#i-like-pack
网鼎杯那道没复现出来 但这道轻松
但是得到的dump文件好像有些地方不全 故还是UPX脱壳好了
check应该就是检查变换后的v17与unk_是否相等 后面那个像是检查前后缀?
总之核心就是如何通过变换后的v17得到初始的输入
乍看loop中有个v10不好确定 动调也找不到规律 其实这里根本没有影响 反正后面对v12重新赋了值
注意到我们考虑的都是byte所以 LOWORD LOBYTE这种编译时编译器加上的我们直接去掉
然后用已知v17[0] = 'm' 枚举v17[j]满足要求就添加
solution.c
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
int main(){
unsigned int enc[] = {0x02, 0x18, 0x0F, 0xF8, 0x19, 0x04, 0x27, 0xD8, 0xEB, 0x00, 0x35, 0x48, 0x4D, 0x2A, 0x45, 0x6B, 0x59, 0x2E, 0x43, 0x01, 0x18, 0x5C, 0x09, 0x09, 0x09, 0x09, 0xB5, 0x7D};
int v17[100]={'m','o','e'};
for ( int j = 1; j <= 27; ++j )
{
for(int x=32;x<=144;x++){
int v11,v12,v10,v13;
v11 = (unsigned __int8)v17[j - 1];
(v10) = (char)v11;
(v12) = (unsigned __int16)(121 * (char)v11) >> 8;
v13 = v12;
(v13) = (char)v12 >> 3;
v10 = v11 - 17 * (v13 - (unsigned __int8)(v17[j - 1] >> 7));
if((v17[j - 1] ^ (unsigned __int8)(v10 + x) ^ 0x19)==enc[j-1]){
v17[j] = x;
}
}
}
for(int i=0;i<28;i++){
printf("%c",v17[i]);
}
}
// moectf{Art_i5_b14s7ing!!!!!} 艺术就是爆破...