re题(37)BUUCTF-[GWCTF 2019]xxor

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;    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值