拿到程序的第一时间运行一下,熟悉一下程序的流程。这个程序的流程是要我们输入UID和key。用PE工具查一下壳,发现有UPX壳,用ESP定律即可脱壳。验证程序是否脱壳成功,可载入OD看是否能查询到字符串,或载入IDA查看是否有函数或是否可以反编译出伪代码。脱壳后还要保证程序与未脱壳程序执行的流程要一致。
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 ... SetConsoleTitleA ("【2022春节】解题领红包之三" ); sub_403930 ((int )dword_41DDD0, "Input your UID: " ); sub_402800 (&v27); if ( v27 > 2000000 ) { v3 = sub_403930 ((int )dword_41DDD0, "Invalid UID, please input again." ); sub_402660 (10 ); v4 = 0 ; ... } sub_403930 ((int )dword_41DDD0, "Input your Key: " ); sub_403BC0 (&dword_41DE60, &v29); v8 = sub_401100 (v27); v9 = sub_401080 (v27); v25 = sub_401110 (v9); v24 = v8; v28 = &v20; sub_402460 ((int )&v20, (int )&v29); if ( sub_401520 (v20, v21, v22, v23, v24, v25) == 1 ) { v10 = sub_403930 ((int )dword_41DDD0, "Success" ); sub_402660 (10 ); v11 = 0 ; ... } ...
1 2 3 4 int __cdecl sub_401100 (signed int a1) { return a1 % 25 ; }
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 int __cdecl sub_401080 (signed int a1) { int v2; int v3; int v4; int v5; int v6; int v7; int v8; int v9; int v10; int v11; int v12; int v13; v2 = 1 ; v3 = 3 ; v4 = 5 ; v5 = 7 ; v6 = 9 ; v7 = 11 ; v8 = 15 ; v9 = 17 ; v10 = 19 ; v11 = 21 ; v12 = 23 ; v13 = 25 ; return *(&v2 + a1 % 12 ); }
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 char __cdecl sub_401520 (char a1, int a2, int a3, int a4, signed int a5, int a6) { ... v19 = v18; std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(0 ); sub_4034E0 ("flag" , strlen ("flag" )); v23 = v18; std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(0 ); sub_4034E0 (&dword_41A0F8, strlen ((const char *)&dword_41A0F8)); ... sub_402BE0 (&dword_41A0B0, strlen ((const char *)&dword_41A0B0)); v17 = a5; v16 = a6; v51 = &v12; sub_402460 ((int )&v12, (int )&a1); sub_4011B0 ((int )&v50, v12, v13, v14, v15, v16, v17); LOBYTE (v52) = 29 ; v8 = sub_403ED0 ((int )&v50, (int )&v19); LOBYTE (v52) = 28 ; v17 = 1 ; if ( v8 ) { ... return = 0 ; } else { std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(v17); LOBYTE (v52) = 27 ; std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(1 ); LOBYTE (v52) = 26 ; ... std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(1 ); LOBYTE (v52) = 0 ; std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(1 ); v52 = -1 ; std::basic_string<char ,std::char_traits<char >,std::allocator<char >>::_Tidy(1 ); result = 1 ; } return result; }
由于sub_4011B0
里面的算法看伪代码太过复杂,想直观看到key变换成什么,可以用OllyDbg动态调试,直接看比较函数sub_403ED0
出来的与原key比较。
OD动态调试 程序说的UID其实是吾爱论坛的UID,我看大牛们wp时百度了好久UID在哪里查看…
载入OD,输入自己的UID和随便的key,比如hhhhhhhhhhhh。输入完后去到sub_401520
进去,再去到sub_403ED0
下断运行至此处。
1 2 3 4 5 6 7 00401CB2 E8 F9F4FFFF call dumped_.004011B0 00401CB7 83C4 1C add esp,0x1C ;403ED0参数从这里开始 00401CBA 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10] 00401CBE C68424 EC010000>mov byte ptr ss:[esp+0x1EC],0x1D 00401CC6 51 push ecx ;第二个参数入栈v19[]=flag{Happy_New_Year_52Pojie_2022}的首地址 00401CC7 8D8C24 D4010000 lea ecx,dword ptr ss:[esp+0x1D4];第一个参数的首地址在ecx中(v50[]) 00401CCE E8 FD210000 call dumped_.00403ED0
为了验证入栈的是否是v19,可查看堆栈窗口。
1 2 3 4 5 6 7 0012FD44 0012FD58 ;首地址为0012FD58 0012FD48 006B5B28 0012FD4C 00000017 0012FD50 0012FFC0 0012FD54 FFFFFFFF 0012FD58 003200FF ;但这个并不是v19,再下一个地址才是 0012FD5C 00392009 ASCII "flag{Happy_New_Year_52Pojie_2022}"
再找ecx->数据窗口中跟随,同理,第二个地址存放的是比较的数据。
1 0012FF18 00 00 00 00 99 20 39 00 0C 00 00 00 1F 00 00 00 ....?9........
继续数据窗口跟随,我们输入的“h”已经全变为“q”了。说明很有可能是单表替换中的移位密码,位数为9。
1 00392099 71 71 71 71 71 71 71 71 71 71 71 71 00 00 00 00 qqqqqqqqqqqq....
再试多几次验证一下,发现不是移位密码!
happynewyear->qxiizktbztxg,只是普通的单表替换。
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
x
w
v
u
t
s
r
q
p
o
n
m
l
k
j
i
h
g
f
e
u
v
w
x
y
z
A
B
C
D
E
F
G
H
I
J
K
L
M
N
d
c
b
a
z
y
X
W
V
U
T
S
R
Q
P
O
N
M
L
K
O
P
Q
R
S
T
U
V
W
X
Y
Z
0
1
2
3
4
{
_
…
J
I
H
G
F
E
D
C
B
A
Z
Y
0
1
2
3
4
{
_
…
所以flag{Happy_New_Year_52Pojie_2022}
逆过来就是smxr{Qxiiz_Ktb_Ztxg_52Ijopt_2022}
注意,这个只是在我UID为1787123的情况下的替换表,UID不同替换表也不同。我看wp说这其实是仿射密码,也就是替换表与静态分析map[UID % 12]
,UID % 25
其实是有关系的。
会的还是太少了,仿射密码的加解密原理自行百度,这里不想写了,最后附上大牛的注册机:
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 #include <stdio.h> #include <string.h> char const flag[] = "flag{Happy_New_Year_52Pojie_2022}" ;int map [] = {1 , 3 , 5 , 7 , 9 , 11 , 15 , 17 , 19 , 21 , 23 , 25 };int main () { int uid; while (scanf ("%d" , &uid) != EOF) { int A = map [uid % 12 ]; int B = uid % 25 ; char buf[sizeof (flag)]; strcpy (buf, flag); char *p = buf; while (*p) { if ('a' <= *p && *p <= 'z' ) { *p = ((*p - 'a' ) * A + B) % 26 + 'a' ; } else if ('A' <= *p && *p <= 'Z' ) { *p = ((*p - 'A' ) * A + B) % 26 + 'A' ; } ++p; } printf ("%s\n" , buf); } return 0 ; }