0x01 前言
团队逆向牛的解题思路,分享出来~
0x02 内容
0.
样本
bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll
1.
静态分析
使用IDAPro
逆向分析样本,样本较小,得到方法列表:
调用关系:
PS:
开始受调用关系误导,分析DLL入口函数调用的几个方法,浪费了时间。
查看内部字符串:
根据yes!
定位到方法
get_string
get_string:
调用关系图,有解密过程调用。
使用IDAPro F5
功能,生成伪码:
大致过程:
1)
获取unk_6E282000
处存储的
0x19
长字节流
2)
用户输入字符串
3)
使用encrypt_str
加密用户输入字符串
4)
加密结果与1
)处
0x19
字节相同,则输入的串为可能的
Flag
。
encrypt_str
伪码:
大致过程:
1) v4
基本上为固定值
0,
原因自己看
2)
输入串长度大于2,
并且只有偶数个字节参与运算, v7
为终止哨兵,其值为
a1+2*result, a1
为串首地址,result
为串长度的
1/2
取整。
3) v6
从首地址开始,每循环一次,跳
2
个字节。
encrypt
伪码:
大致过程:
1)
伪码中参数列表,重点看a3
参数,从
encrypt_str
调用处可看出,
a3
为指向用户输入串中的某个位置,调用一次,前进
2
位
2)
重点看:
此处取了 *a3
和
*(a3 + 1)
的值
此处回写了 *a3
和 *(a3 + 1)
的值
这块是暴力破解的关键。
PS:
可以看出,加密过程是通过一系列复杂的运算来实现,一般算法很难从结果逆推出原始串,先从暴力破解入手。
2.
动态分析
样本为DLL
,导出方法get_string
,需要编写
Load
程序。
为简化调试过程,Load
程序模拟get_string
流程,不需
scanf
控制台输入,主要代码如下:
typedef int(*encrypt_str)(uint8_t*, uint32_t, int*);
auto h = LoadLibrary("bbcdd1f7-9983-4bf4-9fde-7f77a6b947b4.dll");
encrypt_str f = (encrypt_str)((uint8_t*)h + 0x67C + 0xC00);
int v1 = 0xEFBEADDE;
char testStr[] = "0123456789abcdefgh";
f((uint8_t *)testStr, strlen(testStr)+1, &v1);
注意:v1
的值问题,
IDA
翻译的伪码未识别出v1
的类型,从汇编可以看出,
v1
是
4
字节
Int
,0xEFBEADDE
对应伪码中
-34.-83,-66.-17
OD
调试分析步骤:
1)
使用OD
启动编译完的程序
2) bp encrypt_str
下断点
OD
断在
查看寄存器信息:
可知 0073FDD0
处存储待加密的字符串,
OD
数据跟随:
单步步过一次encrypt
调用后,数据区:
前两个字节数据变化,基本肯定,加密方法每次处理2
字节数据,且加密后数据长度不发生变化。
PS:
上述过程原理上只能得出每次只生成两字节加密数据,不能证明加密过程也只有2字节参与,证明方法:可以在数据区按字节下内存读断点,看在encrypt过程中,是否只命中两个字节的读断点。
3.
破解方法
1)
取到dll
中加密后的串,即
get_string
中unk_6E282000
处0x19 Bytes
数据。有效数据长度
24
。
2)
每两字节一组,穷举0x0-0xFF
,共
256*256
种可能,调用
encrypt,
结果与unk_6E282000
对应位置数据比较,一致时找到2Bytes
。
3)
重复过程2) 12
次。
4)
由于算法中,每两字节一组独立运行,暴力枚举量从: 256^24
缩小到
256^2*12 = 786432
,运算时间10
秒以内。
核心代码如下:
uint8_t str[25] = { 0 };[/align] for (int n = 0; n < 25; n+=2)
{
for (int i = 0; i <= 0xFF; i++)
for (int j = 0; j <= 0xFF; j++)
{
uint8_t *data = new uint8_t[25];
memcpy(data, str, 25);
*(data + n) = (uint8_t)i;
*(data + n + 1) = (uint8_t)j;
f(data, 25, &v1);
if (data[n] == pstr[n] && data[n+1] == pstr[n+1]) {
str[n] = i;
str[n + 1] = j;
break;
}
}
}
printf("%s\n", str);
f(str, 25, &v1);
if (memcmp(str, pstr, 25) == 0)
{
printf("It's OK!\n");
}
结果: