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 ", str); f(str, 25, &v1); if (memcmp(str, pstr, 25) == 0) { printf("It's OK! "); }
结果:
0x01 前言
团队逆向牛的解题思路,分享出来~
0x02 内容
结果:
团队逆向牛的解题思路,分享出来~
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 ", str); f(str, 25, &v1); if (memcmp(str, pstr, 25) == 0) { printf("It's OK! "); }
结果: