re1其实是一个对二进制上的运算,就是将每个字节的二进制分为三部分——高位0+1、低位0与中间值,并记录下三部分各自的大小,再以四字节为一组进行重新填充得出验证码
如上图示例,00111100被分为三部分后
高位:001
中位:111
低位: 00
这时将进行重新组合再拼接,生成两组数,既高位组与低位组拼接位一组,中间位为一组
组一:111
组二:00100
记录下所有数据并生成结构体
typedef struct _Data_Structure
{
char g_flag[4]; //flag
byte num[4]; //中间位大小
byte high_num[4]; //高位大小
byte low_num[4]; //低位大小
byte mod[4]; //组一
byte merge[4]; //组二
}Data_Structure, * PData_Structure;
相信师傅们都知道flag的格式是^swpuctf\{\w{4}\-\w{4}\-\w{4}\-\w{4}\-\w{4}\},将每四位设置为一组进行了上述分解后进行最后的填充操作
申请一个40byte空间,同样分为两部分,其中低20字节存放重组后的flag,高20字节存放高低位大小
将mod填充在高位,merge填充在低位,重新组合成新的一组四字节
将高低位各自大小重新组合为一字节
在写逆算法的时候就可以通过从20位大小段中提取出各个字节高低位大小,再结合20位数据段中高位填充mod与低位填充merge的特点还原flag
推荐师傅们在解题的时候,找到结构体位置,通过观察二进制的变化,更能理清思路、发现规律
int main()
{
byte ptable[40] = { 0x8, 0xea, 0x58, 0xde, 0x94, 0xd0, 0x3b, 0xbe, 0x88, 0xd4, 0x32, 0xb6, 0x14, 0x82, 0xb7, 0xaf, 0x14, 0x54, 0x7f, 0xcf, 0x20, 0x20, 0x30, 0x33, 0x22, 0x33, 0x20, 0x20, 0x20, 0x30, 0x20, 0x32, 0x30, 0x33, 0x22, 0x20, 0x20, 0x20, 0x24, 0x20 };
byte flag[20] = { 0 };
byte high[20] = { 0 };
byte low[20] = { 0 };
byte mod[20] = { 0 };
byte m[20] = { 0 };
byte bit;
byte* table;
DWORD num = 0;
for (int i = 0; i < 20; i++)
{
high[i] = ptable[i + 20] >> 4;
low[i] = ptable[i + 20] << 4;
low[i] >>= 4;
mod[i] = 8 - high[i] - low[i];
}
table = ptable;
for (int i = 0; i < 5; i++)
{
int n = 32, sum = 0;
for (int j = 0; j < 4; j++)
{
bit = 1;
n =32 - mod[i * 4 + j];
num = *(DWORD*)table << sum;
m[i * 4 + j] = num >> n;
sum += mod[i * 4 + j];
bit <<= (8 - high[i * 4 + j]);
flag[i * 4 + j] = (m[i * 4 + j] << low[i * 4 + j]) | bit;
}
table += 4;
}
for (int k = 0; k < 20; k++)
{
printf("%c", flag[k]);
}
system("pause");
return 0;
}
ps:其实在这道题中我还写了虚函数hook,为了防止师傅们通过IDA纯看的方式解题,不知道师傅们发现没有
比赛出题并非我的本意,技术交流才是正真的含义,希望对师傅们有所帮助,同时也希望能与各位师傅进行更多的交流