BUUCTF在线评测 (buuoj.cn)
用ida打开文件,ctrl+e定位main函数
也可以用shift+F12查找字符串,找与我们解题有关的字符串
通过字符串定位到引用字符串的函数
进入main entry 但还不是我们要分析的代码
进入__libc_start_main中的main参数,是我们要分析的代码
首先进入sub_400770
找到a1,也就是v7中存放的数据
用z3写个脚本
from z3 import *
a3, a4, a5 = Ints('a3 a4 a5')
x=Solver()
x.add(a3 - a4 == 2225223423)
x.add(a4 + a5 == 4201428739)
x.add(a3 - a5== 1121399208)
check=x.check()
print(check)
model=x.model()
print(model)
#sat
#[a5 = 2652626477, a3 = 3774025685, a4 = 1548802262]
得到结果
unsigned int a1[6] = { 3746099070,550153460, 3774025685 ,1548802262 ,2652626477 ,2230518816 };
刚入门逆向的臭毛病是习惯Hide casts隐藏指针以清晰代码方便阅读的,但在本题中,倘若不留意类型而直接隐藏,在IDA窗口中将得到这样的数据:
//未隐藏指针的代码:注意到 v7 应该是一个unsigned int 数组
if ( (unsigned int)sub_400770(v7, a2) != 1 )
{
puts("NO NO NO~ ");
exit(0);
}
显然,这些数据并不是标准的unsigned int类型,在获取这些数据时可以按h变为16进制进行存放
if ( a1[2] - a1[3] == 2225223423LL
&& a1[3] + a1[4] == 4201428739LL
&& a1[2] - a1[4] == 1121399208LL
&& *a1 == -548868226
&& a1[5] == -2064448480
&& a1[1] == 550153460 )
也可以从汇编窗口逐个获取并计算,且存放数组使用相应的类型
.text:00000000004007D0 mov [rbp+var_8], rax
.text:00000000004007D4 mov eax, 84A236FFh
.text:00000000004007D9 cmp [rbp+var_18], rax
.text:00000000004007DD jnz short loc_400845
.text:00000000004007DF mov eax, 0FA6CB703h
.text:00000000004007E4 cmp [rbp+var_10], rax
.text:00000000004007E8 jnz short loc_400845
.text:00000000004007EA cmp [rbp+var_8], 42D731A8h
.text:00000000004007F2 jnz short loc_400845
.text:00000000004007F4 mov rax, [rbp+var_28]
.text:00000000004007F8 mov eax, [rax]
.text:00000000004007FA cmp eax, 0DF48EF7Eh
.text:00000000004007FF jnz short loc_400834
.text:0000000000400801 mov rax, [rbp+var_28]
.text:0000000000400805 add rax, 14h
.text:0000000000400809 mov eax, [rax]
.text:000000000040080B cmp eax, 84F30420h
.text:0000000000400810 jnz short loc_400834
.text:0000000000400812 mov rax, [rbp+var_28]
.text:0000000000400816 add rax, 4
.text:000000000040081A mov eax, [rax]
.text:000000000040081C cmp eax, 20CAACF4h
可以注意到,IDA中并没有为tmp1、tmp2声明变量(实际上,它们本不是这个名字,但为了方便阅读而被我改成了这个名字;从汇编窗口可以知道它们均为4个字节的变量(int))
如下代码展示了LODWORD和HIDWORD的结果,乍一看似乎相当不同,但实际上这不过是一种比较别扭的写法罢了
注意到tmp2的结果和a1[1]相同,而将a1[0]的类型换为int之后也将得到与tmp1相同的结果,也就是说,这两个函数并没有起到任何作用,只是做了简单的赋值罢了
(尽管我想说具体问题具体分析,但倘若使用的是LOBYTE和HIBYTE的话,结果就将彻底不同了。但通常来说,出题人并不会特地去这样写,至少一般来说,并没有LODWORD这样的函数)
unsigned int a1[6] = { 3746099070,550153460, 3774025685 ,1548802262 ,2652626477 ,2230518816 };
int tmp1, tmp2;
tmp1 = LODWORD(a1[0]);// -548868226
tmp2 = HIDWORD(a1[1]);// 550153460
该汇编代码为for循环中对变量 j 的操作
在C伪代码中可以看见为 j++,而在汇编中的结果显然应该是 j+=2,所以过于依赖伪代码的话在编写解密脚本时将遇到麻烦
因此我们可以知道,这个循环每次获取 v6 中的两个进行加密并放入
(应该记得,形参a1为输入流v6,a2为加密表{2,2,3,4}(DWORD类型数组每4字节一个,应将中间的0省略))
分别获取 v3为第一个数组,v4为第二个数字,v5为一个轮替变量
经过一个for循环后,将结果放回原数组
用c++写个脚本
#include<iostream>
using namespace std;
int main()
{
unsigned int a1[6] = { 3746099070,550153460, 3774025685 ,1548802262 ,2652626477 ,2230518816 };
unsigned int table[4] = { 2,2,3,4 };
unsigned int decode[6];
int v5 = 1166789954 * (0x3F+1);
unsigned int v3, v4;
for (int i = 0; i <= 5; i+=2)
{
int v5 = 0x458BCD42 * 64;
v3 = a1[i];
v4 = a1[i + 1];
for (int j = 0; j <= 0x3F; j++)
{
v4 -= (v3 + v5 + 20) ^ ((v3 << 6) + table[2]) ^ ((v3 >> 9) + table[3]) ^ 0x10;
v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + table[0]) ^ ((v4 >> 9) + table[1]) ^ 0x20;
v5 -= 0x458BCD42;
}
decode[i] = v3;
decode[i + 1] = v4;
}
for (int i = 0; i < 6; i++)
{
printf("%x", decode[i]);//666c61677b72655f69735f6772656174217d
}
}
最终得到的decode数组便是flag,但由于VS默认显示为10进制数,所以应该将结果输出为16进制数并另外进行转换
unsigned int decode[6]={6712417, 6781810, 6643561, 7561063, 7497057, 7610749};
知识点:
根据变量类型和输入数据类型判断输入数据在变量中存储方式,如输入的是int 型变量是int64型。
dword可以实现取数据的低四位
HIDWORD函数与LODWORD函数分别取数据的高四位和低四位,HIWORD和LOWORD函数实现取数据的高两位和低两位最后解出来a1数组,还要把数组变成十六进制,一个数据是6位十六进制数,6个数据就是36位16进制数,两个16进制数作为一个scaii可以变成一个字符,36个16进制数就是18个字符
c语言如何实现获取低四位和高四位
public int getHeight4(byte data){//获取高四位
int height;
height = ((data & 0xf0) >> 4);
return height;
}
public int getLow4(byte data){//获取低四位
int low;
low = (data & 0x0f);//0x0f(00001111)
return low;
}