本人也只是一个初学者,为了对初学者有所帮助,文章的语言可能有点啰嗦。请高手指教!
   DvdComposer是一款制作DVD视频光盘的较好的软件,可以达到专业的DVD标准。它目前没有中文版,但可用eXeScope对其目录下的DvdCompLOC.dll字符串汉化,就完成了软件完全汉化的目的。英文不太好的用户很难完全发挥其功能。半年前在一本发行量很大的电脑杂志上看见有人介绍这个软件,他把“wizard”(向导)中的“Generate Project and Start Building DVD”解释为“制作并刻录”,其实该软件没有刻录功能,其本意是生成DVD方案(保存你的制作设计)并开始制作(制作时间很长),制作其实是生成刻录文件,最后还得要Nero来该录。好了扯远了,回归主题。
  
  声明:以下内容完全是研究和学习软件技术,若被他人利用,责任自负!
  
  半年前我发现了这款软件并喜欢了它,我企图获取它的注册码但没有成功,我只好将其爆破并汉化。爆破其实很简单:将00441BFA的call 0070C03E  改为nop ……nop,并将je改为jmp,再将0044C133的je 0044c1B0改为jnz或jmp就可以了。我在这里只想告诉软件作者花那么多的精力去设计注册算法,不如在防“爆破”上下点功夫。本软件的特点是:爆破容易,注册太难!
  
  1.拦截DcdComposer注册框的困惑
  开始我用OllDebug跟进所有出现注册对话框的Call,最后来到一个消息循环过程,无论怎样按住F8不放,都不出现注册框,按下F9,则弹出了注册框但却失去了与OD的联系。无论怎样反复注册和弹出注册码错误的消息,都不会重返OD。后用Spy.exe软件弄清了注册框是#32770类型的DialogBoxParamW对话框,用SoftICE拦截DialogBoxParamW、DialogBoxParamA或MessageBoxW居然毫无反映!用尽了所有工具仍然查不到注册框是在什么位置运行,实在是困惑了半年!甚至动摇了我(业余的)学习软件的信心。
  前不久我突然想到用菜单ID跟踪注册框,忙用eXeScope查到注册菜单ID=3A6,在OD中查找所有case分支中的3A6所在地一路跟踪,最后还是进入一个消息循环OD不能自拔,但我却恍然大悟,完全改变了思路就有了后面的结果。
  分析:
  (1)该软件调用DialogBoxParamW函数不是在程序中直接call(调用),而是发消息,所以OD总是进入消息循环。本软件要进入对话框过程必须拦截windows消息。
  (2)在OD监控下运行F8,程序运行很慢,大多的windows消息都会丢失,所以不会弹出对话框。
  (3)我发现若OD的所有交叉调用中有DialogBoxParam函数,用SoftICE设断可以拦截,若OD中不出现则SoftICE基本上都不能拦截。我猜想:因程序中没有DialogBoxParam函数名,运行加载时windows不会提交给该应用程序关于DialogBoxParam函数的地址,这时SoftICE不知道该在什么地址设断,故不能成功拦截。同样在给程序的地址设断时,SoftICE也经常失败,其原因是windows的页面交换机制使该地址所在的页面还没有提交给内存,SoftICE不能给一个不存在的内存设断,所以失败。这只是我的猜测,我不知道SoftICE是怎么工作的,请高手指教。
  
  2.在消息驱动机制中过滤有用信息 
  原来DcdComposer注册框是用消息来驱动的,OD是不能跟踪消息的。急忙打开SoftICE、Spy.exe,并让DvdComposer弹出对话框,用Spy取得对话框句柄,在SoftICE中用命令HWND获取对话框的入口地址(在Spy.exe中也有WinProc,但它指向的是USER32,不能用)。如果你在获取的入口地址设断,程序将无法运行,原因是窗口(即对话框)过程有大量的消息进入,若所有的消息都靠你手动放行,程序还能正常么?
  可在该过程的00708456处设计一个消息过滤器,让对我无用的消息安全通过。该地址的代码是:

0070844B  FF75 14 __________push    dword ptr [ebp+14]  ;消息中的lParam
0070844E  FF75 10 __________push    dword ptr [ebp+10]  ;消息中的wParam
00708451  FF75 0C __________push    dword ptr [ebp+C]  ;消息中的Msg
00708454  56 _______________push    esi      ;对话框句柄
00708455  50 _______________push    eax
00708456  E8 DFFEFFFF ______call    0070833A
0070845B  EB 10 ____________jmp     short 0070846D
将00708456的call 0070833A改为jmp 0074DC10(跳到程序空白字节处),加入下列代码:

  消息过滤器代码:
0074DC10  817D 0C110100000 _____cmp     dword ptr [ebp+C], 111 ;不是WM_COMMAND则跳
0074DC17  75 12 ________________jnz     short 0074DC2B
0074DC19  50 ___________________push    eax
0074DC1A  8B45 14 ______________mov     eax, [ebp+14]         ;不是子窗口来的命令则跳
0074DC1D  83F8 00 ______________cmp     eax, 0
0074DC20  74 08 ________________je      short 0074DC2A
0074DC22  25 FFFF0000 __________and     eax, 0FFFF         ;大量高位非0的lParam则跳  
0074DC27  74 01 ________________je      short 0074DC2A         ;是子窗口来的命令则断下 
0074DC29  90 ___________________nop             ;在此处设断  
0074DC2A  58 ___________________pop     eax
0074DC2B  E8 0AA7FBFF __________call    0070833A
0074DC30  E9 26A8FBFF __________jmp     0070845B

  OD中,先让程序弹出注册对话框,输入用户名和序列号后再在0074DC29设断(否则每在编辑框中输入一个字符都会中断一次),然后按下OK按钮。乖乖,狐狸终于被逮住了!一路单步跟进,来到了注册码计算的地方:004C51d0—004C5AB2(长达840行的代码!)。

  3.注册码算法原理
  先别忙看计算过程,840行的计算让你非晕死不可!看看是怎样处理用户名和假注册码的?但查找了相关的代码都找不到单独处理用户名或假注册码的地方,到是频频出现“注册码+固定字串+用户名”的字串,如输入用户名:ComputeUser,注册码:01234567-89abcdef-f9876543-210abcde,则参与计算的字串是“cdeff987000000450123DvdComposeriaMnOTaStHINKaSyOUdRUNKComputeUser”。计算过程中首先调用的是四个常数:A=01234567,B=89abcdef,C=fedcba98,D=76543210。我首先就肯定了计算是MD5算法,但是我错了!
  后来一想,问题就更大了,注册码和用户名同时参与MD5计算,计算结果和谁比较?软件作者把用户名和真注册码用当前程序中的算法生成一个固定的比较字串,软件作者能设计出这样的注册机吗?如果用数学表示应该是:
  F(用户名,真注册码)=正确的字串,而 F(用户名,假注册码)=不正确的字串
  按数学语言,前者是“多对一”函数(即不同的用户都生成相同的字串),后者是“一对一”函数,这是不可能的。因此完全不必去分析它的计算原理,只须看它怎样处理计算结果。
  在004C51D0设断,跟踪发现计算将连接的字串分成两段,前64字节和后64字节(不足添0),前64字节与前面的A、B、C、D常数运算,将结果取代ABCD,再把后64字节与新ABCD运算并再次取代ABCD,最后A|B|C|D=eax(|=or),将结果eax与esi比较,正确则注册成功。
  读者可能对以上叙述设有兴趣,但破解的关键也到了。
  首先关心的是esi中的数据,它与用户名没有关系,但与假注册码有关,如果注册码全是1,它也全是1(esi只用了6位),反正注册码设有用到的数字,它也不用,我把esi的6个数取代字串中的那6个0,把顺序还原后,竟然是假注册码完整的前20位!原来你向软件注册时,比较码也连同注册码一同给你发来了。这样,注册原理就清楚了。
  
  注册时程序取出注册码的第7—12位作为比较码,用0填充取走的数,截取前20位并交换假码的顺序再连接固定字串和用户名,计算后和比较码比较,完成注册过程。

  4. 注册机的实现思路
  程序要求注册码不得少于20位,多余的被忽略,用户名长度不受限制,总长度大于128位的部分被忽略。注册机实现思路是:
  (1)移植程序中004C51d0—004C5AB2的代码到注册机(并不轻松);
  (2)随机生成20位以上的数字,分组符“-”位置任意设定但不占位;
  (3)将第7—12位的数用0取代,并交换顺序连接字串和用户名,如:
  随机生成01234567-89abcdef-f9876543-210abcde,载取前20位01234567-89abcdef-f987,用0取代6789ab并交换顺序成:cdeff987000000450123
  连接字串DvdComposeriaMnOTaStHINKaSyOUdRUNK和用户名ComputeUser,生成长串cdeff987000000450123DvdComposeriaMnOTaStHINKaSyOUdRUNKComputeUser
  (3)按前面说的算法原理,将字串分成64位长度两段,分别带入计算过程,取出计算结果;如,按上面的随机数和用户名就生成:34FEF9BE,取后6位置换随机数的第7—12位为:012345FE-F9BEcdef-f9876543-210abcde,这就是一个可用的注册码(大小写没有关系)。
  
  说明:如果认为把程序在比较注册码的地方改为jmp,不管注册码是否正确都转跳,不是很好和爆破方法吗?如果这样,本程序一开始就会崩溃,原因是该计算和比较是一个公用通道,程序初始化时将对它附带的文件进行完整性检验,否则通过几个小时辛苦生成的DVD文件不能使用,则该软件公司就会关门了。

  • 标 题: 答复
  • 作 者:Isaiah
  • 时 间:2006-11-29 00:57

先佩服一下,50%的精华率.牛~
请问,怎么用消息调用DialogBoxParamW?

  • 标 题: 谢谢4楼指点!回答5楼提问。
  • 作 者:wulje
  • 时 间:2006-11-29 16:06

先谢谢4楼,回头再看看OD插件关于算法的提示。平常我很少使用插件。
再回答5楼的提问。
因为windows机制比较复杂,说消息调用DialogBoxParamW只是一种个人习惯说法。其实如果程序中的主线程或子线程中有DialogBoxParamW函数,则在程序初始化时,其DialogBoxParamW就被调用了,当该函数调用时就指向了一个“回调过程(地址)”,这个过程就是处理一切有关DialogBoxParamW窗口的各种命令。有时候一些对话框,如MessageBox等就常在这个过程中被调用,DialogBoxParamW也可以在这里被调用,如果我们拦截到了这个对话框,则控制权就停留在这个过程中,我们在这里查看,就可以找到我们需要的东西了。如果该过程不弹出任何对话框,OD或SoftICE就得不到该过程的控制权,我们就进不去了。进入这个过程的唯一办法就是windows消息,只要向这个过程发消息,控制权就交给该过程,直到事件被处理完毕后退出。我在文章中提到的进入消息循环,其实是进入了DialogBoxParamW内部中的消息循环,而我们的目的是进入该函数的回调过程。不知我说清楚没有,再次声明,我是初学,说错的地方就各位纠正。