好友
阅读权限 10
听众
最后登录 1970-1-1
yakov
发表于 2021-1-10 01:14
本帖最后由 yakov 于 2021-1-10 02:32 编辑
最近在做Crackme练习,虽然这160个都已经有人写过了,不过我还是先独立尝试解决,等完成后再参看其他朋友的。这个Crackme另外一个朋友的分析贴:
https://www.52pojie.cn/thread-613691-4-1.html
有些部分我觉得还没有完全分析出来,所以我也发个帖子做为补充吧。
PEID查下,Delphi程序,OD载入,搜索字符串,发现有个GREAT,跟随到函数头,下个断点,输入用户名和试练码:
用户名: yakov1
试练码: 123456
点击Register没有断下来,后来尝试点击Cancel却断下来了:
00442BBB |. E8 8006FEFF call aLoNg3x_.00423240 ; 取试练码,eax返回长度
......
00442BCD |. E8 6E06FEFF call aLoNg3x_.00423240 ; 取用户名,eax返回长度
......
00442BD6 |. E8 55FFFFFF call aLoNg3x_.00442B30 ; 跟进去
00442BDB |. 84C0 test al,al ; al不为0就GREAT
00442BDD |. 0F84 86000000 je aLoNg3x_.00442C69
00442BE3 |. 8D45 FC lea eax,[local.1]
00442BE6 |. BA B02C4400 mov edx,aLoNg3x_.00442CB0 ; ASCII "GREAT !!!"
进那个Call,再进其中一个对比的关键Call:
00403B44 /$ 53 push ebx
...
00403B4B |. 39D0 cmp eax,edx ; Name和注册码是否相等
00403B4D |. 0F84 8F000000 je aLoNg3x_.00403BE2
......
这里的算法其实就是判断用户名和注册码相等,相等就弹出消息框,这里似乎不是关键,继续。
再查看字符串有句提示:
00442F66 mov eax,aLoNg3x_.00443038 "You MUSTinsert a valid Long Integer Value in the Code Editor... Thank you :)"
找到函数头,点击Register断下:
00442F28 /. 55 push ebp
......
00442F4E |. E8 ED02FEFF call aLoNg3x_.00423240
00442F53 |. 8B45 F8 mov eax,[local.2] ; local2是试练码,eax返回了长度6
......
00442F59 |. E8 FAF9FBFF call aLoNg3x_.00402958 ; 判断试练码,要大于0
00442F60 |. 837D FC 00 cmp [local.1],0x0 ; 不能为空
00442F64 |. 74 37 je short aLoNg3x_.00442F9D ; 试练码要大于0
00442F66 |. B8 38304400 mov eax,aLoNg3x_.00443038 ; ASCII "YouMUST insert a valid Long Integer Value in the Code Editor... Thank you :)"
......
00442F81 |. E8 06FBFFFF call aLoNg3x_.00442A8C
00442F86 |. A3 30584400 mov dword ptr ds:[0x445830],eax
......
00442FB4 |. A1 30584400 mov eax,dword ptr ds:[0x445830]
00442FB9 |. E8 EAF9FFFF call aLoNg3x_.004429A8 ; 验证Call
00442FBE |. 84C0 test al,al
00442FC0 |. 74 30 je short aLoNg3x_.00442FF2
跟进关键验证Call
004429A8 /$ 55 push ebp
......
004429B1 |. 894D F8 mov [local.2],ecx ; local2 = Name
004429B4 |. 8955 FC mov [local.1],edx ; local1 = 试练码
004429B7 |. 8BF8 mov edi,eax ; edi= eax, 设为KEY
......
004429D2 |. E8 5D10FCFF call aLoNg3x_.00403A34 ; 用户名长度
004429D7 |. 83F8 04 cmp eax,0x4 ; 需>=5,输入框有限制,最大为10
004429DA |. 0F8E 82000000 jle aLoNg3x_.00442A62
......
004429EE |. 8945 F4 mov [local.3],eax ; local3 = 用户名长度
004429F1 |. BE 01000000 mov esi,0x1 ; esi = 1
004429F6 |> 8B45 F8 /mov eax,[local.2] ; for i=1 to len(Name)
004429F9 |. E8 3610FCFF |call aLoNg3x_.00403A34 ; 取用户名长度
004429FE |. 83F8 01 |cmp eax,0x1
00442A01 |. 7C 1D |jl short aLoNg3x_.00442A20 ; 下面循环for (j=len(Name); j>0; --j)
00442A03 |> 8B55 F8 |/movedx,[local.2]
00442A06 |. 0FB65432 FF ||movzx edx,byte ptr ds:[edx+esi-0x1] ; edx = Name,从第1位到最后1位
00442A0B |. 8B4D F8 ||movecx,[local.2]
00442A0E |. 0FB64C01 FF ||movzx ecx,byte ptr ds:[ecx+eax-0x1]; 从最后依次往前取Name1位
00442A13 |. 0FAFD1 ||imul edx,ecx ; Name *= Name[长度]
00442A16 |. 0FAFD7 ||imul edx,edi ; 再乘edi(KEY)
00442A19 |. 03DA ||addebx,edx ; ebx为和,设为sum
00442A1B |. 48 ||dec eax
00442A1C |. 85C0 ||test eax,eax
00442A1E |.^ 75 E3 |\jnz short aLoNg3x_.00442A03
00442A20 |> 46 |inc esi ; esi++
00442A21 |. FF4D F4 |dec [local.3] ; local3长度 - 1
00442A24 |.^ 75 D0 \jnz short aLoNg3x_.004429F6
00442A26 |> 8BC3 mov eax,ebx
00442A28 |. 99 cdq
00442A29 |. 33C2 xor eax,edx
00442A2B |. 2BC2 sub eax,edx
00442A2D |. B9 2A2C0A00 mov ecx,0xA2C2A ; ecx = 0xA2C2A(666666)
00442A32 |. 99 cdq
00442A33 |. F7F9 idiv ecx
00442A35 |. 8BDA mov ebx,edx ; 取余数
00442A37 |. 8B45 FC mov eax,[local.1] ; 试练码
00442A3A |. B9 59000000 mov ecx,0x59 ; ecx = 0x59(89)
00442A3F |. 99 cdq
00442A40 |. F7F9 idiv ecx ; 除89
00442A42 |. 8BC8 mov ecx,eax ; ecx = 商
00442A44 |. 8B45 FC mov eax,[local.1]
00442A47 |. BE 50000000 mov esi,0x50 ; esi = 0x50(80)
00442A4C |. 99 cdq
00442A4D |. F7FE idiv esi
00442A4F |. 03CA add ecx,edx ; 前面的商+余数
00442A51 |. 41 inc ecx ; +1
00442A52 |. 894D FC mov [local.1],ecx
00442A55 |. 3B5D FC cmp ebx,[local.1] ; 再对比前面Name的计算结果
算法伪码:
int sum= 0;
for(i=0; i<len(Name); ++i) //从1取到长度
{
for (j=len(Name)-1; j>=0; --j) //从最后1位取到第1位
sum += Name * Name[j] * KEY;
}
SN 整除 89 + SN % 80 +1 = sum % 0xA2C2A (666666)
如果正确,Register按钮消失 但是关键的是,上面红色004429B7那一句,每次都是0,所以上面所有计算全部没用,都为0!
向上追踪,eax的赋值是在验证Call前一句:
00442FB4 |. A1 30584400 mov eax,dword ptr ds:[0x445830]
但是[0x445830]这里也是每次都是0,右键 -> 查找参考对象 -> 地址常数:
只有2个地方,00442F86和00443118两处,到前面再查看:
00442F66 |. B8 38304400 mov eax,aLoNg3x_.00443038 ; ASCII "YouMUST insert a valid Long Integer Value in the Code Editor... Thank you :)"
......
00442F81 |. E8 06FBFFFF callaLoNg3x_.00442A8C
00442F86 |. A3 30584400 mov dword ptr ds:[0x445830],eax
正好在提示非法输入的地方,上面有个Call,会生成一个值,然后赋值给这个地址。尝试先输入一次错误的,让提示出现,然后再点Register,果然有值了!
现在猜测,这个[0x445830]就是个动态密钥KEY,由用户输入一个不是longinteger的值动态生成,经检测,合法值并不是提示的long integer,而是32位整数值! 先跟进00442F81,确定密钥的生成:
00442AB4 |. 83F8 05 cmp eax,0x5 ; 输入的密钥要大于等于6
00442AB7 |. 7E3D jle short aLoNg3x_.00442AF6
00442AB9 |. BE 7B030000 mov esi,0x37B ; esi = 37B
00442ABE |. 8B45 FC mov eax,[local.1]
00442AC1 |. E8 6E0FFCFF call aLoNg3x_.00403A34
00442AC6 |. 8BD8 mov ebx,eax
00442AC8 |. 4B dec ebx
00442AC9 |. 85DB test ebx,ebx
00442ACB |. 7E 2B jle short aLoNg3x_.00442AF8
00442ACD |. B9 01000000 mov ecx,0x1
00442AD2 |> 8B45 FC /mov eax,[local.1] ; for i=1 to len(Key)-1
00442AD5 |. 0FB60408 |movzx eax,byte ptr ds:[eax+ecx] ; key[i+1]
00442AD9 |. BF 11000000 |mov edi,0x11 ; edi = 17
00442ADE |. 33D2 |xor edx,edx
00442AE0 |. F7F7 |div edi ; key[i+1] % 17
00442AE2 |. 42 |inc edx ; 余数+1
00442AE3 |. 8B45 FC |mov eax,[local.1]
00442AE6 |. 0FB64408 FF |movzx eax,byte ptr ds:[eax+ecx-0x1] ; key
00442AEB |. 0FAFD0 |imul edx,eax ; 再乘余数
00442AEE |. 03F2 |add esi,edx ; esi(初值37B) += edx
00442AF0 |. 41 |inc ecx
00442AF1 |. 4B |dec ebx
00442AF2 |.^ 75 DE \jnz short aLoNg3x_.00442AD2
00442AF4 |. /EB 02 jmp short aLoNg3x_.00442AF8
00442AF6 |>|33F6 xor esi,esi
00442AF8 |>\8BC6 mov eax,esi
00442AFA |. B9 48710000 mov ecx,0x7148 ; 0x7148
00442AFF |. 99 cdq
00442B00 |. F7F9 idiv ecx
00442B02 |. 8BC2 mov eax,edx ; 取余数放入eax
00442B04 |. 99 cdq
00442B05 |. 33C2 xoreax,edx ; eax, edx异或
00442B07 |. 2BC2 sub eax,edx ; eax -= edx
eax就是生成的密钥
算法:
1、输入长度>=6;
2、
esi = 37B
for (i=0; i<len(Key); ++i)
esi+= (key[i+1] % 17 + 1) * key;
esi %= 0x7148;
if (esi>=0)
edx= 0;
else
edx= -1;
esi ^= edx;
esi -= edx;
现在再按上面算法自成的注册码输入,点击Register,按键消失,左边Logo露出,用户名变灰,表示不能再变了。右边又出现一个按键,还得继续。
刚才找那个[0x445830]赋值地址时,有两个,另外一个是:00443118,这里的调用和刚才是同一个的,我们找它的注册码验证算法就行了:
004430F8 |. B8 44324400 mov eax,aLoNg3x_.00443244 ; ASCII "You MUST insert a valid LongInteger Value in the Code Editor... Thank you :)"
004430FD |. E8 6EF4FFFF call aLoNg3x_.00442570
00443102 |. 8D55 F4 lea edx,[local.3]
00443105 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC]
0044310B |. E8 3001FEFF call aLoNg3x_.00423240
00443110 |. 8B45 F4 mov eax,[local.3]
00443113 |. E8 74F9FFFF call aLoNg3x_.00442A8C
00443118 |. A3 30584400 movdword ptr ds:[0x445830],eax ; 生成的密钥赋值
0044311D |. BA 9C324400 movedx,aLoNg3x_.0044329C
00443122 |. 8B83 DC020000 mov eax,dword ptrds:[ebx+0x2DC]
00443128 |. E8 4301FEFF call aLoNg3x_.00423270 ; 这里下断
0044312D |. E9 DD000000 jmp aLoNg3x_.0044320F
00443132 |> 85F6 test esi,esi
00443134 |. 0F8E C4000000 jle aLoNg3x_.004431FE
注册码也是和上面一样的,等于再重复一次刚才的就可以了,到现在logo完全露出来了。
这个Crackme最大的特点,就是它采用了一个密钥,由用户动态输入的值计算出的密钥参与运算!
附上一个MFC的注册机代码,写得不是很理想,注册码不能为负,这个没自动实现,得人工判断:
void CKeygenDlg::OnGen()
{
UpdateData(TRUE);
if (m_szKeyin.GetLength()<6)
{
AfxMessageBox("密钥长度>=6");
return;
}
if (m_szKeyin.SpanIncluding("1234567890")==m_szKeyin)
{
int x = atoi((LPSTR)(LPCTSTR)m_szKeyin);
CString s;
s.Format("%d", x);
if (s==m_szKeyin)
{
AfxMessageBox("请输入一个非32位整数的值,包含字符,或者不是32位整数的数值");
return;
}
}
if (m_szName.GetLength()<6||m_szName.GetLength()>10)
{
AfxMessageBox("Name长度>=6且<=10");
return;
}
int esi = 0x37B, i, edx;
for (i=0; i<m_szKeyin.GetLength()-1; ++i)
esi += (m_szKeyin.GetAt(i+1) % 17 + 1) * m_szKeyin.GetAt(i);
esi %= 0x7148;
if (esi>=0)
edx = 0;
else
edx = -1;
esi ^= edx;
esi -= edx;
m_nKey = esi;
int sum = 0, j, a, b;
for (i=0; i<m_szName.GetLength(); ++i) //从1取到长度
{
for (j=m_szName.GetLength()-1; j>=0; --j) //从最后1位取到第1位
{
a = m_szName.GetAt(i);
b = m_szName.GetAt(j);
sum += m_szName.GetAt(i) * m_szName.GetAt(j) * m_nKey;
}
}
sum %= 666666;
edx = sum / 89 + sum % 80;
while (1)
{
if (edx / 89 + edx % 80 + 1 == sum)
break;
else
{
if (sum>=0)
++edx;
else
--edx;
}
}
m_szSN.Format("%d", edx);
UpdateData(FALSE);
if (edx<=0)
{
AfxMessageBox("输入不合法, 要保证SN是大于0的,密钥生成值取6-8位大写字母,或者11-12位数字一般都可以,或者用户名尽量用大写字母和数字!");
return;
}
}
免费评分
查看全部评分