参考
160个CrackMe破解思路合集
(https://bbs.kanxue.com/thread-214024.htm)
工具和环境
环境:win10 x64 chs
工具:x64dbg IDA BinaryNinja
编译器:gcc
大体扫一眼
◆看图标和UI应该是mfc的。
◆类型应该是Name/Serail类型的。
◆DIE跑一下:UPX的壳。
◆简单输入Name和Serial测试下,有弹窗。
脱壳
那些在xp上好有用的工具都不太兼容win10,魔改版的LoadPE + ImportREC 修复的exe时灵时不灵的。
还好x64dbg仅有的一个插件解决了这个问题。
x64dbg + esp定律找到OEP,使用x64dbg自带的Scylla,三下五除二搞定。
找算法函数
第一种办法就是 IDA根据导入表MessageBox函数,交叉引用找到算法。
第二种就是通过OD或者x64dbg动态调试,弹出对话框后,暂停进程,调用栈回溯,也能找到算法函数。
这里我就不过多赘述。
分析
直接拖入IDA,通过导入表MessageBoxA的交叉引用找到4处引用,2个函数。
第一个函数是MFC封装的MsgBox函数,根据它的交叉引用很快找到了数据处理和比较的地方。
封装的MsgBox。
int __thiscall sub_4100A8(_DWORD *this, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
const CHAR *v4; // eax
HWND v6; // ecx
v4 = lpCaption;
if ( !lpCaption )
v4 = (const CHAR *)*((_DWORD *)AfxGetModuleState() + 4);
if ( this )
v6 = (HWND)this[7];
else
v6 = 0;
return MessageBoxA(v6, lpText, v4, uType);
}
根据MsgBox交叉引用,找到处理函数。
UPX0:00401C20 sub esp, 608h
UPX0:00401C26 push ebx
UPX0:00401C27 push ebp
UPX0:00401C28 push esi
UPX0:00401C29 mov ebp, ecx
UPX0:00401C2B push edi
UPX0:00401C2C mov ecx, 7Dh ; '}'
UPX0:00401C31 xor eax, eax
UPX0:00401C33 lea edi, [esp+3Ch]
UPX0:00401C37 rep stosd
UPX0:00401C39 mov ecx, 7Dh ; '}'
UPX0:00401C3E lea edi, [esp+424h]
UPX0:00401C45 rep stosd
UPX0:00401C47 mov ecx, 7Dh ; '}'
UPX0:00401C4C lea edi, [esp+230h]
UPX0:00401C53 rep stosd
UPX0:00401C55 lea eax, [esp+3Ch]
UPX0:00401C59 push 64h ; 'd'
UPX0:00401C5B push eax
UPX0:00401C5C lea ecx, [ebp+5Ch]
UPX0:00401C5F call MfcGetText ; 获取Name的字符串 name = esp + 3c
UPX0:00401C64 lea ecx, [esp+424h]
UPX0:00401C6B push 64h ; 'd'
UPX0:00401C6D push ecx
UPX0:00401C6E lea ecx, [ebp+0ACh]
UPX0:00401C74 call MfcGetText ; 获取Code的字符串存在 esp + 0x424
UPX0:00401C79 lea edi, [esp+3Ch]
UPX0:00401C7D or ecx, 0FFFFFFFFh
UPX0:00401C80 xor eax, eax
UPX0:00401C82 lea edx, [esp+230h]
UPX0:00401C89 repne scasb
UPX0:00401C8B not ecx ; 求name长度
UPX0:00401C8D sub edi, ecx
UPX0:00401C8F mov eax, ecx
UPX0:00401C91 mov esi, edi
UPX0:00401C93 mov edi, edx
UPX0:00401C95 shr ecx, 2
UPX0:00401C98 rep movsd ; name复制到esp+230h
UPX0:00401C9A mov ecx, eax
UPX0:00401C9C and ecx, 3
UPX0:00401C9F rep movsb
UPX0:00401CA1 lea ecx, [esp+230h]
UPX0:00401CA8 push ecx
UPX0:00401CA9 call __strrev ; 将name反转
UPX0:00401CAE lea edi, [esp+234h]
UPX0:00401CB5 or ecx, 0FFFFFFFFh
UPX0:00401CB8 xor eax, eax
UPX0:00401CBA add esp, 4
UPX0:00401CBD repne scasb
UPX0:00401CBF not ecx ; 求反转后长度
UPX0:00401CC1 sub edi, ecx
UPX0:00401CC3 lea edx, [esp+3Ch]
UPX0:00401CC7 mov esi, edi
UPX0:00401CC9 mov ebx, ecx
UPX0:00401CCB mov edi, edx
UPX0:00401CCD or ecx, 0FFFFFFFFh
UPX0:00401CD0 repne scasb
UPX0:00401CD2 mov ecx, ebx
UPX0:00401CD4 dec edi
UPX0:00401CD5 shr ecx, 2
UPX0:00401CD8 rep movsd ; strcat(name,strrev(name))
UPX0:00401CDA mov ecx, ebx
UPX0:00401CDC lea eax, [esp+18h]
UPX0:00401CE0 and ecx, 3
UPX0:00401CE3 push eax
UPX0:00401CE4 push offset aSoftwareMicros ; "SOFTWARE\Microsoft\Windows\CurrentVe"...
UPX0:00401CE9 push 80000002h
UPX0:00401CEE rep movsb
UPX0:00401CF0 call ds:RegOpenKeyA ; 打开注册表
UPX0:00401CF6 mov ebx, ds:RegQueryValueExA ; 查询ProductID
UPX0:00401CFC lea ecx, [esp+14h]
UPX0:00401D00 lea edx, [esp+230h]
UPX0:00401D07 push ecx
UPX0:00401D08 mov ecx, [esp+1Ch]
UPX0:00401D0C lea eax, [esp+14h]
UPX0:00401D10 push edx
UPX0:00401D11 push eax
UPX0:00401D12 push 0
UPX0:00401D14 push offset aProductid ; "ProductID"
UPX0:00401D19 mov dword ptr [esp+24h], 1
UPX0:00401D21 mov dword ptr [esp+28h], 100h
UPX0:00401D29 push ecx
UPX0:00401D2A call ebx ; RegQueryValueExA ; 查询ProductID
UPX0:00401D2C lea edi, [esp+230h]
UPX0:00401D33 or ecx, 0FFFFFFFFh
UPX0:00401D36 xor eax, eax
UPX0:00401D38 lea edx, [esp+3Ch]
UPX0:00401D3C repne scasb
UPX0:00401D3E not ecx
UPX0:00401D40 sub edi, ecx
UPX0:00401D42 mov dword ptr [esp+10h], 1
UPX0:00401D4A mov esi, edi
UPX0:00401D4C mov edi, edx
UPX0:00401D4E mov edx, ecx
UPX0:00401D50 or ecx, 0FFFFFFFFh
UPX0:00401D53 repne scasb
UPX0:00401D55 mov ecx, edx
UPX0:00401D57 dec edi
UPX0:00401D58 shr ecx, 2
UPX0:00401D5B rep movsd ; 将查询的字符串strcat到name
UPX0:00401D5D mov ecx, edx
UPX0:00401D5F lea eax, [esp+14h]
UPX0:00401D63 and ecx, 3
UPX0:00401D66 push eax
UPX0:00401D67 mov eax, [esp+1Ch]
UPX0:00401D6B lea edx, [esp+14h]
UPX0:00401D6F rep movsb
UPX0:00401D71 lea ecx, [esp+234h]
UPX0:00401D78 mov dword ptr [esp+18h], 100h
UPX0:00401D80 push ecx
UPX0:00401D81 push edx
UPX0:00401D82 push 0
UPX0:00401D84 push offset aRegisteredowne ; "RegisteredOwner"
UPX0:00401D89 push eax
UPX0:00401D8A call ebx ; RegQueryValueExA ; 查询RegisteredOwner
UPX0:00401D8C lea edi, [esp+230h]
UPX0:00401D93 or ecx, 0FFFFFFFFh
UPX0:00401D96 xor eax, eax
UPX0:00401D98 lea edx, [esp+3Ch]
UPX0:00401D9C repne scasb
UPX0:00401D9E not ecx ; 求注册表查询字符串长度
UPX0:00401DA0 sub edi, ecx
UPX0:00401DA2 mov esi, edi
UPX0:00401DA4 mov ebx, ecx
UPX0:00401DA6 mov edi, edx
UPX0:00401DA8 or ecx, 0FFFFFFFFh
UPX0:00401DAB repne scasb
UPX0:00401DAD mov ecx, ebx
UPX0:00401DAF dec edi
UPX0:00401DB0 shr ecx, 2
UPX0:00401DB3 rep movsd
UPX0:00401DB5 mov ecx, ebx
UPX0:00401DB7 and ecx, 3
UPX0:00401DBA rep movsb ; 将查询的字符串strcat到name
UPX0:00401DBC lea edi, [esp+3Ch] ; 由于win10缺少这几个注册表项,其得到的name= name + strrev(name) +strrev(name) + strrev(name)
UPX0:00401DC0 or ecx, 0FFFFFFFFh ; 如果xp下应该是 name + strrev(name) + ProducID(查询得到字符串) + RegisterOwner(查询的字符串)
UPX0:00401DC3 repne scasb
UPX0:00401DC5 not ecx ; 求组合后的长度
UPX0:00401DC7 lea eax, [esp+2Ch]
UPX0:00401DCB dec ecx
UPX0:00401DCC push eax
UPX0:00401DCD mov esi, ecx
UPX0:00401DCF call Md5InitState ; 初始化md5的4个state
UPX0:00401DD4 lea edx, [esp+esi+40h]
UPX0:00401DD8 mov ecx, 13h
UPX0:00401DDD xor eax, eax
UPX0:00401DDF mov edi, edx
UPX0:00401DE1 rep stosd ; 清空name后面19 * 4 字节
UPX0:00401DE3 add esp, 4
UPX0:00401DE6 inc esi
UPX0:00401DE7 stosw
UPX0:00401DE9 mov ecx, esi
UPX0:00401DEB stosb
UPX0:00401DEC and ecx, 3Fh ; 对齐数据64
UPX0:00401DEF mov eax, 40h ; '@' ; 对齐数据64
UPX0:00401DF4 sub eax, ecx ; 对齐数据64
UPX0:00401DF6 mov byte ptr [edx], 80h ; '€' ; name最后结尾赋值0x80
UPX0:00401DF9 cmp eax, 7 ; 对齐数据64
UPX0:00401DFC jg short loc_401E01
UPX0:00401DFE add eax, 40h ; '@' ; 对齐数据64
UPX0:00401E01
UPX0:00401E01 loc_401E01: ; CODE XREF: UPX0:00401DFC↑j
UPX0:00401E01 add esi, eax
UPX0:00401E03 lea edi, [esp+3Ch]
UPX0:00401E07 or ecx, 0FFFFFFFFh
UPX0:00401E0A xor eax, eax
UPX0:00401E0C repne scasb
UPX0:00401E0E not ecx
UPX0:00401E10 dec ecx
UPX0:00401E11 xor edi, edi
UPX0:00401E13 shl ecx, 3 ; 长度*8
UPX0:00401E16 test esi, esi
UPX0:00401E18 mov [esp+esi+34h], ecx ; 添加长度数据
UPX0:00401E1C jle short loc_401E37
UPX0:00401E1E
UPX0:00401E1E loc_401E1E: ; CODE XREF: UPX0:00401E35↓j
UPX0:00401E1E lea edx, [esp+2Ch]
UPX0:00401E22 lea eax, [esp+edi+3Ch]
UPX0:00401E26 push edx
UPX0:00401E27 push eax
UPX0:00401E28 call Md5Transform ; md5Transform
UPX0:00401E2D add edi, 40h ; '@' ; 递增64字节
UPX0:00401E30 add esp, 8
UPX0:00401E33 cmp edi, esi
UPX0:00401E35 jl short loc_401E1E ; for(i=0;i < esi(对齐长度);i +=64)
UPX0:00401E37
UPX0:00401E37 loc_401E37: ; CODE XREF: UPX0:00401E1C↑j
UPX0:00401E37 mov ebx, [esp+2Ch]
UPX0:00401E3B lea ecx, [esp+28h]
UPX0:00401E3F lea edx, [esp+24h]
UPX0:00401E43 push ecx
UPX0:00401E44 lea eax, [esp+24h]
UPX0:00401E48 push edx
UPX0:00401E49 lea ecx, [esp+24h]
UPX0:00401E4D push eax
UPX0:00401E4E push ecx
UPX0:00401E4F lea edx, [esp+434h]
UPX0:00401E56 and ebx, 0FFFFh ; 将state[0]的高位抹去
UPX0:00401E5C push offset aLxLxLxLx ; "%lx%lx%lx%lx"
UPX0:00401E61 push edx
UPX0:00401E62 mov [esp+44h], ebx
UPX0:00401E66 call _sscanf ; 从code字符串中获取4个UINT数据
UPX0:00401E6B add esp, 18h
UPX0:00401E6E cmp eax, 4 ; 不够4个就报错
UPX0:00401E71 jz short loc_401E91
UPX0:00401E73 push 30h ; '0'
UPX0:00401E75 push offset aFailed ; "Failed"
UPX0:00401E7A push offset aHmmmYouDonTEve ; "... Hmmm, you don't even pass the first"...
UPX0:00401E7F mov ecx, ebp
UPX0:00401E81 call MfcMsgBox
UPX0:00401E86 pop edi
UPX0:00401E87 pop esi
UPX0:00401E88 pop ebp
UPX0:00401E89 pop ebx
UPX0:00401E8A add esp, 608h
UPX0:00401E90 retn
UPX0:00401E91 ; ---------------------------------------------------------------------------
UPX0:00401E91
UPX0:00401E91 loc_401E91: ; CODE XREF: UPX0:00401E71↑j
UPX0:00401E91 xor esi, esi
UPX0:00401E93 lea edi, [esp+1Ch]
UPX0:00401E97
UPX0:00401E97 loc_401E97: ; CODE XREF: UPX0:00401EB3↓j
UPX0:00401E97 mov eax, 0BADC0DEh ; for(i=0;i < 3;i++)
UPX0:00401E9C lea ecx, [esi+50h]
UPX0:00401E9F cdq
UPX0:00401EA0 idiv ecx ; loop_cnt = 0xbadc0de / (i + 80)
UPX0:00401EA2 push eax
UPX0:00401EA3 push edi
UPX0:00401EA4 call Encode ; 算法关键:将4个UINT做3轮ENcode
UPX0:00401EA9 add esp, 8
UPX0:00401EAC inc esi
UPX0:00401EAD add edi, 4
UPX0:00401EB0 cmp esi, 3
UPX0:00401EB3 jl short loc_401E97 ; for(i=0;i < 3;i++)
UPX0:00401EB5 xor eax, eax
UPX0:00401EB7
UPX0:00401EB7 loc_401EB7: ; CODE XREF: UPX0:00401EC9↓j
UPX0:00401EB7 mov edx, [esp+eax+1Ch]
UPX0:00401EBB mov ecx, [esp+eax+2Ch]
UPX0:00401EBF cmp edx, ecx
UPX0:00401EC1 jnz short loc_401EE9
UPX0:00401EC3 add eax, 4
UPX0:00401EC6 cmp eax, 10h
UPX0:00401EC9 jl short loc_401EB7
UPX0:00401ECB push 40h ; '@'
UPX0:00401ECD push offset aWelcome ; "Welcome"
UPX0:00401ED2 push offset aManYouReGoodEn ; "... Man, you're good enough to join COR"...
UPX0:00401ED7 mov ecx, ebp
UPX0:00401ED9 call MfcMsgBox
UPX0:00401EDE pop edi
UPX0:00401EDF pop esi
UPX0:00401EE0 pop ebp
UPX0:00401EE1 pop ebx
UPX0:00401EE2 add esp, 608h
UPX0:00401EE8 retn
UPX0:00401EE9 ; ---------------------------------------------------------------------------
UPX0:00401EE9
UPX0:00401EE9 loc_401EE9: ; CODE XREF: UPX0:00401EC1↑j
UPX0:00401EE9 push 30h ; '0'
UPX0:00401EEB push offset aFailed ; "Failed"
UPX0:00401EF0 push offset aBetterLuckNext ; "... Better luck next time ..."
UPX0:00401EF5 mov ecx, ebp
UPX0:00401EF7 call MfcMsgBox
UPX0:00401EFC pop edi
UPX0:00401EFD pop esi
UPX0:00401EFE pop ebp
UPX0:00401EFF pop ebx
UPX0:00401F00 add esp, 608h
UPX0:00401F06 retn
代码分析:
◆获取name和code分别保存
◆name + strrev(name) + ProductID(项的值) + RegisterOwner(项的值);由于是win10没有注册表项, 最终结果是 name + strrev(name) + strrev(name) + strrev(name) ;
◆初始化md5的state
◆name[-1] = 0x80 ;数据对齐64字节,存储len * 8
◆循环Md5Tramsform
◆保留state[0] 低16位,关键跳比较的就是state[0]的数据
◆读取code中的4个UINT
◆分别对 UINT[0] UINT[1];UINT[1]UINT[2];UINT[2]UINT[3] 运算
◆比较 UINT[0] 和 state[0] 如果相等则成功
Encode函数
关键函数Encode函数:
UPX0:00401B90 Encode proc near ; CODE XREF: UPX0:00401EA4↓p
UPX0:00401B90
UPX0:00401B90 arg_0= dword ptr 4
UPX0:00401B90 arg_4= dword ptr 8
UPX0:00401B90
UPX0:00401B90 mov eax, [esp+arg_0]
UPX0:00401B94 push ebx
UPX0:00401B95 mov ebx, [esp+4+arg_4]
UPX0:00401B99 push esi
UPX0:00401B9A mov esi, [eax]
UPX0:00401B9C push edi
UPX0:00401B9D mov edi, [eax+4]
UPX0:00401BA0 test ebx, ebx
UPX0:00401BA2 jbe short loc_401C15
UPX0:00401BA4 push ebp ; edi = 高32位UINT
UPX0:00401BA4 ; esi = 低32位UINT
UPX0:00401BA5
UPX0:00401BA5 loc_401BA5: ; CODE XREF: Encode+7E↓j
UPX0:00401BA5 mov ebp, edi ; for(i=0;i<loop_cnt;i++)
UPX0:00401BA5 ; 第一步就是实现2个UINT的循环左移1位
UPX0:00401BA7 mov ecx, 1
UPX0:00401BAC shr ebp, 1Fh
UPX0:00401BAF mov [esp+10h+arg_4], ebp ; 取高32位最高位,保存一下
UPX0:00401BB3 mov eax, esi
UPX0:00401BB5 mov edx, edi
UPX0:00401BB7 xor ebp, ebp
UPX0:00401BB9 call __allshl ; 2个UINT实现左移1位
UPX0:00401BBE mov ecx, [esp+10h+arg_4] ; 再将保存的的高32位的最高一位给低32位的最低位,实现循环左移
UPX0:00401BC2 or ebp, edx
UPX0:00401BC4 or ecx, eax
UPX0:00401BC6 xor edx, edx
UPX0:00401BC8 mov esi, ecx ; 循环左移结束
UPX0:00401BCA mov ecx, 0Bh ; 后面就是取低32位的第2 13 31位做亦或
UPX0:00401BCF mov eax, esi
UPX0:00401BD1 mov edi, ebp
UPX0:00401BD3 and eax, 4 ; 取低32位数据 2位
UPX0:00401BD6 call __allshl ; 循环做移动11位 11 + 2 = 13位
UPX0:00401BDB mov ecx, esi
UPX0:00401BDD xor ebp, ebp
UPX0:00401BDF and ecx, 2000h ; 取低32位数据 13位
UPX0:00401BE5 xor edx, ebp
UPX0:00401BE7 xor eax, ecx ; 然后 亦或
UPX0:00401BE9 mov ecx, 12h ; 循环做移动18位 13 + 18 = 31 位
UPX0:00401BEE call __allshl
UPX0:00401BF3 mov ecx, esi
UPX0:00401BF5 xor edx, ebp
UPX0:00401BF7 and ecx, 80000000h ; 取低32位数据 31位
UPX0:00401BFD xor eax, ecx ; eax = eax ^ 31位的值
UPX0:00401BFF mov ecx, 1
UPX0:00401C04 call __allshl ; 左移动一位将,亦或结果存入edx
UPX0:00401C09 xor esi, eax ; eax 不管是0还是0x8000000左移1位后总是0
UPX0:00401C0B xor edi, edx ; 高32位的最后一位和亦或结果做亦或
UPX0:00401C0D dec ebx
UPX0:00401C0E jnz short loc_401BA5 ; for(i=0;i<loop_cnt;i++)
UPX0:00401C0E ; 第一步就是实现2个UINT的循环左移1位
UPX0:00401C10 mov eax, [esp+10h+arg_0]
UPX0:00401C14 pop ebp
UPX0:00401C15
UPX0:00401C15 loc_401C15: ; CODE XREF: Encode+12↑j
UPX0:00401C15 mov [eax], esi
UPX0:00401C17 mov [eax+4], edi
UPX0:00401C1A pop edi
UPX0:00401C1B pop esi
UPX0:00401C1C pop ebx
UPX0:00401C1D retn
UPX0:00401C1D Encode endp
代码解读,直接c代码把
// 编码所用
void AllShl(unsigned int* low32, unsigned int* high32, unsigned int left) {
unsigned int _low32, _high32, lowLeft;
if (left < 32)
{
_low32 = *low32;
_high32 = *high32;
lowLeft = _low32 >> (32 - left);
_high32 = _high32 << left;
_low32 = _low32 << left;
_high32 = _high32 | lowLeft;
*low32 = _low32;
*high32 = _high32;
}
}
void Encode(unsigned int* low32, unsigned* high32, unsigned int loop) {
unsigned int b2,h31,tHigh;
for (unsigned int i = 0; i < loop; loop--) {
// 第一步就是循环左移一位,高32位最高位会溢出,所以源程序先保存最高位,然后,补到低32位的最低为
// 其实就是一个圈
// 自己实现了一个圈
h31 = (*high32) >> 31;
AllShl(low32, high32, 1);
*low32 = *low32 | h31;
tHigh = 0;
b2 = *low32 & 4; // low32取第2位
AllShl(&b2, &tHigh, 11); // 左移11位 + 原来的2位 = 13位
b2 = b2 ^ (*low32 & 0x2000);// 第二位和第13位亦或
AllShl(&b2, &tHigh, 18); // 左移18位 + 原来的13位 = 31位
b2 = b2 ^ (*low32 & 0x80000000);// 第二位和第31位亦或
AllShl(&b2, &tHigh, 1);
*low32 = *low32 ^ b2; // b2 始终等于0
*high32 = *high32 ^ tHigh;
}
}
简化后的Encode
void Encode(unsigned int* low32, unsigned* high32, unsigned int loop) {
unsigned int h31, b2, b13, b31;
for (unsigned int i = 0; i < loop; loop--) {
// 第一步就是循环左移一位,高32位最高位会溢出,所以源程序先保存最高位,然后,补到低32位的最低为
// 其实就是一个圈
// 自己实现了一个圈
h31 = (*high32) >> 31;
AllShl(low32, high32, 1);
*low32 = *low32 | h31;
// 其实后面的就是取低32位中第 2,13,31位的值进行亦或;然后和高32位最低为进行亦或
// 简化得到
b2 = ((*low32) & 4) >> 2; // 取2位
b13 = ((*low32) & 0x2000) >> 13; // 取13位
b31 = ((*low32) & 0x80000000) >> 31; // 取31位
b2 = b2 ^ b13 ^ b31;
*high32 = *high32 ^ b2;
}
}
简化后其实就2步骤,根据低32位还原高32位;然后循环右移1位。
Decode代码可得:
// 解码所用
void AllShr(unsigned int* low32, unsigned int* high32, unsigned int right) {
unsigned int _low32, _high32, highRight;
if (right < 32)
{
_low32 = *low32;
_high32 = *high32;
highRight = _high32 << (32 - right);
_high32 = _high32 >> right;
_low32 = _low32 >> right;
_low32 = _low32 | highRight;
*low32 = _low32;
*high32 = _high32;
}
}
void Decode(unsigned int* low32, unsigned* high32, unsigned int loop) {
unsigned int l1, b2, b13, b31;
for (unsigned int i = 0; i < loop; loop--) {
// 第一步还原高32位的最低位
// 还是求低32位的第 2 13 31 位的亦或值,根据亦或特性 还原高32位数值
b2 = ((*low32) & 4) >> 2; // 取2位
b13 = ((*low32) & 0x2000) >> 13; // 取13位
b31 = ((*low32) & 0x80000000) >> 31; // 取31位
b2 = b2 ^ b13 ^ b31;
*high32 = *high32 ^ b2;
// 第二步右旋转1位
l1 = *low32 & 1; // 取low32最低位
AllShr(low32, high32, 1); // 右移动1位
l1 = l1 << 31;
*high32 = *high32 | l1; // 将l1补到高32位上
}
}
验证可行性
unsigned int a[4];
a[0] = 0x11111111;
a[1] = 0x22222222;
a[2] = 0x33333333;
a[3] = 0x44444444;
printf("Org : %08lx %08lx %08lx %08lxn", a[0], a[1], a[2], a[3]);
for (i = 0; i < 3; i++) {
base = 0x0BADC0DE / (i + 0x50);
printf("%d loop = %lxn", i, base);
Encode(a + i, a + i + 1, base);
}
printf("Encode: %08lx %08lx %08lx %08lxn", a[0], a[1], a[2], a[3]);
for (i = 2; i >= 0; i--) {
base = 0x0BADC0DE / (i + 0x50);
printf("%d loop = %lxn",i, base);
Decode(a + i, a + i + 1, base);
}
printf("Decode: %08lx %08lx %08lx %08lxn", a[0], a[1], a[2], a[3]);
return;
验证结果
Org : 11111111 22222222 33333333 44444444
0 loop = 255f35
1 loop = 24e918
2 loop = 2475dd
Encode: 647577db 5250c59b 90dc6469 57a10cc3
2 loop = 2475dd
1 loop = 24e918
0 loop = 255f35
Decode: 11111111 22222222 33333333 44444444
keygen
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "md5.h"
char* _strrev(char* s)
{
char* h = s;
char* t = s;
char ch;
while (*t++) {};
t--;
t--;
while (h < t)
{
ch = *h;
*h++ = *t;
*t-- = ch;
}
return(s);
}
// 解码所用
void AllShr(unsigned int* low32, unsigned int* high32, unsigned int right) {
unsigned int _low32, _high32, highRight;
if (right < 32)
{
_low32 = *low32;
_high32 = *high32;
highRight = _high32 << (32 - right);
_high32 = _high32 >> right;
_low32 = _low32 >> right;
_low32 = _low32 | highRight;
*low32 = _low32;
*high32 = _high32;
}
}
void Decode(unsigned int* low32, unsigned* high32, unsigned int loop) {
unsigned int l1, b2, b13, b31;
for (unsigned int i = 0; i < loop; loop--) {
// 第一步还原高32位的最低位
// 还是求低32位的第 2 13 31 位的亦或值,根据亦或特性 还原高32位数值
b2 = ((*low32) & 4) >> 2; // 取2位
b13 = ((*low32) & 0x2000) >> 13; // 取13位
b31 = ((*low32) & 0x80000000) >> 31; // 取31位
b2 = b2 ^ b13 ^ b31;
*high32 = *high32 ^ b2;
// 第二步右旋转1位
l1 = *low32 & 1; // 取low32最低位
AllShr(low32, high32, 1); // 右移动1位
l1 = l1 << 31;
*high32 = *high32 | l1;
}
}
void main()
{
int i, len,buffLen,lenOffset;
unsigned char* data;
char name[128] = "";
char names[16] = "";
unsigned int base;
MD5_CTX ctx;
printf("pls,input 'name'>");
scanf("%s", name);
buffLen = strlen(name) * 4 + 1 + 8; // name + 3次name反转 + 一位字符结束的特殊符号0x80,4字节int + 4字节空白
buffLen = (buffLen + 0x40) / 0x40 * 0x40; // 64对齐
lenOffset = buffLen - 8; // 最后8字节位置是int=长度<<3
data = malloc(buffLen);
memset(data, 0, buffLen);
printf("name:%sn", name);
strcpy(data, name);
strcpy(names, name);
_strrev(names);
for (i = 0; i < 3; i++)
{
strcat(data, names); // 因为win10没有注册表项,结果就是这样子
}
printf("data:%sn", data);
len = strlen(data);
data[len] = 0x80;
len += 1;
*(unsigned int*)(data + lenOffset) = len * 8;
MD5Init(&ctx);
for ( i = 0; i < buffLen; i+=64)
{
MD5Transform(ctx.state, (unsigned char *)data + i);
}
printf("md5 : %08lx %08lx %08lx %08lxn", ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3]);
ctx.state[0] = ctx.state[0] & 0xffff;
printf("md5 : %08lx %08lx %08lx %08lxn", ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3]);
for (i = 2; i >= 0; i--) {
base = 0x0BADC0DE / (i + 0x50);
printf("%d loop = %lxn", i, base);
Decode(ctx.state + i, ctx.state + i + 1, base);
}
printf("code : %08lx %08lx %08lx %08lxn", ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3]);
return;
}
几组组NS供验证:
helloworld : 7db37660 ae4c5d05 d2783eea 1d6514f2
pediy.com : b3f21c39 e8ca4483 4146d6f6 5644545b
看雪ID:zhenwo
https://bbs.kanxue.com/user-home-396123.htm
# 往期推荐
2、在Windows平台使用VS2022的MSVC编译LLVM16
3、神挡杀神——揭开世界第一手游保护nProtect的神秘面纱
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):逆向分析 160个CrackMe五星052