BUUCTF [网鼎杯 2020 青龙组]singal
两种做法
算法逆向
关键就是将正向加密时v9的值找出来 逆向解密时反向即可(在case里面操作v9很容易错)
还有就是加密解密有些语句前后左右要对调
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
signed main(){
unsigned char unk_403040[512] = {
0x0A, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF,
0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF,
0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x84, 0xFF, 0xFF, 0xFF,
0x07, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF,
0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00
};
unsigned int a1[114] = {
0x0000000A, 0x00000004, 0x00000010, 0x00000008, 0x00000003, 0x00000005, 0x00000001, 0x00000004,
0x00000020, 0x00000008, 0x00000005, 0x00000003, 0x00000001, 0x00000003, 0x00000002, 0x00000008,
0x0000000B, 0x00000001, 0x0000000C, 0x00000008, 0x00000004, 0x00000004, 0x00000001, 0x00000005,
0x00000003, 0x00000008, 0x00000003, 0x00000021, 0x00000001, 0x0000000B, 0x00000008, 0x0000000B,
0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002,
0x00000051, 0x00000008, 0x00000004, 0x00000024, 0x00000001, 0x0000000C, 0x00000008, 0x0000000B,
0x00000001, 0x00000005, 0x00000002, 0x00000008, 0x00000002, 0x00000025, 0x00000001, 0x00000002,
0x00000036, 0x00000008, 0x00000004, 0x00000041, 0x00000001, 0x00000002, 0x00000020, 0x00000008,
0x00000005, 0x00000001, 0x00000001, 0x00000005, 0x00000003, 0x00000008, 0x00000002, 0x00000025,
0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002,
0x00000041, 0x00000008, 0x0000000C, 0x00000001, 0x00000007, 0x00000022, 0x00000007, 0x0000003F,
0x00000007, 0x00000034, 0x00000007, 0x00000032, 0x00000007, 0x00000072, 0x00000007, 0x00000033,
0x00000007, 0x00000018, 0x00000007, 0xFFFFFFA7, 0x00000007, 0x00000031, 0x00000007, 0xFFFFFFF1,
0x00000007, 0x00000028, 0x00000007, 0xFFFFFF84, 0x00000007, 0xFFFFFFC1, 0x00000007, 0x0000001E,
0x00000007, 0x0000007A
};
int a2 = 114;
int result; // eax
char Str[200]; // [esp+13h] [ebp-E5h] BYREF
unsigned char v4; // [esp+DBh] [ebp-1Dh]
int v5; // [esp+DCh] [ebp-1Ch]
int v6; // [esp+E0h] [ebp-18h]
int v7; // [esp+E4h] [ebp-14h]
int v8; // [esp+E8h] [ebp-10h]
int v9; // [esp+ECh] [ebp-Ch]
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
v5 = 0;
while ( 1 ) // OpCode:unk_403040 a2:114
{
result = v9;
if ( v9 >= a2 )
break;
// cout<<result<<",";
switch ( a1[v9] )
{
case 1:
Str[v6 + 100] = v4; // 给Str[100~114]赋值
++v9;
++v6;
++v8;
break;
case 2:
v4 = a1[v9 + 1] + Str[v8];
v9 += 2;
break;
case 3:
v4 = Str[v8] - LOBYTE(a1[v9 + 1]);
v9 += 2;
break;
case 4:
v4 = a1[v9 + 1] ^ Str[v8];
v9 += 2;
break;
case 5:
v4 = a1[v9 + 1] * Str[v8];
v9 += 2;
break;
case 6:
++v9;
break;
case 7:
// if ( Str[v7 + 100] != OpCode[v9 + 1] )
// {
// printf("what a shame...");
// exit(0);
// }
++v7;
v9 += 2;
break;
case 8:
Str[v5] = v4;
++v9;
++v5;
break;
case 10:
// read(Str);
++v9;
break;
case 11:
v4 = Str[v8] - 1;
++v9;
break;
case 12:
v4 = Str[v8] + 1;
++v9;
break;
default:
continue;
}
}
cout<<v5<<" "<<v6<<" "<<v7<<" "<<v8<<" "<<v9<<"\n\n\n";
//op[]是代码加密的正向指令流(每次v9的值)
unsigned int op[]={0,1,3,4,6,7,9,10,12,13,15,16,17,18,19,20,22,23,25,26,28,29,30,31,32,33,35,36,38,39,41,42,44,45,46,47,48,49,51,52,54,55,57,58,60,61,63,64,66,67,69,70,72,73,75,76,78,79,81,82,83,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112};
unsigned int data[15] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
unsigned char buf[100];
unsigned char flag[100];
// v9 = 112;
v8 = 15;
v7 = 15;
v6 = 15;
v5 = 15;
int k=75;
while (k>=0) // OpCode:unk_403040 a2:114
{
// cout<<v9<<"\n";
int v9 = op[k];
switch(a1[v9]){
case 1:
v6--;
v8--;
// v9--;
v4 = buf[v6];
break;
case 2:
// v9 -= 2;
flag[v8] = v4 - a1[v9+1];
break;
case 3:
// v9 -= 2;
flag[v8] = v4 + a1[v9+1];
break;
case 4:
// v9 -= 2;
flag[v8] = v4 ^ a1[v9+1];
break;
case 5:
// v9 -= 2;
flag[v8] = v4 / a1[v9+1];
break;
case 6:
// v9--;
break;
case 7:
// v9 -= 2;
v7--;
buf[v7] = a1[v9+1];
break;
case 8:
v5--;
// v9--;
v4 = flag[v5];
break;
case 10:
cout<<"Start!\n\n";
// v9--;
break;
case 11:
// v9--;
flag[v8] = v4 + 1;
break;
case 12:
// v9--;
flag[v8] = v4 - 1;
break;
default:
continue;
}
k--;
}
for(int i=0;i<15;i++)
cout<<flag[i];
return 0;
}
angr库求解
import angr
project = angr.Project('signal.exe') #创建项目,加载二进制文件
state = project.factory.entry_state() #创建state
sim = project.factory.simgr(state) #创建sim
sim.explore(find=0x40175e,avoid=0x4016e6) # 希望到达的和避免的分支
if sim.found:
res = sim.found[0]
res = res.posix.dumps(0)
print("[+] Success! Solution is: {}".format(res.decode("utf-8")))
这里的0x40175e对应的vm_operad函数最后返回result的地方(也就是全部跑完) 0x4016e6对应的What a shame 失败(这里有个疑惑 为什么只能选择0x4016e6 尝试发现就算选择前面的if处的地址都跑不出结果 为什么呢???)
oh我知道了。。。 它if判断的地址和后面shame的不是连续的。。。
将signal.exe和.py放在同一个文件夹下 Linux运行
结果: