本帖最后由 Pnmker 于 2015-5-24 03:39 编辑
原本不想写这篇破文,没有多少兴奋点,也因为已经有道友发了相关的破文,但转念一想每个人都有是新手的时候,(当然我现在也是一个新手,160个才练到第5个呢),新手想找些资料或者大牛来帮忙解惑却怎么也解决不了的那种走投无路的感觉,我想每个成长起来的人都曾经有过,所以谨以此文献给那些奔走在志向成为大牛的新手们。大牛们如果觉得写得不好就直接无视吧。 在我跟踪005这个CM的时候,我心里一直在默念一句话:“ajj是不是疯了,弄了这么一个操蛋的注册验证流程!”。当然如果只是爆破那非常简单,然而我们最终的目标是要Keygen It。尽管流程我弄明白了,但没有写keygen,就是因为那个操蛋的流程我觉得还是不要在Keygen It上浪费精力了。如果有谁有兴趣写,写出了可以分享下给我哦。 开始之前,我觉得还是有必要把一些相关的内容和链接留在这里。ajj的004和005这两个CM有某些地方可以相互借鉴一些,也都已经有破文发布,希望大家可以都学习一下: 44018723兄果然神通,160个CM大概都已经研究过了吧,很多也已经发了破文,有兴趣的话可以关注下44018723兄。上面两个链接就是44018723兄发表的关于ajj的两个CM的破文,其中004我在那篇文章的12楼又做了一些补充。这篇文章也算是对005的一个补充吧。下面进入正题. 首先,我先给出005注册验证的那个操蛋流程,真的是不费一番心思真不好搞出来。
第一步:新建一个路径为X:\ajj.126.c0m\j\o\j\o\ok.txt的文本文件,文件内容的二进制为 20 61 6A 6A D0 B4 B5 C4 43 4B 6D 65D5 E6 C0 C3 21 FF FF,给出二进制的原因主要是因为后面两个字符FF FF打文本打不出来,这句话的文本显示为“ajj写的CKme真烂! 这个文件的作用就是会在界面中多显示出一个输入框(下面记为Edit2),但是初始是禁用的,输不了东西。 第二步:打开程序,初始化完成之后,鼠标右键点击“注册”按钮5次。记住一定是鼠标右键,而且必须是点击5次,如果点错不要找我哦; 第三步:在显示图片的图片框中双击没有图片显示的地方; 这一步完成之后,输入框Edit2就可以输入内容了。 第四步:输入注册名和Edit2,。这两个输入的内容也是有要求的,注册名长度必须是3的倍数,Edit2输入的内容长度必须为8,且第2个字符为’_’,第6个字符为’,’。 我在跟踪的时候分别输入的”trisinker”和”1_345,78” 第五步:输入完成之后,双击Edit2; 第六步:在图片3(“性相近“)出现时,移动鼠标从界面的右下角外部进入程序界面; 第七步:在图片2(“性本善“)出现时,移动鼠标从界面的左下角外部进入程序界面; 第八步:在图片 4(“习相远“)出现时,鼠标左键点击1次图片4,右键点击8次图片4. 注意这是最后一步,可能每台机器的状况都不一样。请勿按照上面说的点击。 其实这一步中点击图片1(“人之初“),图片2、图片3、图片4哪些图片,鼠标左键和右键各点击多少次,这个是一个很复杂的算法。 决定点击方式的参数就是输入的两个字符串,以及程序所在磁盘的剩余磁盘空间,在第五步的双击Edit2时候会计算出一个值存储在ds:[ebx+0x30C]中, 根据这个值就可以知道如何点击图片。
好了,只要做完这八步,就可以成功完成注册了,这时候“注册“按钮的文本会变为”注册了“,至于ajj在程序附带的文档中说图片会有轻微变化,我眼小没看出来。 怎么样,这个注册验证够变态了吧,下面我们就开始分析为什么要这样一步步地去操作,以及该如何去操作(主要指第八步)。建议大家先看一下上面列出来的44018723兄的005破文,有些操作在这里我就不再过多浪费时间了,比如 脱壳。
先用OD加载程序后,使用中文搜索引擎搜索到下面这段代码(其实这段代码为定时器2的定时处理函数,下称Timer2Timer): [Asm] 纯文本查看 复制代码 004473E4 . 53 push ebx ; Timer2Timer
004473E5 . 8BD8 mov ebx,eax
004473E7 . 81BB 04030000>cmp dword ptr ds:[ebx+0x304],0xC34
004473F1 . 0F84 88000000 je CKme002.0044747F
004473F7 . 81BB 08030000>cmp dword ptr ds:[ebx+0x308],0x230D
00447401 . 74 7C je short CKme002.0044747F
00447403 . 81BB 10030000>cmp dword ptr ds:[ebx+0x310],0xF94
0044740D . 75 70 jnz short CKme002.0044747F
0044740F . 8B83 18030000 mov eax,dword ptr ds:[ebx+0x318]
00447415 . 3B83 14030000 cmp eax,dword ptr ds:[ebx+0x314]
0044741B . 75 62 jnz short CKme002.0044747F
0044741D . 81BB 1C030000>cmp dword ptr ds:[ebx+0x31C],0x3E7
00447427 . 74 56 je short CKme002.0044747F
00447429 . 33D2 xor edx,edx ; ntdll.KiFastSystemCallRet
0044742B . 8B83 D8020000 mov eax,dword ptr ds:[ebx+0x2D8]
00447431 . 8B08 mov ecx,dword ptr ds:[eax]
00447433 . FF51 5C call dword ptr ds:[ecx+0x5C]
00447436 . 33D2 xor edx,edx ; ntdll.KiFastSystemCallRet
00447438 . 8B83 DC020000 mov eax,dword ptr ds:[ebx+0x2DC]
0044743E . 8B08 mov ecx,dword ptr ds:[eax]
00447440 . FF51 5C call dword ptr ds:[ecx+0x5C]
00447443 . 33D2 xor edx,edx ; ntdll.KiFastSystemCallRet
00447445 . 8B83 E0020000 mov eax,dword ptr ds:[ebx+0x2E0]
0044744B . 8B08 mov ecx,dword ptr ds:[eax]
0044744D . FF51 5C call dword ptr ds:[ecx+0x5C]
00447450 . 33D2 xor edx,edx ; ntdll.KiFastSystemCallRet
00447452 . 8B83 E4020000 mov eax,dword ptr ds:[ebx+0x2E4]
00447458 . 8B08 mov ecx,dword ptr ds:[eax]
0044745D . A1 A8984400 mov eax,dword ptr ds:[0x4498A8]
00447462 . 83C0 70 add eax,0x70
00447465 . BA 8C744400 mov edx,CKme002.0044748C ; 厉害厉害真厉害!佩服佩服真佩服!!
0044746A . E8 EDC4FBFF call CKme002.0040395C
0044746F . BA B8744400 mov edx,CKme002.004474B8 ; 注册了
00447474 . 8B83 EC020000 mov eax,dword ptr ds:[ebx+0x2EC]
0044747A . E8 3DCCFDFF call CKme002.004240BC
0044747F > 5B pop ebx ; kernel32.7C817077
00447480 . C3 retn
正如44018723兄所说这段代码就是爆破的用武之地,但这里我们并不是要爆破,所以就要解决44018723兄所没能解决了的问题,就是如何找到这些ds:[ebx+0x304](第一步)、ds:[ebx+0x308](第二步、第三步)、ds:[ebx+0x310](第六步、第七步)、ds:[ebx+0x318](第8步)、ds:[ebx+0x314](第六步、第七步)内存地址是在哪里进行赋值的。处理这件事,我个人觉得没有什么好的办法,至少对于还是新手的我来说我没有找到什么好办法,考验的主要是耐心,没有技巧。唯一能做的一件事就是查看所有的反汇编代码把这些地址统统揪出来,然后仔细的分析在哪里可能会完成赋值操作,以及要完成所要的赋值操作要满足什么条件。 在这里我再插入一段可能对于破 Delphi程序比较适用的方法。我们从上面找到的那段爆破关键代码段的第一行地址004473E4,在OD中选中这一行然后右键à查看参考à选定命令,这时候就会出现下面的结果:
c005-0
双击00446BB5那一行,这时候就会看到这个地址附近都是类似的代码: [Asm] 纯文本查看 复制代码 00446AE2 0C db 0C
00446AE3 00 db 00
00446AE4 11 db 11
00446AE5 00 db 00
00446AE6 1C6C4400 dd CKme002.00446C1C
00446AEA . 0A db 0A
00446AEB . 46 6F 72 6D 4>ascii "FormCreate"
00446AF5 12 db 12
00446AF6 00 db 00
00446AF7 286E4400 dd CKme002.00446E28
00446AFB . 0B db 0B
00446AFC . 54 69 6D 65 7>ascii "Timer1Timer"
00446B07 17 db 17
00446B08 00 db 00
00446B09 A46F4400 dd CKme002.00446FA4
00446B0D . 10 db 10
00446B0E . 42 75 74 74 6>ascii "Button1MouseDown"
00446B1E 15 db 15
00446B1F 00 db 00
00446B20 DC6F4400 dd CKme002.00446FDC
00446B24 . 0E db 0E
00446B25 . 50 61 6E 65 6>ascii "Panel1DblClick"
00446B33 14 db 14
00446B34 00 db 00
00446B35 F86F4400 dd CKme002.00446FF8
00446B39 . 0D db 0D
00446B3A . 45 64 69 74 3>ascii "Edit2DblClick"
00446B47 14 db 14
00446B48 00 db 00
00446B49 EC704400 dd CKme002.004470EC
00446B4D . 0D db 0D
00446B4E . 46 6F 72 6D 4>ascii "FormMouseMove"
00446B5B 16 db 16
00446B5C 00 db 00
00446B5D 34724400 dd CKme002.00447234
00446B61 . 0F db 0F
00446B62 . 49 6D 61 67 6>ascii "Image1MouseDown"
00446B71 16 db 16
00446B72 00 db 00
00446B73 A0724400 dd CKme002.004472A0
00446B77 . 0F db 0F
00446B78 . 49 6D 61 67 6>ascii "Image2MouseDown"
00446B87 16 db 16
00446B88 00 db 00
00446B89 0C734400 dd CKme002.0044730C
00446B8D . 0F db 0F
00446B8E . 49 6D 61 67 6>ascii "Image3MouseDown"
00446B9D 16 db 16
00446B9E 00 db 00
00446B9F 78734400 dd CKme002.00447378
00446BA3 . 0F db 0F
00446BA4 . 49 6D 61 67 6>ascii "Image4MouseDown"
00446BB3 12 db 12
00446BB4 00 db 00
00446BB5 E4734400 dd CKme002.004473E4
00446BB9 . 0B db 0B
00446BBA . 54 69 6D 65 7>ascii "Timer2Timer"
00446BC5 13 db 13
00446BC6 00 db 00
00446BC7 C0744400 dd CKme002.004474C0
00446BCB . 0C db 0C
00446BCC . 42 75 74 74 6>ascii "Button1Click"
哈哈,虽然我没有写过Delphi程序,毕竟还是写过C++/C#程序的,我第一反应就是这肯定就是跟MFC中的消息映射具有相同的功能,这些都是在定义鼠标按下,鼠标移动,定时器等消息的处理过程,而dd CKme002.0044xxxx这个就是对应的消息处理函数,要知道上面列出的那些内存地址如何赋值,必定离不开对这些消息处理函数的跟踪,也就是要在这些地址上面下断点,但不要忙着把断点都下了,一次都下了也没什么好处,我们还是从上面列出的内存地址一个一个开始解决,用到时再下断点也不迟。 由于查上述内存地址在哪些地方有赋值操作要通读反汇编代码,所以可以选择任意一款静态反汇编工具来搜索,这里我选用的是W32DAsm。 用W32DAsm反汇编完成之后,我们首先搜索ebx+0x304这个内存地址,具体方法这里就不再叙述了。可以找到如下之处:
c005-1
回到OD查找这两行地址所在的函数头,就会发现刚好是在窗台创建FormCreate的消息处理函数dd CKme002.00446C1C里面,果断下断点,重启程序、F9然后就进入了这个断点, 下面的跟踪我就不讲了,关于ebx+0x304这个内存地址他所做的操作就是在文件X:\ajj.126.c0m\j\o\j\o\ok.txt存在并且文件内容为“ajj写的CKme真烂!“时赋值 mov dwordptr ds:[ebx+0x304],0xC34,这个正是我们在爆破点处(其实就是定时器Timer2的定时处理函数)遇到的第一个cmp判断,解决! 赶紧新建好X:\ajj.126.c0m\j\o\j\o\ok.txt保存好内容,重启程序看一下吧。(什么?没有这个盘符,自己想办法吧!)是不是多出来一个输入框啊,这就是我们上面所说的第一步操作,哈哈!可惜不能输入,下面就要来解决这个问题了。 嗯,要怎么解决呢,好像是没有办法了。还是回头看看我们讲过的那一堆消息处理函数吧,文本框不能输入要变得能输入必须得经过事件触发消息处理函数来完成,看那里没有错! 如果你醒目,那么在消息处理函数紧接着的上面你会看到这么一堆代码 :[Asm] 纯文本查看 复制代码 00446A3A D0 db D0
00446A3B 02 db 02
00446A3C 00 db 00
00446A3D 00 db 00
00446A3E 00 db 00
00446A3F 00 db 00
00446A40 . 06 db 06
00446A41 . 50 61 6E 65 6>ascii "Panel1"
00446A47 D4 db D4
00446A48 02 db 02
00446A49 00 db 00
00446A4A 00 db 00
00446A4B 01 db 01
00446A4C 00 db 00
00446A4D . 06 db 06
00446A4E . 54 69 6D 65 7>ascii "Timer1"
00446A54 D8 db D8
00446A55 02 db 02
00446A56 00 db 00
00446A57 00 db 00
00446A58 02 db 02
00446A59 00 db 00
00446A5A . 06 db 06
00446A5B . 49 6D 61 67 6>ascii "Image1"
00446A61 DC db DC
00446A62 02 db 02
00446A63 00 db 00
00446A64 00 db 00
00446A65 02 db 02
00446A66 00 db 00
00446A67 . 06 db 06
00446A68 . 49 6D 61 67 6>ascii "Image2"
00446A6E E0 db E0
00446A6F 02 db 02
00446A70 00 db 00
00446A71 00 db 00
00446A72 02 db 02
00446A73 00 db 00
00446A74 . 06 db 06
00446A75 . 49 6D 61 67 6>ascii "Image3"
00446A7B E4 db E4
00446A7C 02 db 02
00446A7D 00 db 00
00446A7E 00 db 00
00446A7F 02 db 02
00446A80 00 db 00
00446A81 . 06 db 06
00446A82 . 49 6D 61 67 6>ascii "Image4"
00446A88 E8 db E8
00446A89 02 db 02
00446A8A 00 db 00
00446A8B 00 db 00
00446A8C 03 db 03
00446A8D 00 db 00
00446A8E . 05 db 05
00446A8F . 45 64 69 74 3>ascii "Edit1"
00446A94 EC db EC
00446A95 02 db 02
00446A96 00 db 00
00446A97 00 db 00
00446A98 04 db 04
00446A99 00 db 00
00446A9A . 07 db 07
00446A9B . 42 75 74 74 6>ascii "Button1"
00446AA2 F0 db F0
00446AA3 02 db 02
00446AA4 00 db 00
00446AA5 00 db 00
00446AA6 03 db 03
00446AA7 00 db 00
00446AA8 . 05 db 05
00446AA9 . 45 64 69 74 3>ascii "Edit2"
00446AAE F4 db F4
00446AAF 02 db 02
00446AB0 00 db 00
00446AB1 00 db 00
00446AB2 05 db 05
00446AB3 00 db 00
00446AB4 . 06 db 06
00446AB5 . 4C 61 62 65 6>ascii "Label1"
00446ABB F8 db F8
00446ABC 02 db 02
00446ABD 00 db 00
00446ABE 00 db 00
00446ABF 05 db 05
00446AC0 00 db 00
00446AC1 . 06 db 06
00446AC2 . 4C 61 62 65 6>ascii "Label2"
00446AC8 FC db FC
00446AC9 02 db 02
00446ACA 00 db 00
00446ACB 00 db 00
00446ACC 05 db 05
00446ACD 00 db 00
00446ACE . 06 db 06
00446ACF . 4C 61 62 65 6>ascii "Label3"
00446AD5 00 db 00
00446AD6 03 db 03
00446AD7 00 db 00
00446AD8 00 db 00
00446AD9 01 db 01
00446ADA 00 db 00
00446ADB . 06 db 06
00446ADC . 54 69 6D 65 7>ascii "Timer2"
这是什么?控件定义。不要问我怎么知道的,我不懂Delphi,但是我已经破了004,当我再次看到这一堆东西的时候,我立马做的一件事情就是返回到004去确定这件事情。004和005的这些相关代码对比之后,我就确定了,这一段代码就是控件定义的相关代码。而且也确定了两个输入框Edit1和Edit2的ID(暂且称为ID吧,我不知道Delphi如何称呼,但MFC里面叫ID)分别为0x2E8和0x2F0,但是与界面上的输入框如何对应我们还不确定。如果我们注意到与输入框相关的消息处理函数只有一个Edit2DblClick,对应函数地址为dd CKme002.00446FF8,那么这个对应关系也容易确定了。因为现在界面上,能输入的输入框只有一个,只要我们在00446FF8这个地址处下断,双击一下能不能断下来就可以了。经确定,界面中上面的是Edit1,下面的是Edit2。而Edit2现在是禁用的,要查找在哪里恢复可输入状态的,只需要在W32DAsm反汇编里搜索0x2F0这个ID就可以了。搜到了两处:
C005-2
C005-3
这两处一处是在FormCreate消息处理函数里,一处是在Panel1DblClick消息处理函数里,可以猜测FormCreate是设置为禁用,Panel1DblClick则是设置为启用,如果注意到第一段代码里有xor edx, edx,而第二段代码里有mov dl,01那么就可以知道我们的猜想是正确的了。 好了,在Panel1DblClick的函数地址dd CKme002.00446FDC处下断,在图片框没有图片的地方双击。果然进来了,但cmp dword ptr ds:[eax+0x308],0x29D后直接跳转了,无法启用Edit2,(可以先爆破确认是不是此处启用Edit2,不过这是肯定的)。那么现在就是要如何使eax+0x308的值变为0x29D了. 转到W32DAsm搜索0x308,找到如下代码:
C005-4
C005-5
不用说,很容易就可以确定,一个是在FormCreate消息处理函数里将其值初始化为0x28E;一个是在Button1MouseDown消息处理函数里的两处处理一个是加3,一个是设置为0x230D. 现在就是要考虑让其尤初始值0x28E变为0x29D的,显然不能让其变为0x230D,那么只能是每次加3喽,那么(0x29D-0x28E)/3=5,对啦,就是要Button1MouseDown消息处理函数执行5次,界面上只有一个按钮(控件定义中也再也没有其他按钮了,这个一定要确定,万一还有隐藏按钮呢),那么就是要在注册按钮上点击鼠标5次了,而且必须是右键!这就是我们在第二步里面要求的操作,千万别点鼠标左键,不然就没法得到0x29D了。 ds:[eax+0x308]的值已经变为0x29D了,这时再在图片框没有图片的地方双击,就可以启用Edit2了,第三步也完成。 下面就可以按照第四步的要求输入注册名和Edit2的内容,至于要求是怎么得到的当然是在Edit2的双击处理函数Edit2DblClick的地址处下断得来的dd CKme002.00446FF8。 这里只列举Edit2的要求,其他的自己跟踪代码。
c005-6
而且在这个函数里面还完成了给一个我们关注的内存地址赋值ebx+0x30C,这个虽然没有在Timer2Timer消息处理函数的程序爆破点处使用但在赋值ebx+0x314的时候用到了:
c005-7
到现在为止我们还是只解决了ebx+0x304和ebx+0x308的赋值问题,ebx+0x310、ebx+0x314、ebx+0x318还要继续解决。先在W32DAsm中搜索0x310得到以下代码:
c005-8
这两处又同时出现在了FormMouseMove的消息处理函数里面,通过下断dd CKme002.004470EC这个地址跟踪就可以得出第六步和第七步所要进行的操作,同时在FormMouseMove的消息处理函数里面也可以找到对ebx+0x314的赋值:
c005-9
下面再分析ebx+0x318的赋值问题,从FormCreate消息处理函数中知道它被初始化为0,
c005-10
而在Timer2Timer定时器处理函数中最终判断是否注册通过时需要ebx+0x318的值和ebx+0x314的值相等,而ebx+0x314的值在第六步和第七步已经计算出来了。通过搜索反汇编代码可以得知ebx+0x318的赋值是通过对图片1(“人之初“)、图片2(”性本善“)、图片3(”性相近“),图片4(”习相远“)的鼠标点击来完成的:
c005-11
c005-12
c005-13
c005-14
可以看出ebx+0x318(跟esi+0x318指向同一片内存)是通过点击4个图片加和得出来的,所以自己凑吧只要凑出来的点击加和ebx+0x318的值等于ebx+0x314的值就可以了! 至此,我们要得到的内存值都已经揭晓,要说最复杂的就算ebx+0x314的计算了,计算它首先要得到ebx+0x30C的值(取0,1,2,3),需要涉及到注册名和磁盘剩余空间等,是个工作量。下面是我写得一段不成熟的求ebx+0x30C代码: [Asm] 纯文本查看 复制代码 int GetEax(DWORD low, DWORD high)
{
__asm
{
push ebx
push esi
push edi
push edx
push ecx
mov ebx, 0x04
mov ecx, 0x40
xor esi, esi
xor edi, edi
mov edx, dword ptr ds:[ebp+0xC];
mov eax, dword ptr ds:[ebp+0x8];
mylabel:
shl eax, 1;
rcl edx, 1;
rcl esi, 1;
rcl edi, 1;
cmp edi, 0x00;
jb _looppd;
ja mylabel2;
cmp esi, ebx;
jb _looppd
mylabel2:
sub esi, ebx;
sbb edi, 0x00;
inc eax
_looppd:
loop mylabel;
mov eax, esi;
pop ecx
pop edx
pop edi
pop esi
pop ebx
}
}
extern "C" __declspec(dllexport) char * keygen(const char* user)
{
ULARGE_INTEGER FreeBytesAvailableToCaller,TotalNumberOfBytes,TotalNumberOfFreeBytes;
int len = strlen(user);
if(GetDiskFreeSpaceExA(NULL, &FreeBytesAvailableToCaller,&TotalNumberOfBytes,&TotalNumberOfFreeBytes))
{
int eax = GetEax(TotalNumberOfFreeBytes.LowPart+len+0x02, TotalNumberOfFreeBytes.HighPart);
//eax的值就是ebx+0x30C的值
}
return NULL;
}
|