Previously
坐!大!牢!
然而,真的是纯纯坐大牢吗?
还是我在某些方面犯蠢了呢?
Reverse
Small
似乎是挺简单的一道汇编题,但是……?
题目在许多渠道都无法正确反汇编甚至运行,比方说ida,die,甚至gdb都救不了,这里就不一一列举失败画面了。无奈只能字节码转汇编硬刚:
注意到loc_68的代码有些眼熟,又是左移4位又是右移5位,又有准确的delta和key,形似tea(tea加密相关可以看看人在码途的文章,应该是处理原文的主要部分。还有这一段:
cmp edx, 23h ; '#'
jl short loc_52
应该对应着tea的加密轮数,tea似乎稳了。但是,这只是我犯浑的开始——
一开始读汇编代码的时候,我注意到被位运算的两个寄存器不一样,寄存器eax和ecx似乎是分存被处理数据的左半和右半。于是在一阵分析后认为这是对原先的tea进行修改,把加密方程变成了这样:
v0 += ((v1<<4) + KEY[0]) ^ (v1 + sum) ^ ((v0>>5) + KEY[1]);
v1 += ((v0<<4) + KEY[2]) ^ (v0 + sum) ^ ((v1>>5) + KEY[3]);
但是我很显然忽略了一个问题,汇编语段里出现了一个ebp寄存器,这玩意的作用相当于开了一个栈,且ebp始终作为某一时刻这个栈的记录存在。在第一个方程过后出现了这样一个语句:
add ebp, ecx
mov ecx, ebp
此时ecx作为左半边的处理结果入栈。而在之后又出现了一个诡异的场景:
mov eax, ebp
shl eax, 4
add eax, 45h ; 'E'
xor ecx, eax
mov eax, ebp
ebp的值同样在之后被用在了eax身上!结合上面的汇编代码,我们不难发现,eax只是被当作临时存储上一级运算结果的变量,真正存储每一次式子运算结果的,一直是ecx!而这也就说明……出题人根本没动过tea,动的是实现tea的方式……
好了,既然认准了是原装的tea,那么直接搬脚本也是合情合理,问题来了,数据呢?要处理的数据呢?
这就要提到我第二次麻瓜的地方了……就是这里。
按说到了syscall这块,整个程序也就差不多结束了。但很显然,年轻的我并没有发现什么不对,因为当时那底下还有几个函数——
但是,字节码转汇编程序,某种意义上就是对凌乱数据的解读。而我在一堆函数里迟迟找不到原文或者密文的原因只有可能是一个——
在找主函数的时候,我可能把原本作为数据的部分也转化了。
果然,在跟着题解回滚之后,看到原本在那下面的地方果然是出题人藏都不打算藏的数据集:
那么,该做啥也很清楚了。以下代码参考了0x000000AC和Arr3stY0u:
#include <stdio.h>
#include <stdint.h>
uint32_t KEY[4]={1,0x23,0x45,0x67}; // Key space for bit shifts
int n;
void encrypt(uint32_t* v) {
uint32_t v0=v[0], v1=v[1], sum=0, i; // set up
uint32_t delta=0x67452301; // a key schedule constant, but different in this topic
for (i=0; i < 35; i++) { // basic cycle start
sum += delta;
v0 += ((v1<<4) + KEY[0]) ^ (v1 + sum) ^ ((v1>>5) + KEY[1]);
v1 += ((v0<<4) + KEY[2]) ^ (v0 + sum) ^ ((v0>>5) + KEY[3]);
} // end cycle
v[0]=v0; v[1]=v1;
}
void decrypt (uint32_t* v) {
uint32_t v0=v[0], v1=v[1], sum=0x67452301*35, i; // set up, the sum is different because delta
uint32_t delta=0x67452301; // a key schedule constant
for (i=0; i<35; i++) { // basic cycle start
v1 -= ((v0<<4) + KEY[2]) ^ (v0 + sum) ^ ((v0>>5) + KEY[3]);
v0 -= ((v1<<4) + KEY[0]) ^ (v1 + sum) ^ ((v1>>5) + KEY[1]);
sum -= delta;
} // end cycle
v[0]=v0; v[1]=v1;
}
void dumpdata(uint32_t* v)
{
for (int i=0;i<n;i++)
for (int j=0;j<sizeof(uint32_t)/sizeof(uint8_t);j++)
printf("%c",(v[i] >> (j*8)) & 0xFF);
printf("\n");
return;
}
int main()
{
uint32_t v[] = { 0xde087143,0xc4f91bd2,0xdaf6dadc,0x6d9ed54c,0x75eb4ee7,0x5d1ddc04,0x511b0fd9,0x51dc88fb };
n = sizeof(v)/sizeof(uint32_t);
printf(" Original Values: [");
for(int i=0;i<n;i++)
printf(" %X", v[i]);
printf(" ]");
// for(int i=0;i<n/2;i++)
// encrypt(&v[i*2]);
//
// printf("\n Encrypted: [");
// for(int i=0;i<n;i++)
// printf(" %X", v[i]);
// printf(" ]");
// dumpdata(v); //there's no need encrypt xd
for(int i=0;i<n/2;i++)
decrypt(&v[i*2]); //expand the operation to loop for more data
printf("\n Decrypted: [");
for(int i=0;i<n;i++)
printf(" %X", v[i]);
printf(" ]\n");
dumpdata(v); //to print out data in string format
return 0;
}
flag{327a6c4304ad5938eaf0efb6cc3e53dc}
麻了,汇编一点不会,练底力去。