重新看了下aLoNg3x.2,有好多不懂的。跟了很久后,大概看了下别人的流程,才知道怎么算序列号···但写代码捉急。
拿到程序,大体看还是输名字和序列号。查壳,没有加壳,因为是Delphi写的,所以按钮事件还是通过DE来找。
主要的按钮就是RegsisterClick注册按钮,和AgainClick检测按钮。整个程序的流程:
1.先输入name,长度需要大于4位
2.输入含有字符的字符串,长度大于5位
3.输入正确的注册码,然后注册键按钮会消失
4.输入含有字符的字符串,长度大于5位
5.输入正确的注册码。输错的话,注册键按钮会恢复
最后again按钮消失,注册成功。
流程借鉴别人看起来很简单,自己跟就不是那么一回事了。然后分析下两个主要按钮的代码。
首先根据DE中的地址,给注册键和again键下断。F9运行。程序跑起来。然后点击注册按钮,断到了注册函数中。
00442F28 /. 55 push ebp ; Register
00442F29 |. 8BEC mov ebp,esp
00442F2B |. 83C4 F8 add esp,-0x8 ; 开辟局部变量
00442F2E |. 53 push ebx
00442F2F |. 56 push esi
00442F30 |. 33C9 xor ecx,ecx ; aLoNg3x_.0041E570
00442F32 |. 894D F8 mov [local.2],ecx ; aLoNg3x_.0041E570
00442F35 |. 8BD8 mov ebx,eax
00442F37 |. 33C0 xor eax,eax
00442F39 |. 55 push ebp
00442F3A |. 68 22304400 push aLoNg3x_.00443022 ; 异常处理程序
00442F3F |. 64:FF30 push dword ptr fs:[eax]
00442F42 |. 64:8920 mov dword ptr fs:[eax],esp
00442F45 |. 8D55 F8 lea edx,[local.2] ; 传参
00442F48 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC] ; 函数地址
00442F4E |. E8 ED02FEFF call <aLoNg3x_.GetText>
00442F53 |. 8B45 F8 mov eax,[local.2] ; 获取CODE的值
00442F56 |. 8D55 FC lea edx,[local.1]
00442F59 |. E8 FAF9FBFF call <aLoNg3x_.calc> ; 输入的字符串转换成一组数转化成16进制
00442F5E |. 8BF0 mov esi,eax
00442F60 |. 837D FC 00 cmp [local.1],0x0 ; 如果CODE输入是空的或者是非数字,local1=1
00442F64 |. 74 37 je short aLoNg3x_.00442F9D ; 如果输入格式正确,不会去设置[0x445830]
00442F66 |. B8 38304400 mov eax,aLoNg3x_.00443038 ; ASCII 59,"ou MUST insert a valid Long Integer Value in the Code Editor... Thank you :)"
00442F6B |. E8 00F6FFFF call aLoNg3x_.00442570
00442F70 |. 8D55 F8 lea edx,[local.2]
00442F73 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC]
00442F79 |. E8 C202FEFF call <aLoNg3x_.GetText> ; 获取编辑框的值
00442F7E |. 8B45 F8 mov eax,[local.2]
00442F81 |. E8 06FBFFFF call <aLoNg3x_.Set445830> ; 这个函数设置[0x445830]位置的值
00442F86 |. A3 30584400 mov dword ptr ds:[0x445830],eax ; 这里关键,这里如果设置为0,
00442F8B |. BA 90304400 mov edx,aLoNg3x_.00443090 ; UNICODE "0"
00442F90 |. 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC]
00442F96 |. E8 D502FEFF call aLoNg3x_.00423270
00442F9B |. EB 6F jmp short aLoNg3x_.0044300C
00442F9D |> 85F6 test esi,esi ; esi存储的是CODE字符串转换后的值
00442F9F |. 7E 5A jle short aLoNg3x_.00442FFB
00442FA1 |. 8D55 F8 lea edx,[local.2]
00442FA4 |. 8B83 D8020000 mov eax,dword ptr ds:[ebx+0x2D8]
00442FAA |. E8 9102FEFF call <aLoNg3x_.GetText> ; 获取name,长度在eax
00442FAF |. 8B4D F8 mov ecx,[local.2] ; name的值
00442FB2 |. 8BD6 mov edx,esi ; CODE转换后的值
00442FB4 |. A1 30584400 mov eax,dword ptr ds:[0x445830]
00442FB9 |. E8 EAF9FFFF call <aLoNg3x_.test> ; 检测输入的CODE
00442FBE |. 84C0 test al,al ; 输入正确后,al为非0,注册按钮消失,again按钮出现。
00442FC0 |. 74 30 je short aLoNg3x_.00442FF2
00442FC2 |. 33D2 xor edx,edx
00442FC4 |. 8B83 CC020000 mov eax,dword ptr ds:[ebx+0x2CC]
00442FCA |. E8 6101FEFF call aLoNg3x_.00423130
00442FCF |. B2 01 mov dl,0x1
00442FD1 |. 8B83 E8020000 mov eax,dword ptr ds:[ebx+0x2E8]
00442FD7 |. E8 5401FEFF call aLoNg3x_.00423130
00442FDC |. 33D2 xor edx,edx
00442FDE |. 8B83 D8020000 mov eax,dword ptr ds:[ebx+0x2D8]
00442FE4 |. 8B08 mov ecx,dword ptr ds:[eax] ; aLoNg3x_.004427AC
00442FE6 |. FF51 60 call dword ptr ds:[ecx+0x60]
00442FE9 |. 33C0 xor eax,eax
00442FEB |. A3 30584400 mov dword ptr ds:[0x445830],eax
00442FF0 |. EB 1A jmp short aLoNg3x_.0044300C
00442FF2 |> 33C0 xor eax,eax
00442FF4 |. A3 30584400 mov dword ptr ds:[0x445830],eax
00442FF9 |. EB 11 jmp short aLoNg3x_.0044300C
00442FFB |> B8 9C304400 mov eax,aLoNg3x_.0044309C
00443000 |. E8 6BF5FFFF call aLoNg3x_.00442570
00443005 |. 33C0 xor eax,eax
00443007 |. A3 30584400 mov dword ptr ds:[0x445830],eax
0044300C |> 33C0 xor eax,eax
0044300E |. 5A pop edx ; aLoNg3x_.004245C0
0044300F |. 59 pop ecx ; aLoNg3x_.004245C0
00443010 |. 59 pop ecx ; aLoNg3x_.004245C0
00443011 |. 64:8910 mov dword ptr fs:[eax],edx
00443014 |. 68 29304400 push aLoNg3x_.00443029
00443019 |> 8D45 F8 lea eax,[local.2]
0044301C |. E8 9707FCFF call <aLoNg3x_.引用计数减1>
00443021 \. C3 retn
这块的代码,主要做的事:获取输入的CODE字符串,将其转换成十六进制,这是针对输入纯数字,比如输入10,然后会转换成0xA保存起来,然后转换函数中,会设置局部变量local1的值,如果CODE输入的是空或者带字母的字符串,就会去设置0x445830这个地址的值具体如何设置在下面的函数中有分析。设置完这个内存地址的值后,就通过使用这个值,去计算Name字符串生成的注册码。然后检测是否正确,如果输入正确,那么注册键就消失,出现一个再次尝试的按钮。
00442F81 |. E8 06FBFFFF call <aLoNg3x_.Set445830> 函数内部
00442A8C >/$ 55 push ebp ;设置内存地址0x445830值的函数
00442A8D |. 8BEC mov ebp,esp
00442A8F |. 51 push ecx
00442A90 |. 53 push ebx
00442A91 |. 56 push esi
00442A92 |. 57 push edi
00442A93 |. 8945 FC mov [local.1],eax ; aLoNg3x_.0044309C
00442A96 |. 8B45 FC mov eax,[local.1]
00442A99 |. E8 4A11FCFF call <aLoNg3x_.useCount> ;引用计数
00442A9E |. 33C0 xor eax,eax ; aLoNg3x_.0044309C
00442AA0 |. 55 push ebp
00442AA1 |. 68 212B4400 push aLoNg3x_.00442B21
00442AA6 |. 64:FF30 push dword ptr fs:[eax]
00442AA9 |. 64:8920 mov dword ptr fs:[eax],esp ;seh异常处理函数
00442AAC |. 8B45 FC mov eax,[local.1]
00442AAF |. E8 800FFCFF call <aLoNg3x_.字符串长度>
00442AB4 |. 83F8 05 cmp eax,0x5 ; 长度必须大于5
00442AB7 |. 7E 3D jle short aLoNg3x_.00442AF6
00442AB9 |. BE 7B030000 mov esi,0x37B ; 第一个数0x37B
00442ABE |. 8B45 FC mov eax,[local.1]
00442AC1 |. E8 6E0FFCFF call <aLoNg3x_.字符串长度>
00442AC6 |. 8BD8 mov ebx,eax ; aLoNg3x_.0044309C
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] ; 检测字符串序列号的算法
00442AD5 |. 0FB60408 |movzx eax,byte ptr ds:[eax+ecx] ; 取第ecx个字符,转换成assic
00442AD9 |. BF 11000000 |mov edi,0x11
00442ADE |. 33D2 |xor edx,edx
00442AE0 |. F7F7 |div edi ; 第一个字符的assic/0x11
00442AE2 |. 42 |inc edx ; 余数+1
00442AE3 |. 8B45 FC |mov eax,[local.1]
00442AE6 |. 0FB64408 FF |movzx eax,byte ptr ds:[eax+ecx-0x1] ; 取第ecx-1个字符的assic
00442AEB |. 0FAFD0 |imul edx,eax ; edx==(余数计算结果edx)*第ecx-1个assic的值
00442AEE |. 03F2 |add esi,edx ; 0x37B+计算结果,累加
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 ; 计算出esi
00442AFA |. B9 48710000 mov ecx,0x7148 ; ecx=0x7148
00442AFF |. 99 cdq
00442B00 |. F7F9 idiv ecx ; esi/0x7148
00442B02 |. 8BC2 mov eax,edx ; eax==余数
00442B04 |. 99 cdq
00442B05 |. 33C2 xor eax,edx
00442B07 |. 2BC2 sub eax,edx
00442B09 |. 8BD8 mov ebx,eax ; ebx=计算结果
00442B0B |. 33C0 xor eax,eax ; aLoNg3x_.0044309C
00442B0D |. 5A pop edx ; 0019F620
00442B0E |. 59 pop ecx ; 0019F620
00442B0F |. 59 pop ecx ; 0019F620
00442B10 |. 64:8910 mov dword ptr fs:[eax],edx
00442B13 |. 68 282B4400 push aLoNg3x_.00442B28
00442B18 |> 8D45 FC lea eax,[local.1] ; eax==name字符串
00442B1B |. E8 980CFCFF call <aLoNg3x_.引用计数减1>
00442B20 \. C3 retn
00442B21 .^ E9 5207FCFF jmp aLoNg3x_.00403278
00442B26 .^ EB F0 jmp short aLoNg3x_.00442B18
00442B28 . 8BC3 mov eax,ebx ; 计算结果
00442B2A . 5F pop edi ; 0019F620
00442B2B . 5E pop esi ; 0019F620
00442B2C . 5B pop ebx ; 0019F620
00442B2D . 59 pop ecx ; 0019F620
00442B2E . 5D pop ebp ; 0019F620
00442B2F . C3 retn
要进入这个函数,首先必须输入带有字母的字符串,进入函数后,首先检测字符串的长度是否大于5,小于等于5退出。满足条件后,进行计算。逐一获取CODE字符串的值,比如我输入"abcdef",先取出b,然后b的assic/0x11得到一个余数tmp,tmp+1,然后再取a,用tmp*=a的assic,然后计算结果+最开始的数字0x37b,计算出一个数字NUM,接着再取c 和 b继续计算,累加NUM,把字符串都计算完后,最后得到一个数字ret。
用这个ret/0x7148,计算出一个余数,那么这个余数就是设置那个455830地址的值。接下来,因为CODE输入的如果是非数字,会弹一次警告框,此时如果输入的是带字母的字符串,长度大于5,那么455830就会被设置成一个非0值。程序继续往下走,就会去将再次输入的CODE(必须是纯数字)进行检测,看是否正确。而此时输入的CODE,其实是对输入的name进行计算的出来的。
00442FB9 |. E8 EAF9FFFF call <aLoNg3x_.test> ; 检测输入的CODE
004429A8 >/$ 55 push ebp
004429A9 |. 8BEC mov ebp,esp
004429AB |. 83C4 F4 add esp,-0xC
004429AE |. 53 push ebx
004429AF |. 56 push esi
004429B0 |. 57 push edi
004429B1 |. 894D F8 mov [local.2],ecx ; name
004429B4 |. 8955 FC mov [local.1],edx ; CODE转换
004429B7 |. 8BF8 mov edi,eax ; eax为0
004429B9 |. 8B45 F8 mov eax,[local.2]
004429BC |. E8 2712FCFF call <aLoNg3x_.useCount> ; 引用计数
004429C1 |. 33C0 xor eax,eax ; aLoNg3x_.0044309C
004429C3 |. 55 push ebp
004429C4 |. 68 7A2A4400 push aLoNg3x_.00442A7A
004429C9 |. 64:FF30 push dword ptr fs:[eax]
004429CC |. 64:8920 mov dword ptr fs:[eax],esp ; 异常处理函数的安装
004429CF |. 8B45 F8 mov eax,[local.2]
004429D2 |. E8 5D10FCFF call <aLoNg3x_.字符串长度> ; 获取的是长度?
004429D7 |. 83F8 04 cmp eax,0x4
004429DA |. 0F8E 82000000 jle aLoNg3x_.00442A62 ; name的长度要大于4,否则就退出了
004429E0 |. 33DB xor ebx,ebx ; ebx=0
004429E2 |. 8B45 F8 mov eax,[local.2]
004429E5 |. E8 4A10FCFF call <aLoNg3x_.字符串长度>
004429EA |. 85C0 test eax,eax ; aLoNg3x_.0044309C
004429EC |. 7E 38 jle short aLoNg3x_.00442A26
004429EE |. 8945 F4 mov [local.3],eax ; aLoNg3x_.0044309C
004429F1 |. BE 01000000 mov esi,0x1
004429F6 |> 8B45 F8 /mov eax,[local.2]
004429F9 |. E8 3610FCFF |call <aLoNg3x_.字符串长度>
004429FE |. 83F8 01 |cmp eax,0x1
00442A01 |. 7C 1D |jl short aLoNg3x_.00442A20
00442A03 |> 8B55 F8 |/mov edx,[local.2]
00442A06 |. 0FB65432 FF ||movzx edx,byte ptr ds:[edx+esi-0x1>; 取每一个字符到edx
00442A0B |. 8B4D F8 ||mov ecx,[local.2]
00442A0E |. 0FB64C01 FF ||movzx ecx,byte ptr ds:[ecx+eax-0x1>; 倒序取第n个字符给ecx
00442A13 |. 0FAFD1 ||imul edx,ecx
00442A16 |. 0FAFD7 ||imul edx,edi ; edi=0,任何数乘都为0
00442A19 |. 03DA ||add ebx,edx ; ebx+edi始终为0
00442A1B |. 48 ||dec eax ; aLoNg3x_.0044309C
00442A1C |. 85C0 ||test eax,eax ; aLoNg3x_.0044309C
00442A1E |.^ 75 E3 |\jnz short aLoNg3x_.00442A03
00442A20 |> 46 |inc esi
00442A21 |. FF4D F4 |dec [local.3]
00442A24 |.^ 75 D0 \jnz short aLoNg3x_.004429F6
00442A26 |> 8BC3 mov eax,ebx ; 输入的name计算出一组数
00442A28 |. 99 cdq
00442A29 |. 33C2 xor eax,edx
00442A2B |. 2BC2 sub eax,edx
00442A2D |. B9 2A2C0A00 mov ecx,0xA2C2A ; 计算出来的数字/0xA2C2A
00442A32 |. 99 cdq
00442A33 |. F7F9 idiv ecx
00442A35 |. 8BDA mov ebx,edx ; ebx==余数
00442A37 |. 8B45 FC mov eax,[local.1] ; 取CODE转换后的值
00442A3A |. B9 59000000 mov ecx,0x59
00442A3F |. 99 cdq
00442A40 |. F7F9 idiv ecx ; name转换后的数/0x59
00442A42 |. 8BC8 mov ecx,eax ; ecx=商
00442A44 |. 8B45 FC mov eax,[local.1]
00442A47 |. BE 50000000 mov esi,0x50
00442A4C |. 99 cdq
00442A4D |. F7FE idiv esi ; name转换后的数/0x50
00442A4F |. 03CA add ecx,edx ; 第一个结果的 商+第二个结果的 余数
00442A51 |. 41 inc ecx ; 结果+1
00442A52 |. 894D FC mov [local.1],ecx ; 结果
00442A55 |. 3B5D FC cmp ebx,[local.1] ; 结果和计算出来的数相同
00442A58 |. 75 04 jnz short aLoNg3x_.00442A5E
00442A5A |. B3 01 mov bl,0x1 ; 相同,bl=1
00442A5C |. EB 06 jmp short aLoNg3x_.00442A64
00442A5E |> 33DB xor ebx,ebx
00442A60 |. EB 02 jmp short aLoNg3x_.00442A64
00442A62 |> 33DB xor ebx,ebx
00442A64 |> 33C0 xor eax,eax ; eax清空=0
00442A66 |. 5A pop edx ; 0019F620
00442A67 |. 59 pop ecx ; 0019F620
00442A68 |. 59 pop ecx ; 0019F620
00442A69 |. 64:8910 mov dword ptr fs:[eax],edx
00442A6C |. 68 812A4400 push aLoNg3x_.00442A81
00442A71 |> 8D45 F8 lea eax,[local.2]
00442A74 |. E8 3F0DFCFF call <aLoNg3x_.引用计数减1>
00442A79 \. C3 retn
00442A7A .^ E9 F907FCFF jmp aLoNg3x_.00403278
00442A7F .^ EB F0 jmp short aLoNg3x_.00442A71
00442A81 . 8BC3 mov eax,ebx ; 也就是输入正确,eax==1
00442A83 . 5F pop edi ; 0019F620
00442A84 . 5E pop esi ; 0019F620
00442A85 . 5B pop ebx ; 0019F620
00442A86 . 8BE5 mov esp,ebp
00442A88 . 5D pop ebp ; 0019F620
00442A89 . C3 retn
这个函数是检测CODE输入是否正确的函数。
在调用这个函数开始的位置,有这么一句:
这个就是上面计算出来的那个余数sss。
进入函数后,可以看到edi=eax,然后就取输入的name,来计算生成的序列号,比如输入"123456",
首先取‘1’,然后取‘6’,注意都是用assic计算 。‘1’*‘6’,计算出结果num1,num2=num1*sss,然后累加,这里用TMP表示累加数,TMP+=num2,然后取‘1’*‘5’,当一轮取完,开始‘2’*‘6’进行下一轮,直到‘6’*‘6’这一轮走完,TMP就累加结束。
然后用TMP/0xA2C2A计算出一个余数,存到ebx中。
接着用输入的name,也就是转化成十六进制的数nameNum
name转换后的数nameNum/0x59的商 n1
name转换后的数nameNum/0x50的余数 n2
result=n1+n2+1
ebx的值和result的值如果相等,那么序列号就正确。
用式子表示就是 x/0x59+x%0x50+1=上面说过的那个余数sss。
那个余数可以是随便输入一串字母,就算出来了,但是这里如何求x(x就是正确的序列号)转成代码,算法太渣,想不出来。
这是爆破的,序列号等会算了再写写,然后again中调用的函数和注册的一样,都是先输入一串带字母的数,计算出一个余数,再输入正确的序列号,检测通过后,消失。
爆破就算了说吧。