湖湘杯-RE-Writeup

本文最后更新于:2021年11月16日 晚上

Hideit

静态不好分析,直接动态调试找到输入的地方。看到如下加密,findcrypt搜到salsa20。

先进行xxtea解密,在salsa20,因为我没有创建文件,所以输入为空,输入正确的随机数。直接拿到最后的字节流,进行异或即可。

image-20211116195726546

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))
// do
// {
// v7 -= 0x61C88647;
// v8 = (v7 >> 2) & 3;
// v5 += ((v7 ^ v3) + (v6 ^ v13[v8])) ^ (((16 * v6) ^ (v3 >> 3)) + ((v6 >> 5) ^ (4 * v3)));
// v3 += ((v7 ^ v5) + (v5 ^ v13[v8 ^ 1])) ^ (((16 * v5) ^ (v5 >> 3)) + ((v5 >> 5) ^ (4 * v5)));
// v6 = v3;
// --v4;
// }
// while ( v4 );
// if ( v5 == 0x1130BE1B && v3 == 0x63747443 )
// {
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52 / n;
sum = DELTA;
z = v[n - 1];
do
{
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
z = v[p] += MX;
}
y = v[0];
z = v[n - 1] += MX;
//printf("%x\n", y);
sum -= 0x61C88647;
} while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum += 0x61C88647;
} while (--rounds);
}
}

int main()
{
//uint32_t v[2] = {0x1130BE1B, 0x63747443};
//uint32_t v[2] = {0x69746f64, 0x74697374};
//uint32_t s[8] = {0xfa9c936b, 0x254b68eb, 0x1af95485, 0x7bbc8430, 0x92f3ce2c, 0x67ae63fe, 0x18fbe7f3, 0x9332b3a2};
uint32_t const k[4] = {0x72, 0x202, 0x13, 0x13};
int n = 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
// printf("加密前原始数据:%x %x %x %x %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
// btea(v, n, k);
// printf("加密后的数据:%x %x %x %x %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
btea(v, n, k);
printf("解密后的数据:%x %x\n", v[0], v[1]);
return 0;
}


s1 = [0x8D, 0xE2, 0x3D, 0xC2, 0x19, 0xF2, 0x2D, 0xCA, 0x18, 0x14, 0xCF, 0x52, 0x77, 0x5A, 0x9C, 0x13, 0xAA, 0xCC, 0x04, 0x5B, 0x92, 0xC1, 0x0C, 0x68, 0x45, 0x58, 0xF9, 0x47, 0x68, 0xD9, 0x35, 0xC5]
s2 = [0xEB, 0x8E, 0x5C, 0xA5, 0x62, 0xB4, 0x1C, 0x84, 0x5C, 0x59, 0xFC, 0x0D, 0x43, 0x3C, 0xAB, 0x20, 0xD8, 0x93, 0x33, 0x13, 0xA1, 0x9E, 0x39, 0x00, 0x76, 0x14, 0xB5, 0x04, 0x58, 0x9D, 0x06, 0xB8]
flag = ""
for i in range(32):
flag += chr(s1[i] ^ s2[i])
print(flag)

shell

当初学习windows逆向的时候,没好好做笔记,现在全忘了,这才hxb可算是栽了。

main函数有解密代码,解密之后发现是一个pe文件,直接dump出来,但是没什么用,ida加载出错。后来知道是因为区块没有对齐。

image-20211116195810471恢复pe文件,一般都是以0x200对齐,开始我想不通为什么程序在内存中就是0x1000对齐,后来醒悟了。

image-20211116195823975

Virtual offest:就是在内存中相对基址的偏移。

image-20211116195833218

Virtual size:区块在内存中的大小,看ida中这个地址处(140001000+11c6)。

image-20211116195848300

RAW Data offset:文件中的偏移。

image-20211116195856329

RAW size:按照值对齐之后的size。

image-20211116195905059

这里对齐值为0x200,我们修复文件。

image-20211116195914655

在shell程序中会有调试事件,遇到int 3会对输入和0x78进行异或,遇到非法指令则会改变rip,根据rip的值我们在dump出来的程序中进行jmp跳转。

image-20211116195927832

image-20211116195935425

得到伪代码:

image-20211116195942967

1
2
3
4
5
6
7
8
9
10
11
s=[0x1E,0x15,0x1B,0x1C,0x07,0x4D,0x1F,0x1B,0x12,0x17,0x4B,0x44,0x47,0x58,0x12,0x47,0x58,0x58,0x47,0x5F,0x54,0x54,0x58,0x42,0x59,0x57,0x50,0x01,0x49,0x51,0x53,0x57,0x3D,0x6B,0x3E,0x6F,0x3D,0x6D,0x6C,0x3E,0x69,0x2C]
flag = ""
for i in range(len(s)):
flag += chr(s[i] ^ i ^ 0x78)
print(flag)

# Str[i] = ~(~(i & ~(i & Str[i])) & ~(Str[i] & ~(i & Str[i])));
# (i & ~(i & Str[i])) || (Str[i] & ~(i & Str[i]))
# ((i & (~i)) || (i & (~Str[i]))) || ((Str[i] & (~i)) || (Str[i] & (~Str[i])))
# (i & (~Str[i])) || (Str[i] & (~i))
# i ^ Str[i]

challenge

安装llvm12.

1
2
3
4
5
6
llvm-dis-12 challenge #llvm bitcode转为llvm汇编
llc-12 -march=x86-64 challenge -o hello.s #llvm bitcode转为指定架构汇编
lli-12 challenge #运行文件
clang-12 challenge.bc -o hhhh #编译bc文件为可执行文件。

然后可以分析代码并调试,但是程序有的函数很大,反编译失败,可以看到两个提示,找两个函数名。不会正则的我真是废了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
第一个直接IDA python脚本:

name = 0x4054E0 #NuMC68teYH03w9QW
invoke = []
for xref in CodeRefsTo(name,0):
invoke.append(xref-0x20)
print(invoke)
for func in invoke:
i = 0
for xref in CodeRefsTo(func, 0):
i += 1
if i == 4:
print(GetFunctionName(func))
# Oh0o5263yKkJPgNC
1
2
3
4
5
6
7
第二个,半个正则,然后手动,真的是学不会这东西。
根据几种位数的数字,计算110变量有多长。
x = 6
res = (x + 3) * 111 + 2
^[!0-9\s={,]{1000,1001}}$
这样正则出来数据少了,倒是满足了手动。之后看大佬们怎么做的,再记录一下。
#yswT4YyIjOk1vHAo

image-20211116195437727

image-20211116194814382


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!