Re_0x01

RE_起点

0x01 Reversing-x64Elf-100

exeinfo 64 位 ELF 文件,无壳

IDA 分析

有 main 函数 , 跟进 (反编译)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[264]; // [rsp+0h] [rbp-110h] BYREF
unsigned __int64 v5; // [rsp+108h] [rbp-8h]

v5 = __readfsqword(0x28u);
printf("Enter the password: ");
if ( !fgets(s, 255, stdin) )
return 0LL;
if ( (unsigned int)sub_4006FD(s) )
{
puts("Incorrect password!");
return 1LL;
}
else
{
puts("Nice!");
return 0LL;
}
}

经过一个 pass 的判断后执行 sub_4006FD( )
跟进 sub_4006FD (反编译)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 __fastcall sub_4006FD(__int64 a1)
{
int i; // [rsp+14h] [rbp-24h]
__int64 v3[4]; // [rsp+18h] [rbp-20h]

v3[0] = (__int64)"Dufhbmf";
v3[1] = (__int64)"pG`imos";
v3[2] = (__int64)"ewUglpt";

// v3[3][7]

for ( i = 0; i <= 11; ++i )
{
if ( *(char *)( v3[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) != 1 )
return 1LL;
}
return 0LL;
}

猜测处理之后输出的是 flag

1
2
3
*(char *)(  v3[i % 3] + 2*(i / 3)  ) // v3[i%3][2*(i/3)]
=
*(char *)( i+a1 ) + 1 // a1[i]

编写 exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
key = ["Dufhbmf","pG`imos","ewUglpt"]

flag=""

# *(char *)(  v3[i % 3] + 2*(i / 3)  ) // v3[i%3][2*(i/3)]

# =

# *(char *)( i+a1 ) + 1  // a1[i]

for i in range(12):

    flag += chr(ord( key[i%3][2*int(i/3)] ) - 1 )

print(flag)

0x02 666

ELF 64bit obj. Shared obj file

main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[240];
char v5[240];

memset(s, 0, 0x1EuLL);
printf("Please Input Key: ");
__isoc99_scanf("%s", v5);
encode(v5, s);
if ( strlen(v5) == key )
{
if ( !strcmp(s, enflag) ) // 判断条件 enflag=izwhroz""w"v.K".Ni
puts("You are Right");
else
puts("flag{This_1s_f4cker_flag}"); // fake flag
}
return 0;
}

可以看到条件

1
2
3
4
5
6
__isoc99_scanf("%s", v5);
encode(v5, s);

if ( strlen(v5) == key ) // 比较两个字符串长度

if ( !strcmp(s, enflag) ) // 比较两个字符串的大小

同时跟进 key

1
.data:000004080 key             dd 12h

h 是十六进制 12(16) = 18(10)
所以 key 长度是 18
enflag = izwhroz””w”v.K”.Ni
ASCII 文本,十六进制,二进制,十进制,Base64 转换器 (rapidtables.org)
换算成十进制
enflag = 105 122 119 104 114 111 122 34 34 119 34 118 46 75 34 46 78 105

encode 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// encode(v5, s);
int __fastcall encode(const char *a1, __int64 a2)
{
char v3[104];
int v4;
int i;

i = 0;
v4 = 0;
if ( strlen(a1) != key )
return puts("Your Length is Wrong");
for ( i = 0; i < key; i += 3 ) // 三个一组
{
v3[i + 64] = key ^ (a1[i] + 6);
v3[i + 33] = (a1[i + 1] - 6) ^ key;
v3[i + 2] = a1[i + 2] ^ 6 ^ key;
// 前三个为加密算法
// 后三个为指针赋值
*(_BYTE *)(a2 + i) = v3[i + 64];
*(_BYTE *)(a2 + i + 1LL) = v3[i + 33];
*(_BYTE *)(a2 + i + 2LL) = v3[i + 2];
}
return a2;
}

针对

1
2
3
v3[i + 64] = 18 ^ (a1[i] + 6);
v3[i + 33] = (a1[i + 1] - 6) ^ 18;
v3[i + 2] = a1[i + 2] ^ 6 ^ 18;

a1:一个指向字符数组的指针,表示要加密的字符串。

解密

1
2
3
4
5
6
7
enflag=[105,122,119,104,114,111,122,34,34,119,34,118,46,75,34,46,78,105]
flag=''

for i in range(0,18,3):
flag+=chr((18^enflag[i])-6)
flag+=chr((18^enflag[i+1])+6)
flag+=chr((18^enflag[i+2])^6)

0x03 easyRE1

给了两个附件, x64 分析, 无壳

main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s1[264]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v5; // [rsp+118h] [rbp-8h]

v5 = __readfsqword(0x28u);
puts("What is the password?");
gets(s1);
if ( !strcmp(s1, "the password") )
puts("FLAG:db2f62a36a018bce28e46d976e3f9864");
else
puts("Wrong!!");
return 0;
}

?
这还真是 flag

0x04 lucknum

ELF 64bit obj. Shared obj file

main( )

1
2
3
4
5
6
7
8
9
10
11
12
13
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-34h] BYREF
char s[48]; // [rsp+10h] [rbp-30h] BYREF

strcpy(s, "flag{c0ngr@tul@ti0n_f0r_luck_numb3r}");
v4 = 0;
__isoc99_scanf(&unk_2004, &v4);
if ( v4 == 7 )
return puts(s);
else
return puts("sorry!");
}

不是 哥们
..

0x05 reverse_re3

64 位 无壳

main( )

1
2
3
4
5
6
7
8
9
10
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v4; // [rsp+4h] [rbp-Ch]

sub_11B4(a1, a2, a3);
do
v4 = sub_940();
while ( v4 != 1 && v4 != -1 );
return 0LL;
}

sub_940( )

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
__int64 sub_940()
{
int v0; // eax
int v2; // [rsp+8h] [rbp-218h]
int v3; // [rsp+Ch] [rbp-214h]
char v4[520]; // [rsp+10h] [rbp-210h] BYREF
unsigned __int64 v5; // [rsp+218h] [rbp-8h]

v5 = __readfsqword(0x28u);
v3 = 0;
memset(v4, 0, 0x200uLL);
_isoc99_scanf(&unk_1278, v4, v4);
while ( 1 )
{
do
{
v2 = 0;
sub_86C();
v0 = v4[v3];
if ( v0 == 100 )
{
v2 = sub_E23();
}
else if ( v0 > 100 )
{
if ( v0 == 115 )
{
v2 = sub_C5A();
}
else if ( v0 == 119 )
{
v2 = sub_A92();
}
}
else
{
if ( v0 == 27 )
return 0xFFFFFFFFLL;
if ( v0 == 97 )
v2 = sub_FEC();
}
++v3;
}
while ( v2 != 1 );
if ( dword_202AB0 == 2 )
break;
++dword_202AB0;
}
puts("success! the flag is flag{md5(your input)}");
return 1LL;
}

其中 w a s d = 119 97 115 100
因此 (在 F5 反编译的伪代码里面单击按”R” 就可以快捷更换成 ASCII==>字符 )

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
sub_940(){
while ( 1 ){do
{
if ( v0 == 'd' )
{
v2 = sub_E23(); //向右走
}

else if ( v0 > 'd' )
{
if ( v0 == 's' )
{
v2 = sub_C5A();//向下走
}

else if ( v0 == 'w' )
{
v2 = sub_A92();//向上走
}
}

else
{
if ( v0 == 27 )
return 0xFFFFFFFFLL;
if ( v0 == 'a' )
v2 = sub_FEC();//向左走
}
++v3;
}

while ( v2 != 1 );
if ( dword_202AB0 == 2 )
break;
++dword_202AB0;
}

return 1LL;
}

因此这是一个操控 wasd 移动的函数
研究一下移动函数 sub_E23()

sub_E23( )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 sub_E23()
{
if ( dword_202AB8 != 14 )
{
if ( dword_202020[225 * dword_202AB0 + 1 + 15 * dword_202AB4 + dword_202AB8] == 1 )
{
dword_202020[225 * dword_202AB0 + 1 + 15 * dword_202AB4 + dword_202AB8] = 3;
dword_202020[225 * dword_202AB0 + 15 * dword_202AB4 + dword_202AB8] = 1;
}
else if ( dword_202020[225 * dword_202AB0 + 1 + 15 * dword_202AB4 + dword_202AB8] == 4 )
{
return 1LL;
}
}
return 0LL;
}

这里面涉及了一些东西 我替换一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sub_E23(){
  if ( y != 14 )
  {
    if ( map[225 * num + 1 + 15 * x + y] == 1 )
    {
      map[225 * num + 1 + 15 * x + y] = 3;
      map[225 * num + 15 * x + y] = 1;
    }
    else if ( map[225 * num + 1 + 15 * x + y] == 4 )
    {
      return 1LL;
    }
  }
  return 0LL;
}

好看点了
那么步进这几个变量 看看意义

1
2
3
4
5
6
.data:000202020 dword_202020    dd 5 dup(1), 0Ah dup(0), 5 dup(1), 0, 3, 2 dup(1), 6 dup(0)
.data:000202020 ; DATA XREF: sub_86C+82↑o
.data:000202020 ; sub_A92+76↑o ...
.........
.data:000202A68 dd 1, 0Eh dup(0), 4, 0
.data:000202A68 _data ends

dword_202020:map 其实就是题目给的地图,可以双击进去看看(shift+e 提取数据)

dword_202AB0:num 代表哪个迷宫,此题有 3 个迷宫(至于为什么后文会提及)

dword_202AB4:x 代表行(因为 15*,说明可能是一行 15 个数字,大胆猜测!)

dword_202AB8:y 代表列

225 = 15 * 15
大概是 15×15 的地图

dword 分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sub_E23(){
  if ( y != 14 )
  {
    if ( map[225 * num + 1 + 15 * x + y] == 1 )
    {
      map[225 * num + 1 + 15 * x + y] = 3;
      map[225 * num + 15 * x + y] = 1;
    }
    else if ( map[225 * num + 1 + 15 * x + y] == 4 )
    {
      return 1LL;
    }
  }
  return 0LL;
}
  1. y != 14 只有 15 行, 也就是最多向下位移 14 次
  2. map[225 * num + 1 + 15 * x + y] == 1 判断当前位置右移的数字是不是 1,如果是将该位置标志为 3,之前的位置标志为 1,其实就是暗示我们 1 是可以走的,而 3 其实是我们的(起点)实际位置,如果不理解后面看看迷宫就明白了
  3. map[225 * num + 1 + 15 * x + y] == 4 判断当前位置右移的数字是不是 4, 是 4 就 1LL 结束游戏
  4. 提醒有的题目不一定是只有上下左右四个方向(斜向), 因此可以去看看 sub_C5A() sub_FEC() 是不是也是这个思路
  5. 15 * x 说明每次走都会增加一行

(到这里我觉得大体是这样: 225 个数字连续排列, 当按照 15×15 的格式来 output 就成了一个迷宫图)

现在回到原来的函数

部分 sub_940( )

1
2
3
4
while ( v2 != 1 );
if ( dword_202AB0 == 2 )
break;
++dword_202AB0;

这里的 dword_202AB0==2 我们在前面提到它可能是标识哪一个迷宫的,这里说 ==2 的话就 break,否则就++,说明这里会循环 3 次,说明会有 3 个迷宫,接下来我们来看看这个稍微的迷宫长什么样

步进 dword_202020 (map)

dword_202020

按 shift+e 提取数据,然后导出来 (C unsigned char array decimal)

1
2
3
4
5
6
unsigned char ida_chars[] =
{
1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
...
0, 0, 4, 0, 0, 0, 0, 0, 0, 0
};

这里太长了 中间部分隐藏掉算了
这里 txt 里面替换掉 , 空格之类的, 只保留 01

脚本生成地图

dword 类型的数据是 4 位一组,只取第一位作为数值,也就是说 100010000111 最终会变成 110 ,后 3 位是填充的
(如果提取的是 initialized C variable 就可以避免这一步)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 输入需要处理的字符串
input_string = input("请输入需要处理的字符串:")

# 将所有字符连起来,去掉换行符
output_string = input_string.replace("\n", "")
ZeK1D = output_string.replace(" ", "")

def extract_chars(input_string):
# 将输入字符串分割为每四位
chunks = [input_string[i:i+4] for i in range(0, len(input_string), 4)]

# 提取每四位中的第一位字符
first_chars = [chunk[0] for chunk in chunks if chunk]

# 将字符连在一起并输出
output= ''.join(first_chars)
print(output)

# 调用函数并输出结果
extract_chars(ZeK1D)

(这里输出之后是 3 张地图 )

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
// 3 是起点
111110000000000
111110311000000
111110001000000
111110001000000
111110001111100
111110000000100
111110000000100
111110000000110
111110000000010
111110000000040
111111111111111
111111111111111
111111111111111
111111111111111
111111111111111
// ddsssddddsssdss

// 3 是起点
110000000000000
110311111000000
110110001000000
110000001000000
110110001111100
110110000000100
110110000000100
110110000011110
110110000010010
110110000010000
110111111010110
110111111111110
110000000000040
111111111111111
111111111111111
// dddddsssddddsssaassssddds

// 3 是起点
000000000000000
031100000000000
000101110000000
000111010000000
000010010000000
011010010000000
001110010000000
000000010000000
000000011110000
000000000010000
000000000010000
000000000010000
000000000011110
000000000000010
000000000000040
// ddssddwddssssssdddssssdddss

结果 ddsssddddsssdssdddddsssddddsssaassssdddsddssddwddssssssdddssssdddss
flag 是 md5 加密的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib
def md5_encrypt(input_string):
# 将字符串转换为字节
input_bytes = input_string.encode('utf-8')
# 创建一个md5 hash对象
hash_object = hashlib.md5()
# 向hash对象提供数据
hash_object.update(input_bytes)
# 获取16进制的加密字符串
encrypted_string = hash_object.hexdigest()
return encrypted_string

input_str = "ddsssddddsssdssdddddsssddddsssaassssdddsddssddwddssssssdddssssdddss"
encrypted_str = md5_encrypt(input_str)
print(f"The MD5 encrypted string is: {encrypted_str}")

输出 md5 为 aeea66fcac7fa80ed8f79f38ad5bb953

0x06 1000Click

无壳 x64
卧槽 这个程序这么多文件

Shift + F12 查看字符串
Ctrl + F 搜索 flag 看看
搜到了一大堆?
双击步入任意一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.data:005C4AEF                 align 10h
.data:005C4AF0 aFlagSusvek8ywl db 'flag{SUsVEk8Ywltso}',0
.data:005C4B17 align 4
.data:005C4B18 aFlagLoa77rxsiq db 'flag{LOa77RXSIqZWC}',0
.data:005C4B3F align 10h
.data:005C4B40 aFlagSiwprwdvx7 db 'flag{SIwprwdVx7RXe}',0
.data:005C4B67 align 4
.data:005C4B68 ; CHAR Text[]
.data:005C4B68 Text db 'flag{TIBntXVbdZ4Z9}',0 // real flag
.data:005C4B68 ; DATA XREF: sub_402790+23↑o
.data:005C4B68 ; sub_4027D0:loc_40282A↑o
.data:005C4B8F align 10h
.data:005C4B90 aFlagHilcmjfvtk db 'flag{hILCmjfvtk0hs}',0
.data:005C4BB7 align 4
.data:005C4BB8 aFlagX3rcv2cx2t db 'flag{X3rCv2cx2t18G}',0
.data:005C4BDF align 10h
.data:005C4BE0 aFlagUahcq9fz0d db 'flag{UaHcQ9fz0D8ZX}',0
.data:005C4C07 align 4
.data:005C4C08 aFlagYxvrfjabc5 db 'flag{YXvrfJAbc58tl}',0

发现只有一个 flag 是有数据调用的,猜测是最终的 flag

0x07 crypt

怎么有密码啊 QwQ
RC4

exeinfo : x64 Microsoft Visual C++ v14.26 - 2019 - microsoft.com (exe 4883EC28-48) - no sec. Cab.7z.Zip [ Win Vista ]

PE 控制台程序

main( )

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
unsigned int v4; // eax
void *v5; // rax
void *v7; // rax
int i; // [rsp+24h] [rbp-D4h]
void *v9; // [rsp+28h] [rbp-D0h]
char v10[32]; // [rsp+30h] [rbp-C8h] BYREF
char Str[128]; // [rsp+50h] [rbp-A8h] BYREF

strcpy(Str, "12345678abcdefghijklmnopqrspxyz");
memset(&Str[32], 0, 0x60ui64);
memset(v10, 0, 0x17ui64);
sub_1400054D0("%s", v10);
v9 = malloc(0x408ui64);
v3 = strlen(Str);
sub_140001120(v9, Str, v3);
v4 = strlen(v10);
sub_140001240(v9, v10, v4);
for ( i = 0; i < 22; ++i )
{
if ( ((unsigned __int8)v10[i] ^ 0x22) != byte_14013B000[i] )
{
v5 = (void *)sub_1400015A0(&off_14013B020, "error");
_CallMemberFunction0(v5, sub_140001F10);
return 0;
}
}
v7 = (void *)sub_1400015A0(&off_14013B020, "nice job");
_CallMemberFunction0(v7, sub_140001F10);
return 0;
}

分析 main

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int str_length; // eax
unsigned int myflag_length; // eax
void *v5; // rax
void *v7; // rax
int i; // [rsp+24h] [rbp-D4h]
void *memory; // [rsp+28h] [rbp-D0h]
char myflag[32]; // [rsp+30h] [rbp-C8h] BYREF
char Str[128]; // [rsp+50h] [rbp-A8h] BYREF

strcpy(Str, "12345678abcdefghijklmnopqrspxyz");
memset(&Str[32], 0, 0x60ui64);
memset(myflag, 0, 0x17ui64);
sub_1400054D0("%s", myflag); // 输入字符串
memory = malloc(0x408ui64); // v9应该是一个内存地址,是申请下来的地址
str_length = strlen(Str);
sub_140001120(memory, Str, str_length); // 关键
myflag_length = strlen(myflag);
sub_140001240(memory, myflag, myflag_length); // 关键
for ( i = 0; i < 22; ++i )
{
if ( ((unsigned __int8)myflag[i] ^ 0x22) != main_break[i] )// 加密之后的myflag[i]^0x22=main_break[i]
{
v5 = (void *)sub_1400015A0(&off_14013B020, "error");// 输出错误
_CallMemberFunction0(v5, sub_140001F10);
return 0;
}
}
v7 = (void *)sub_1400015A0(&off_14013B020, "nice job");// 正确
_CallMemberFunction0(v7, sub_140001F10);
return 0;
}

((unsigned __int8)myflag[i] ^ 0x22) != main_break[i]
提取 main_break[ ]数组 (ida_chars[])
(SHITF+e)

1
2
3
4
5
6
unsigned char ida_chars[] =
{
0x9E, 0xE7, 0x30, 0x5F, 0xA7, 0x01, 0xA6, 0x53, 0x59, 0x1B,
0x0A, 0x20, 0xF1, 0x73, 0xD1, 0x0E, 0xAB, 0x09, 0x84, 0x0E,
0x8D, 0x2B, 0x00, 0x00
};

sub_140001120( )

re4_init( )函数

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
sub_140001120(memory, Str, str_length);
__int64 __fastcall sub_140001120(_DWORD *a1, __int64 a2, int a3)
{
__int64 result; // rax
int i; // [rsp+0h] [rbp-28h]
int j; // [rsp+0h] [rbp-28h]
int v6; // [rsp+4h] [rbp-24h]
int v7; // [rsp+8h] [rbp-20h]
int v8; // [rsp+Ch] [rbp-1Ch]
_DWORD *v9; // [rsp+10h] [rbp-18h]

*a1 = 0;
a1[1] = 0;
v9 = a1 + 2;
for ( i = 0; i < 256; ++i )
v9[i] = i;
v6 = 0;
result = 0i64;
LOBYTE(v7) = 0;
for ( j = 0; j < 256; ++j )
{
v8 = v9[j];
v7 = (unsigned __int8)(*(_BYTE *)(a2 + v6) + v8 + v7);
v9[j] = v9[v7];
v9[v7] = v8;
if ( ++v6 >= a3 )
v6 = 0;
result = (unsigned int)(j + 1);
}
return result;
}

这段代码看起来是实现了一种称为 RC4 算法(也称为 ARC4)的流密码算法,用于加密和解密数据。让我们逐步分析它:

函数调用

c 复制代码
sub_140001120(memory, Str, str_length);
这是函数调用,传递了三个参数:

  • memory:可能是一个指向缓冲区的指针,用于存储加密后的结果。
  • Str:一个指向数据的指针,这里用  a2  表示,通常是待加密或解密的数据。
  • str_length:数据的长度,这里用  a3  表示,表示数据的字节数。
函数定义分析
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
__int64 __fastcall sub_140001120(_DWORD *a1, __int64 a2, int a3)
{
__int64 result; // 返回值
int i; // 循环变量
int j; // 循环变量
int v6; // 数据索引变量
int v7; // 临时变量
int v8; // 临时变量
_DWORD *v9; // RC4算法的S盒

// 初始化S盒
*a1 = 0;
a1[1] = 0;
v9 = a1 + 2;
for ( i = 0; i < 256; ++i )
v9[i] = i;

v6 = 0; // 数据索引初始化
result = 0i64; // 返回值初始化为0
LOBYTE(v7) = 0; // 临时变量v7初始化为0

// RC4算法核心循环
for ( j = 0; j < 256; ++j )
{
v8 = v9[j]; // 从S盒中获取一个元素
v7 = (unsigned __int8)(*(_BYTE *)(a2 + v6) + v8 + v7); // 计算临时变量v7
v9[j] = v9[v7]; // 交换S盒中的两个元素
v9[v7] = v8;
if ( ++v6 >= a3 ) // 更新数据索引
v6 = 0;
result = (unsigned int)(j + 1); // 更新返回值
}
return result;
}
分析解释
  1. S 盒初始化
    • a1  是一个指向  _DWORD  类型的指针,用于存储 RC4 算法中的 S 盒。
    • *a1 = 0;  和  a1[1] = 0;  初始化了 S 盒的前两个位置为 0。
    • v9 = a1 + 2;  将  v9  指向 S 盒的起始地址(第三个位置)。
  2. S 盒初始化
    • 使用一个简单的循环将 S 盒中的值初始化为  [0, 1, 2, ..., 255]
  3. 核心循环
    • 使用两个循环变量  j  和  v6  对 S 盒进行操作。
    • v8 = v9[j];  获取 S 盒中的一个值。
    • v7 = (unsigned __int8)(*(_BYTE *)(a2 + v6) + v8 + v7);  计算一个临时变量  v7,其中  a2  是数据的起始地址,v6  是数据的当前索引。
    • v9[j] = v9[v7];  和  v9[v7] = v8;  交换 S 盒中的两个值,根据计算得到的  v7
    • if ( ++v6 >= a3 ) v6 = 0;  更新数据索引  v6,如果超过数据长度  a3  则重新开始。
    • result = (unsigned int)(j + 1);  更新返回值。
总结

这段代码实现了 RC4 算法的主要部分,用于生成一个伪随机的 S 盒,并对数据进行加密或解密。具体的加密和解密过程需要结合其他代码来完整实现。
我们的 key 是 传入的 str = "12345678abcdefghijklmnopqrspxyz"

sub_140001240( )

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
_DWORD *__fastcall sub_140001240(_DWORD *a1, __int64 a2, int a3)
{
_DWORD *result; // rax
int i; // [rsp+0h] [rbp-28h]
int v5; // [rsp+4h] [rbp-24h]
int v6; // [rsp+8h] [rbp-20h]
int v7; // [rsp+Ch] [rbp-1Ch]
int v8; // [rsp+10h] [rbp-18h]
_DWORD *v9; // [rsp+18h] [rbp-10h]

v5 = *a1;
v6 = a1[1];
v9 = a1 + 2;
for ( i = 0; i < a3; ++i )
{
v5 = (unsigned __int8)(v5 + 1);
v7 = v9[v5];
v6 = (unsigned __int8)(v7 + v6);
v8 = v9[v6];
v9[v5] = v8;
v9[v6] = v7;
*(_BYTE *)(a2 + i) ^= LOBYTE(v9[(unsigned __int8)(v8 + v7)]);
}
*a1 = v5;
result = a1;
a1[1] = v6;
return result;
}

这段代码实现了 RC4 算法的一部分,用于对数据进行解密或解码。让我们逐步分析它:

函数定义分析

c 复制代码

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
_DWORD *__fastcall sub_140001240(_DWORD *a1, __int64 a2, int a3)
{
_DWORD *result; // 返回值
int i; // 循环变量
int v5; // 从a1中读取的第一个值
int v6; // 从a1中读取的第二个值
int v7; // 临时变量
int v8; // 临时变量
_DWORD *v9; // RC4算法的S盒

// 从a1中读取初始状态
v5 = *a1;
v6 = a1[1];
v9 = a1 + 2; // 指向S盒的指针

// RC4解密核心循环
for (i = 0; i < a3; ++i)
{
v5 = (unsigned __int8)(v5 + 1); // 更新v5
v7 = v9[v5]; // 从S盒中获取值v7
v6 = (unsigned __int8)(v7 + v6); // 更新v6
v8 = v9[v6]; // 从S盒中获取值v8
v9[v5] = v8; // 交换S盒中的两个值
v9[v6] = v7;
*(_BYTE *)(a2 + i) ^= LOBYTE(v9[(unsigned __int8)(v8 + v7)]); // 对数据解密并异或
}

*a1 = v5; // 更新a1中的值
result = a1;
a1[1] = v6; // 更新a1中的值
return result; // 返回a1
}

分析解释
  1. 变量初始化
    • v5  和  v6  分别从  a1  中读取初始状态,这可能是前一个使用 RC4 算法加密时保存的状态。
    • v9  是指向 RC4 算法的 S 盒的指针,从  a1 + 2  开始。
  2. RC4 解密核心循环
    • for  循环对每个字节进行解密操作,循环次数由  a3  决定,a3  是数据的长度。
    • v5 = (unsigned __int8)(v5 + 1);  和  v6 = (unsigned __int8)(v7 + v6);  分别用来更新  v5  和  v6
    • v7 = v9[v5];  和  v8 = v9[v6];  从 S 盒中取出两个值  v7  和  v8
    • v9[v5] = v8;  和  v9[v6] = v7;  交换 S 盒中的两个值。
    • *(_BYTE *)(a2 + i) ^= LOBYTE(v9[(unsigned __int8)(v8 + v7)]);  对数据进行解密并与原始数据异或,a2  是数据的起始地址。
  3. 状态更新
    • *a1 = v5;  和  a1[1] = v6;  更新  a1  中的状态,以便下次使用时能继续正确解密数据。
总结

这段代码实现了 RC4 算法的解密过程,用来解密通过相同的密钥加密的数据。它通过交换 S 盒中的元素和对数据的异或操作来逆转加密过程,恢复原始数据。

解密

RC4 加密算法的原理及实现_叙述 rc4 原理以及过程-CSDN 博客

分析 main 函数得知:
加密之后的 myflag[i]^0x22=ida_chars[i] 也就是 myflag[i]=ida_chars[i]^0x22`
因此可以得到密文

1
2
3
4
5
6
7
encode_flag=[]
ida_chars=[0x9E, 0xE7, 0x30, 0x5F, 0xA7, 0x01, 0xA6, 0x53, 0x59, 0x1B,0x0A, 0x20, 0xF1, 0x73, 0xD1, 0x0E, 0xAB, 0x09, 0x84, 0x0E,0x8D, 0x2B, 0x00, 0x00]
for i in range(len(ida_chars)):
encode_flag.append(ida_chars[i]^0x22)
print(encode_flag)
# encode_flag 是经过rc4加密后的标准密文
# encode_flag = [188, 197, 18, 125, 133, 35, 132, 113, 123, 57, 40, 2, 211, 81, 243, 44, 137, 43, 166, 44, 175, 9, 34, 34]

因此有了 encodeflag 跟 key

1
2
3
encode_flag = [188, 197, 18, 125, 133, 35, 132, 113, 123, 57, 40, 2, 211, 81, 243, 44, 137, 43, 166, 44, 175, 9, 34, 34]

key = "12345678abcdefghijklmnopqrspxyz"

同时:

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
#include <stdio.h>
#include <string.h>
typedef unsigned longULONG;

/*初始化函数*/
void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len)
{
    int i = 0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++)
    {
        s[i] = i;
        k[i] = key[i % Len];
    }
    for (i = 0; i < 256; i++)
    {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j]; // 交换s[i]和s[j]
        s[j] = tmp;
    }
}

/*加解密*/
void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len)
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
   
    for (k = 0; k < Len; k++)
    {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j]; // 交换s[x]和s[y]
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] ^= s[t];
    }
}

int main()
{
    unsigned char s[256] = {0}, s2[256] = {0}; // S-box
    char key[256] = {"12345678abcdefghijklmnopqrspxyz"};
    char pData[] = {0xbc, 0xc5, 0x12, 0x7d, 0x85, 0x23, 0x84, 0x71, 0x7b, 0x39, 0x28, 0x2, 0xd3, 0x51, 0xf3, 0x2c, 0x89, 0x2b, 0xa6, 0x2c, 0xaf, 0x9, 0x22, 0x22}; // 十六进制
   
    unsigned long len = strlen(pData);
    int i;
   
    printf("pData=%s\n", pData);
    printf("key=%s,length=%d\n\n", key, strlen(key));
   
    rc4_init(s, (unsigned char *)key, strlen(key)); // 已经完成了初始化
    printf("\n\n");
    for (i = 0; i < 256; i++) // 用s2[i]暂时保留经过初始化的s[i],很重要的!!!
    {
        s2[i] = s[i];
    }
    // 可以看到,加解密函数都是相同的
    printf("已经加密,现在解密:\n\n");
    rc4_crypt(s2, (unsigned char *)pData, len); // 解密
    printf("pData=%s\n\n", pData);
    return 0;
}       # 解密方法

pData=flag{nice_to_meet_you}nm

Re+Crypto 对我还是太上强度了点 (