从moectf2025-upx题目里入门链式加密

积累

【套路名称】

链式加密

【触发条件】

一项数据的生成是由其中另一些项决定的

点击展开查看详情

就像一个数列,从左往右依次更新,更新中又有左边或者右边的数据参与(此时就要考虑清楚数据是已处理或是没有被处理)

【操作链路】

  1. 从上往下依次分析代码作用(包括参数的理解)

  2. 之后从下往上,分析“等效”参数,简化理解

    链式异或逆向:先定已知边界明文作锚点,再从最后一个密文往前逐位反推,每一步都依赖前一步已还原的明文。

  3. 写脚本

  4. 查错误

【我的卡壳点】

今天卡在:

fgets函数的理解,它收录了\n

数组元素数没看仔细,有一些没处理的项我直接扔了

例子

ida反编译结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
int __fastcall main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rax
__int64 len; // rcx
__int64 v5; // rax
int table_1; // r9d
__int64 v7; // r8
char v8; // dl
__int128 v10[8]; // [rsp+20h] [rbp-148h]
int v11; // [rsp+A0h] [rbp-C8h]
int v12; // [rsp+A4h] [rbp-C4h]
int v13; // [rsp+A8h] [rbp-C0h]
char v14[48]; // [rsp+B0h] [rbp-B8h]
char Buffer[112]; // [rsp+E0h] [rbp-88h] BYREF

v10[0] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400032E0);
v10[1] = (__int128)_mm_load_si128((const __m128i *)&xmmword_140003310);
v10[2] = (__int128)_mm_load_si128((const __m128i *)&xmmword_140003320);
v10[3] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400032F0);
v10[4] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400032D0);
v10[5] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400032B0);
v10[6] = (__int128)_mm_load_si128((const __m128i *)&xmmword_140003300);
v10[7] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400032C0);
v11 = ')';
v12 = '$';
v13 = 'V';
printf("please input your flag: ");
v3 = _acrt_iob_func(0);
fgets(Buffer, 100, v3);
len = -1i64;
do
++len;
while ( Buffer[len] );
v5 = 0i64;
table_1 = 0;
if ( (int)len > 0 )
{
v7 = 0i64;
do
{
v8 = Buffer[v7] ^ 0x21;
if ( table_1 < (int)len - 1 )
v8 ^= Buffer[v7 + 1];
v14[v7] = v8;
++table_1;
++v7;
}
while ( v7 < (int)len );
}
while ( v14[v5] == *((_DWORD *)v10 + v5) )
{
if ( ++v5 >= 35 ) // v5=34
return 0;
}
printf("you will never get the flag!!!!\n");
return 0;
}

开始第一步上到下理解

找flag,buffer数组改名为flag数组,,第二个do while里面,v8和v7搞不懂意思导致模糊,v7其实就是从0开始一直到长度,那么v8就是对flag全部进行异或0x21,这个table_1是我改的名,他的用意是执行if语句,只要没到倒数第二个字符,v8同时异或0x21和flag的后一个字符,然后用v14逐个进行储存
全部执行完到下一个while,v5又是一个移位,v10数组是这些数据,E0,10,20,F0,D0,B0,00,C0的顺序,但是不知道具体怎么看这个数据
这个while是在比较v14的各项和v10的各项相不相等

第二步下往上整合

那我逆向的想法就是
v14数组等效于v10数组
v7是len,然后dowhile那里从下往上写?table不要了,也是len,但是后面怎么处理呢,一开始是从左往右更新,现在要从右往左逆向,然后呢?或者进行爆破!弄个for从前往后顺

没啥事别爆破,正常思路理算法

第三步写代码

1
2
3
4
5
6
7
8
9
unsigned char flag[35]; int len = 35; // 从最后一个字符开始逆推 
for (int i = len - 1; i >= 0; i--)
{
if (i == len - 1)
{ // 最后一个字符:没有后继 flag[i+1],直接异或还原
flag[i] = target[i] ^ 0x21;
}
else { // 请根据正向公式
v14[i] = flag[i] ^ 0x21 ^ flag[i+1]

写出来这一段,发现乱码

后面才了解是fgets以及数组元素处理的问题

正确代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>
using namespace std;
unsigned char target[35] = {
0x23, 0x2B, 0x27, 0x36, 0x33, 0x3C, 0x03, 0x48,
0x64, 0x0B, 0x1D, 0x76, 0x7B, 0x10, 0x0B, 0x3A,
0x3F, 0x65, 0x76, 0x29, 0x15, 0x37, 0x1C, 0x0A,
0x08, 0x21, 0x3E, 0x3C, 0x3D, 0x16, 0x0B, 0x24,
0x29, 0x24, 0x56
};
int main()
{
char flag[100]={0};
int len=35;
flag[35] = '\n'; // 起点单独放在这里,不放进target
for(int i=len-1;i>=0;i--)
{
flag[i] = target[i] ^ flag[i + 1] ^ 0x21;
}
cout << flag;
}

思考

当时第一次面对这个代码很难受,感觉这些参数都揉在一起,不想开始。

后面耐下性子认真读,发现并没有那么难。

相似题目

CTF2中的pyre

站内搜索

没有找到内容!