第一次遇到AVX指令集的题目,感觉比较有意思记录一下

了解VPSHUFB指令

重点是这个说明书

https://www.felixcloutier.com/x86/pshufb

里面介绍了AVX指令集中vpshufb的使用方法(左上Index可以看到所有指令)

当然最重要的部分我先放这了:

1
PSHUFB performs in-place shuffles of bytes in the destination operand (the first operand) according to the shuffle control mask in the source operand (the second operand). 

1
PSHUFB 以第二个操作数为shuffle控制掩码将目标操作数(第一个操作数)中执行以字节为单位的shuffle。该指令排列目标操作数中的数据,使随机掩码不受影响。

那么问题来了

shuffle是什么?

直译是洗牌,当然也有随机播放、改组的意思

在这个地方,我觉得可以理解为“重组”

即将操作数依据某个掩码(mask)进行“重组”操作

因此,我们可以这么理解

1
PSHUFB的作用就是将给到的第一个操作数以第二个操作数为索引进行重组

举个例子

现在我们在有操作数1,写成数组形式为

1
ymm0 = [0x2A, 0x3A, 0x4A]

操作数2

1
ymm1 = [0x01, 0x02, 0x00]

当我们使用VPSHUFB,以ymm2为shuffle control mask时,就会得到这样的结果

1
ymm0 = [0x4A, 0x2A, 0x3A]

可见,ymm0内的数根据ymm1给出的进行了重排,得到了一个原有元素不变但位置变化的数组,并覆盖了原先ymm0内的数

让我们开始做题吧

题目是栢鹭杯2023RE赛道的第一题

相关文件我懒得放了,想学的找个方式联系我我发你(小社工)

第一步,先查壳

(吐槽一下已经很久没见过比赛有壳的了)

好的没壳

第二步,上IDA

没壳就先上IDA吧(32位)

一眼Input code

直接在这F5吧

加载一下VC32_14的签名,然后把printf标注一下可以得到这个

可以看到两个函数sub_415190和sub_4156D0

看看第一个

下滑看到不少常量,应该很有用

然后看看第二个

也是常量,不过按照Little Endian来看,这就是0123…DEF然后循环,应该没什么大作用

那么重点可能就在第一个函数那几个__asm里面了

__asm为内联汇编,也就是说IDA没有分析出来这玩意干什么的,于是直接丢进来了

所以要知道这个玩意干什么用的,就得上动态调试了

第三步,动态调试

使用x32dbg进行动调

(别问我为什么不用OD,OD解析不出来AVX指令集,把我坑惨了)

这里用的是吾爱的itruth,感谢大佬施舍

直接定位关键位置吧

这个就是最重要的函数

随便输点东西先看看能不能走完它

不太友善,直接一个failed

看来得一步步找cmp然后nop掉了

先把爆破狂魔放一边,这种题硬跳肯定出不来flag的

不过便于后续操作,先硬跳一遍看看结果如何

硬跳之后发现给出来的flag似乎是一个hash值

也就是说我们可能只需要解决一个函数即可,后面交由程序生成

(这样就不用敲hash函数力(喜))

先保存下来,去看看到底有什么call

先看第一个call

这个call的数据格式很像在IDA里面看到的常量的格式,于是在内存中跟到这个ebp地址看看得到一个全FF的内容

似乎没什么作用

那么继续走一走

那么这里就有收获了

这就是我们刚刚看到的那些常量,以及一些奇奇怪怪的汇编

由于这些常量应该是有用的,那么我们就可以把他们按照Little Endian拿出来备用

接下来看汇编

值得注意的是三个指令

vpxor、vpshufb和vpcmpeqb

根据字面意思,应该能推断第一和第二个的作用

即异或运算和比较运算

至于中间这个,参考文章开头所述

那么就可以知道,最后一段的数据是用来比较的,第二段的数据是用来重排的,第一段的数据是用来异或的

知道这些那么题目就好做了

这里放上python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = [0xCD, 0xEB, 0x5E, 0x76, 0x0F, 0x22, 0xAF, 0x31, 0x82, 0x92, 0x3C, 0xEF, 0xB8, 0xC1, 0x76, 0x06,
0x18, 0x2F, 0xB5, 0x7D, 0x7F, 0x0A, 0xEA, 0x85, 0x92, 0x00, 0x89, 0xA3, 0x2C, 0xE2, 0xE7, 0x32]
t = [0x0A, 0x07, 0x06, 0x0E, 0x02, 0x0B, 0x03, 0x0D, 0x01, 0x09, 0x00, 0x05, 0x04, 0x0F, 0x08, 0x0C,
0x1B, 0x18, 0x17, 0x1F, 0x14, 0x1E, 0x12, 0x13, 0x15, 0x1D, 0x16, 0x1C, 0x11, 0x10, 0x19, 0x1A]
c = [0x5F, 0x91, 0x99, 0xE8, 0x4E, 0xD0, 0xB0, 0x92, 0xB1, 0x3C, 0x4F, 0xF4, 0x17, 0x76, 0xDA, 0x12,
0x2A, 0x35, 0x01, 0x15, 0xF9, 0x97, 0x5E, 0x19, 0x9D, 0xC2, 0x15, 0x99, 0x70, 0x7D, 0x9F, 0xCC]

cc = [0]*32
for i in range(32):
idx = t[i]
cc[idx] = c[i]

for i in range(32):
cc[i] ^= a[i]

print(bytearray(cc).hex().upper())

最后结果放入源程序中就可以得到flag了