C写的CM一枚,XP可跑
本帖最后由 Sweettea 于 2021-4-26 19:44 编辑CM一枚
控制台程序
只有唯一正确解
输入:长度16以内的字符串
输出:成功输出true,失败输出false
无壳,
无暗桩,
无反调试,
有混淆,
有花指令
支持系统:XP - WIN10
破解要求:
爆破,追码,还原算法均可,看各位大佬玩得开心就好
==============以下2020年7月7日 00:54:17编辑======================
感谢大佬们的热心分析!
CM算法是个修改了的base64
此CM经过两次混淆:
第一次是基于C源代码的混淆,控制流平坦,无膨胀和花指令
第二次是编译链接阶段基于汇编代码的混淆,对代码进行了随机膨胀,加入了代码片段之间的随机跳转和花指令
贴上第二次膨胀混淆前的CM可执行文件.
关键函数功能:
sub_4010D0:
unsigned int __stdcall MyStrlen1(const char *string);
递归实现的strlen
sub_4012B0:
char __stdcall Dec1(char value);
单字节解密函数,用于解密base64表和要输出的"true"&"false"字符串
sub_4013A0:
void __stdcall KC_base64_encode(char *pIn, char *pOut);
修改的base64加密函数
sub_402010:
void *__stdcall DecMemcpy(void *dest, const void *src, unsigned int count);
调用了sub_4012B0的memcpy函数
sub_402240:
void __stdcall DoCmp(char *szInput, int (__cdecl *pfnputs)(const char *));
核心验证函数入口
@solly逆向找出的爆破点2:
00404520 85D2 test edx,edx ; test edx,edx
在第二次膨胀混淆前的文件中对应的地址是:
loc_402683:
movzx edx,
test edx, edx
jz short loc_402698
附上第二次混淆等价替换的模版文件:
解码结果:
本帖最后由 solly 于 2020-7-6 02:08 编辑
暴破位置,以下地址改成 xor edx, edx 即可:
00401A17 33D2 xor edx,edx
就是把变换后的输入码与"oPRofQ6uigRhpmwHcYQt"比较时,强行将每个字符比较结果变成相等。 本帖最后由 小菜鸟一枚 于 2020-7-5 12:39 编辑
跑不出来,OD CTRL+A也不太好用,这就是混淆吗?一下子选中三行代码,请问楼主这是c/c加加写的吗,能不能请教一下这个混淆和花指令该怎么做,或者给个教程吧,谢谢!
int sub_401020()
{
__int128 v1; // @1
__int64 v2; // @1
int v3; // @1
v3 = 0;
v1 = 0i64;
_mm_storel_epi64((__m128i *)&v2, 0i64);
sub_401070((int)"Input:", 0);
sub_4010A0("%16s", &v1);
sub_40180B(&v1, sub_41A35B);
return 0;
}
signed int __cdecl sub_41A35B(const char *a1)
{
signed int result; // eax@2
int v2; // eax@3
signed int v3; // eax@4
signed int v4; // edx@4
_BYTE *v5; // esi@4
_BYTE *v6; // eax@6
int *v7; // @14
const char **v8; // @14
int *v9; // @14
unsigned int v10; // @14
int v11; // @3
if ( a1 )
{
v2 = sub_41A1FC(1);
v11 = v2;
if ( (*(_DWORD *)(v2 + 12) >> 12) & 1 )
goto LABEL_17;
v3 = sub_42295B(v2);
v4 = v3;
v5 = &unk_4350F8;
if ( v3 == -1 || v3 == -2 )
v6 = &unk_4350F8;
else
v6 = (_BYTE *)(dword_435F18 + 48 * (v3 & 0x3F));
if ( v6 )
goto LABEL_18;
if ( v4 != -1 && v4 != -2 )
v5 = (_BYTE *)(dword_435F18 + 48 * (v4 & 0x3F));
if ( v5 & 1 )
{
LABEL_18:
*(_DWORD *)sub_421F2E() = 22;
sub_421E72();
result = -1;
}
else
{
LABEL_17:
v10 = strlen(a1);
v7 = &v11;
v8 = &a1;
v9 = (int *)&v10;
result = sub_41A2D1(v11, &v7);
}
}
else
{
*(_DWORD *)sub_421F2E() = 22;
sub_421E72();
result = -1;
}
return result;
}
函数里面调函数,学习了,里面的那个函数有一次参数*a1,用OD跑了下参数也是输入的字符串,里面那个函数两个地方返回-1,应该是错误跳转,LANLE 17可能是正确的流程,然后跟丢了,没什么好的思路:'(weeqw
OD层层回溯,也只能看到在哪里把false入栈的
004274C0 .F64408 28 01test byte ptr ds:,0x1
004274C5 .75 15 jnz short CrackMe.004274DC
004274C7 .E8 62AAFFFF call CrackMe.00421F2E
004274CC .C700 09000000 mov dword ptr ds:,0x9
004274D2 .E8 44AAFFFF call CrackMe.00421F1B
004274D7 .8320 00 and dword ptr ds:,0x0
004274DA .EB 14 jmp short CrackMe.004274F0
004274DC >FF75 10 push dword ptr ss: ;这是字符串false\r\n长度,为7
004274DF .FF75 0C push dword ptr ss: ;这就是字符串false\r\n
解不开了
本帖最后由 Sweettea 于 2020-7-5 13:08 编辑
小菜鸟一枚 发表于 2020-7-5 12:35
跑不出来,OD CTRL+A也不太好用,这就是混淆吗?一下子选中三行代码,请问楼主这是c/c加加写的吗,能不能请 ...
sub_41A35B是c库函数puts,
sub_40180B(&v1, sub_41A35B);
这是将puts函数指针从参数传递进sub_40180B里面调用
咋和c库函数刚上了呢{:1_924:} Sweettea 发表于 2020-7-5 13:05
sub_41A35B是c库函数puts,
sub_40180B(&v1, sub_41A35B);
00401029|.C745 FC 00000>mov ,0x0
00401030|.68 60E14200 push CrackMe.0042E160 ;ASCII "Input:"
00401035|.0F1145 E4 movups dqword ptr ss:,xmm0
00401039|.660fd645 f4 movq qword ptr ss:,xmm0
0040103E|.E8 2D000000 call CrackMe.00401070
00401043|.8D45 E4 lea eax,
00401046|.50 push eax
00401047|.68 68E14200 push CrackMe.0042E168 ;ASCII "%16s"
0040104C|.E8 4F000000 call CrackMe.004010A0
00401051|.83C4 0C add esp,0xC
00401054|.8D45 E4 lea eax,
00401057|.68 5BA34100 push CrackMe.0041A35B
0040105C|.50 push eax
0040105D|.E8 A9070000 call CrackMe.0040180B
好吧,那我分析不出来了,我怎么调试都觉得0040104C 这个call是最大获取16个字符串,接下来只剩下sub_40180B这个call有问题,觉得只能分析它了 花指令和各种跳转看得头疼。。。。感觉得写脚本去一遍,或者静态模拟真实指令的东西才能把sub_40180B搞清楚
印象比较深的大概有
1.
mov reg,addr
leareg,
jmp reg
2.
call offset
offset:
add/sub , idata
retn
好奇楼主是怎么写出来的……是用宏指令完成的吗?求分享,非常好奇
类似的东西在壳里见过。。。不过他们的实现方式基本都是自己调一个反汇编引擎,然后直接代码膨胀/等价混淆再塞进去这样 风扫春残雪 发表于 2020-7-5 14:45
花指令和各种跳转看得头疼。。。。感觉得写脚本去一遍,或者静态模拟真实指令的东西才能把sub_40180B搞清楚 ...
提供下比较简单的思路:
vs编译选项里设置生成.asm文件,编译
对生成的.asm文件进行逐行解析,等价替换
把一个函数内的汇编代码分成n个小片段
每个代码片段之间随机加入跳转代码
每个代码片段随机打乱顺序
ml.exe编译.asm文件
link.exe链接obj
头晕。。。试了很多种方法,最后发现还是得还原回来。。。还是老老实实从下到上分析吧
5#的乱序是最简单的几个,拼好了这些以后还要把一些辣鸡代码给收缩回来。感觉是个大工程……先把乱序拼回来了,吃饭去了
记录几个比较常见的模式(目测至少有十几种模式)
412D36 40834d : mov dword ptr ss:,edx
412D36 408351 : lea esp,dword ptr ss:
412D36 408473 : lea esp,dword ptr ss:
412D36 408477 : mov edx,dword ptr ss:
412D36 40847b : mov ecx,0x71F870
412D36 408480 : sub ecx,0x231ECD
412D36 4084dd : mov dword ptr ss:,ebx
412D36 4084e1 : sub esp,0x4
412D36 4082f9 : pop ebx
412D36 408305 : mov dword ptr ss:,edx
412D36 408309 : sub esp,0x4
412D36 4086b0 : pop edx 风扫春残雪 发表于 2020-7-5 19:31
头晕。。。试了很多种方法,最后发现还是得还原回来。。。还是老老实实从下到上分析吧
5#的乱序是最简单 ...分析得不错。
代码经过膨胀,要还原回来的确是个大工程。膨胀方式大约有个二三十种。
调试观察找出规律,找到爆破点感觉差不多就OK了。要还原算法,个人感觉是有点难了。要追码的话,找到关键点后,密码学数学大佬应该可以推出正确注册码。 本帖最后由 solly 于 2020-7-6 17:59 编辑
暴破2:
00404520 85D2 test edx,edx ; test edx,edx
00404522 0F84 69040000 je CrackMe.00404991
把 je 指令 NOP 掉,这里是字符串比较结果的检查,跳转则显示 false,不跳转则显示 true。
页:
[1]
2