软件界面:
经测试得知,序列号格式为xxxx-xxxx-xxxx-xxxx
构建测试序列号: 1111-2222-3333-4444
断在MessageBoxW出,栈回溯找调用点
运气不错直接找到了正确提示处,关键跳转为: 0078EE96 jne xxx,
往上翻翻可以看到,很有规律的比较,这里很大可能是比较序列号是否正确的地方
找到我们输入的序列号,首次出现位置: 0078EB0A,字符串地址:0028ABE8,在字符串处下硬件访问断点,直接运行
断在此处:0078454E
下方可以看到有一处访问了常量: 0xF0BB18
里面存放了一串有规则的字符串,先保留下来: ABJDEFGHICKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz+/
需要注意到函数内部使用了byte_0xF0BB18数组,是我们第5步找到的固定字符串常量
可以看到确实有点像加密,有3个参数,函数返回的值是一个长度
弄清楚3个参数是什么:
参数1: 我们输入的序列号字符串地址
参数2: 存放加密后的字符串缓冲区
参数3: 0x13(是序列号的长度,固定的)
得到加密后的序列号: MTEnMSqoM9IoLTMpMpMjNDQqNA==
疑似Base64编码,但经测试并非Base64
正确序列号比较的字符串与第6步得到的加密序列号有一点不同
第6步得到的加密序列号: MTEnMSqoM9IoLTMpMpMjNDQqNA==
进行比较的序列号: MUGmIVwhE0Cd@YCMpMjNDQqNA== (中间有一个空格)
仔细分析发现前16个字符又被加密了一次,多次测试后发现二次加密的地址的固定的
记住被二次加密的序列号缓冲区地址: 00F0E37C,重新运行一次,观察缓冲区状况,发现是在0078ECE6处的函数里面进行了二次加密
使用IDA静态分析此函数
函数内部有大量的混淆代码,由于之前已经知道二次加密只操作了16字节,观察到下方有一个比较像加密的,经解密测试后,其余都是混淆代码,无实际意义
MUTn]=tbR^db@U@~3WQjMTInM6==
有两处加密,逆向思维,应该先解密第二次加密
传入第8步得到的正确序列号解密前16个字节得到:
MTVmY8reZWniLXNq3WQjMTInM6==
这一步需要详细分析一下之前IDA静态分析的函数内部实现,这里讲一下关键的几个点:
1.原来的加密:
每次取3个字符进行加密,加密后生成4个字符
最后1个字符特殊加密处理成了"=="
有一处加密没有用上
2.暴力枚举解密:
逆向思维: 取4个字符解密成3个字符
最后一个字符: 加密序列长度28 可以整除4,所以只能取最后4个字符解密得到最后一个字符
传入上一步解密了前16字节的字符串得到:
15pb-hell-stud-1212
测试结果:
完美!
总结:
此次逆向是第一次尝试反推演序列号,过程的艰辛难以言表.
对于初学者来说,至少对我来说,有好几次都想放弃,几个小时的分析,却没有一点收获,觉得大概是不行了...
虽然过后感觉此程序逆向难度确实没有想象中难,可是当局者迷啊
但万幸还是坚持下来了,此次逆向对我逆向能力的提升有很大的帮助!
此次逆向的要点:
1.先观察错误提示,找到提示处,栈回溯,看看能不能找到提示成功点
2.找到成功点后,往上回溯,观察关键跳转,运气好可以直接找到比较处
3.如果有输入,找到我们输入的字符串缓冲区,下硬件访问断点
附件已经上传
void sub_78A810(BYTE
*
a1)
{
for
(
int
j
=
0
; j <
16
;
+
+
j)
/
/
加密前
16
个字节
a1[j] ^
=
j;
}
void sub_78A810(BYTE
*
a1)
{
for
(
int
j
=
0
; j <
16
;
+
+
j)
/
/
加密前
16
个字节
a1[j] ^
=
j;
}
void Decrypt(BYTE
*
a)
{
int
len
=
0x1C
;
/
/
加密后的序列号长度
0x1C
=
=
28
while
(
len
>
0
)
{
if
(
len
=
=
4
)
{
for
(
int
i
=
0
; i <
0x7F
; i
+
+
)
{
if
(byte_F0BB18[(i >>
2
) &
0x3F
]
=
=
a[
0
])
{
if
(byte_F0BB18[
16
*
(i &
3
)]
=
=
a[
1
])
{
printf(
"%c"
, i);
break
;
}
}
}
}
for
(
int
i
=
0
; i <
0x7F
; i
+
+
)
{
if
(byte_F0BB18[(i >>
2
) &
0x3F
]
=
=
a[
0
])
/
/
1
M
{
for
(
int
j
=
0
; j <
0x7F
; j
+
+
)
{
if
(byte_F0BB18[(j >>
4
) |
16
*
(i &
3
)]
=
=
a[
1
])
/
/
2
T
{
for
(
int
k
=
0
; k <
0x7F
; k
+
+
)
{
if
(byte_F0BB18[(k >>
6
) |
4
*
(j &
0xF
)]
=
=
a[
2
])
/
/
3
E
{
if
(byte_F0BB18[k &
0x3F
]
=
=
a[
3
])
{
printf(
"%c%c%c"
, i, j, k);
break
;
}
}
}
}
}
}
}
a
+
=
4
;
len
-
=
4
;
}
}
void Decrypt(BYTE
*
a)
{
int
len
=
0x1C
;
/
/
加密后的序列号长度
0x1C
=
=
28
while
(
len
>
0
)
{
if
(
len
=
=
4
)
{
for
(
int
i
=
0
; i <
0x7F
; i
+
+
)
{
if
(byte_F0BB18[(i >>
2
) &
0x3F
]
=
=
a[
0
])
{
if
(byte_F0BB18[
16
*
(i &
3
)]
=
=
a[
1
])
{
printf(
"%c"
, i);
break
;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-5-5 09:12
被wx_断浪天涯编辑
,原因: 上传附件