palm玩了这么久,用的大部分xx软件基本上都是别人搞定的,偶连palm上的程序也只写过"hello world",更不要提破解了。前一段时间心血来潮,想研究一下palm系统的程序,于是找了个注册机制看起来比较简单的程序QED(柿子只捡软的捏:)),决定将其肢解而后快。

palm os 5.0以下配置的主机使用的是MOTOROLA公司出品的龙珠68000处理器作为核心。具体资料参见MOTOROLA公司提供的《M68000 8-/16-/32-Bit Microprocessors User’s Manual》。将被肢解的程序是QED,palm上级负盛名的文本编辑器。偶用的是猪哥汉化修改的QED XP版本,去tompda(玩palm的都知道)搜一下就能找到。

开始动手吧。肢解这种程序最好的工具自然是偶们大名鼎鼎的IDA。打开IDA,载入QED,选择处理器的时候选68000,然后稍等片刻,分析结果就出来了。仔细看了片刻,得出了一个结论——不懂*_*大家不要急着扔臭鸡蛋阿,给你满屏幕68k的汇编代码你也会头晕的。现在只好临阵磨枪了。去MOTOROLA和palmone的网站搜罗一通,找了几本书和SDK下来,恶补了一阵子,结果什么也没有看下去,只好硬着头皮继续上。

打开PALM的模拟器,载入QED,选择注册,随便输入试炼码,将弹出一个对话框告诉你“输入无效”。看来就从这里下手了。在SDK中扒了半天,从sdk40-doc.zip中找到《Palm OS Programmer’s API Reference》。经过与鸟文的一番搏斗,终于在找到了这样的一个函数:FrmCustomAlert。原型是:UInt16 FrmCustomAlert (UInt16 alertId, const Char *s1, const Char *s2, const Char *s3)。介绍说:Create a modal dialog from an alert resource and display the dialog until the user taps a button in the alert dialog.看来就是它了。为了保险起见,用OnBoard C写了个“hello world”验证,结果样式和“输入无效”一样。然后祭出秘密武器RsrcEdit,发现了痕迹——Talt 1000——这就是那个无情的“输入无效”。

在IDA中查找FrmCustomAlert,经过几次定位,找到了一处可疑的地方:
code0001:00001D4E loc_2E0E:                               ; CODE XREF: sub_2D72+88j
code0001:00001D4E                 move.l  a0,-(sp)
code0001:00001D50                 move.w  #$3E8,-(sp)
code0001:00001D54                 systrap FrmCustomAlert()
这里调用的就是资源号为1000的对话框。向上看:
code0001:00001D2A                 lea     byte_2D50,a0
code0001:00001D2E                 move.l  a0,-(sp)
code0001:00001D30                 lea     dword_2D52,a0
code0001:00001D34                 move.l  a0,-(sp)
code0001:00001D36                 lea     dword_2D57,a0
code0001:00001D3A                 bra     loc_2E0E
code0001:00001D3E ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
code0001:00001D3E
code0001:00001D3E loc_2DFE:                               ; CODE XREF: sub_2D72+68j
code0001:00001D3E                 lea     off_2D5B+3,a0
code0001:00001D42                 move.l  a0,-(sp)
code0001:00001D44                 lea     dword_2D65,a0
code0001:00001D48                 move.l  a0,-(sp)
code0001:00001D4A                 lea     dword_2D6A,a0
这些地址对应的字符串分别是:“感谢您”和“输入无效”,看来就是这个地方了。

全部复制上来:
code0001:00001CB2 ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
code0001:00001CB2
code0001:00001CB2 ; Attributes: bp-based frame
code0001:00001CB2
code0001:00001CB2 proc            sub_2D72()              ; DATA XREF: sub_137A:loc_14C4o
code0001:00001CB2
code0001:00001CB2 var_54          = -$54
code0001:00001CB2 var_40          = -$40
code0001:00001CB2 var_32          = -$32
code0001:00001CB2 arg_0           =  8
code0001:00001CB2
code0001:00001CB2                 link    a6,#-$34
code0001:00001CB6                 movem.l d3-d4/a2,-(sp)
code0001:00001CBA                 movea.l arg_0(a6),a2
code0001:00001CBE                 clr.b   d3
code0001:00001CC0                 systrap FrmGetActiveForm()
code0001:00001CC4                 move.l  a0,d4
code0001:00001CC6                 move.w  (a2),d0
code0001:00001CC8                 cmpi.w  #$18,d0
code0001:00001CCC                 beq     loc_2E4C
code0001:00001CD0                 bhi.s   loc_2D9C
code0001:00001CD2                 cmpi.w  #9,d0
code0001:00001CD6                 beq.s   loc_2DA8
code0001:00001CD8                 bra     loc_2F2C
code0001:00001CDC ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
code0001:00001CDC
code0001:00001CDC loc_2D9C:                               ; CODE XREF: sub_2D72+1Ej
code0001:00001CDC                 cmpi.w  #$1C,d0
code0001:00001CE0                 beq     loc_2E26
code0001:00001CE4                 bra     loc_2F2C
code0001:00001CE8 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
code0001:00001CE8
code0001:00001CE8 loc_2DA8:                               ; CODE XREF: sub_2D72+24j
code0001:00001CE8                 cmpi.w  #$646,8(a2)
code0001:00001CEE                 bne.s   loc_2E26    ;或取试炼码
code0001:00001CF0                 moveq   #-$28,d3
code0001:00001CF2                 add.l   a6,d3
code0001:00001CF4                 move.l  d3,-(sp)
code0001:00001CF6                 bsr     sub_576C    ;获取系统当中的用户名
code0001:00001CFA                 move.l  d3,-(sp)
code0001:00001CFC                 bsr     sub_57AE    ;以用户名为参数,计算注册码
code0001:00001D00                 move.w  d0,d3      ;真正的注册码出现了
code0001:00001D02                 move.l  dword_82AC-A5BASE(a5),-(sp)
code0001:00001D06                 systrap MemHandleLock()
code0001:00001D0A                 move.l  a0,-(sp)
code0001:00001D0C                 systrap StrAToI()    ;把试炼码转换为数字
code0001:00001D10                 clr.l   d1
code0001:00001D12                 move.w  d3,d1
code0001:00001D14                 lea     $50+var_40(sp),sp
code0001:00001D18                 cmp.l   d0,d1      ;关键的比较,随便修改一下,比如cmp.l d0,d0即可实现爆破
code0001:00001D1A                 bne.s   loc_2DFE    ;转跳意味着失败
code0001:00001D1C                 lea     unk_82D4-A5BASE(a5),a0
code0001:00001D20                 move.w  d3,$42(a0)
code0001:00001D24                 move.b  #1,byte_8290-A5BASE(a5)
code0001:00001D2A                 lea     byte_2D50,a0
code0001:00001D2E                 move.l  a0,-(sp)
code0001:00001D30                 lea     dword_2D52,a0
code0001:00001D34                 move.l  a0,-(sp)
code0001:00001D36                 lea     dword_2D57,a0
code0001:00001D3A                 bra     loc_2E0E
code0001:00001D3E ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
code0001:00001D3E
code0001:00001D3E loc_2DFE:                               ; CODE XREF: sub_2D72+68j
code0001:00001D3E                 lea     off_2D5B+3,a0
code0001:00001D42                 move.l  a0,-(sp)
code0001:00001D44                 lea     dword_2D65,a0
code0001:00001D48                 move.l  a0,-(sp)
code0001:00001D4A                 lea     dword_2D6A,a0
code0001:00001D4E
code0001:00001D4E loc_2E0E:                               ; CODE XREF: sub_2D72+88j
code0001:00001D4E                 move.l  a0,-(sp)
code0001:00001D50                 move.w  #$3E8,-(sp)    ;对话框编号1000
code0001:00001D54                 systrap FrmCustomAlert()  ;决定命运的对话框

跟进sub_57AE,看看注册算法如何:

code0001:000046EE ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
code0001:000046EE
code0001:000046EE ; Attributes: bp-based frame      ;注册算法
code0001:000046EE
code0001:000046EE proc            sub_57AE()              ; CODE XREF: sub_2D72+4Ap
code0001:000046EE                                         ; sub_399E+42p
code0001:000046EE
code0001:000046EE var_C           = -$C
code0001:000046EE arg_0           =  8
code0001:000046EE
code0001:000046EE                 link    a6,#0
code0001:000046F2                 movem.l d3-d4/a2,-(sp)
code0001:000046F6                 movea.l arg_0(a6),a2
code0001:000046FA                 clr.w   d3                    ;d3放的是最终的注册码,先清空
code0001:000046FC                 move.l  a2,-(sp)
code0001:000046FE                 move.l  a2,-(sp)
code0001:00004700                 systrap StrToLower()    ;全部字符转换为小写
code0001:00004704                 suba.l  a0,a0                 ;偏移计数器
code0001:00004706                 move.b  (a2),d1               ;取用户名的第一个字符
code0001:00004708                 bra     loc_580A              ;好戏开始了:)
code0001:0000470C ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
code0001:0000470C
code0001:0000470C loc_57CC:                               ; CODE XREF: sub_57AE+5Ej
code0001:0000470C                 move.w  d1,d0
code0001:0000470E                 addi.w  #-$61,d0        ;ascll码转换
code0001:00004712                 cmpi.w  #$19,d0         ;是否是26个字母中的一个
code0001:00004716                 bhi.s   loc_5804        ;非法字符,跳过(是否大于#$19)
code0001:00004718                 clr.w   d4              ;清空循环计数器
code0001:0000471A
code0001:0000471A loc_57DA:                               ; CODE XREF: sub_57AE+54j
code0001:0000471A                 move.w  d3,d2           ;备份已经计算好的注册码
code0001:0000471C                 moveq   #$F,d0
code0001:0000471E                 lsr.w   d0,d2           ;d2>>#$0F->d2
code0001:00004720                 lsl.w   #1,d3           ;d3<<#$01->d3
code0001:00004722                 tst.b   d1
code0001:00004724                 bge.s   loc_57EA
code0001:00004726                 ori.w   #1,d3           ;d3|#$1->d3
code0001:0000472A
code0001:0000472A loc_57EA:                               ; CODE XREF: sub_57AE+36j
code0001:0000472A                 move.w  d1,d0
code0001:0000472C                 andi.w  #$7F,d0 ; ''
code0001:00004730                 move.w  d0,d1           ;待计算的字符加上#$7F->d1
code0001:00004732                 lsl.w   #1,d1           ;d1<<#$01->d1
code0001:00004734                 tst.b   d2
code0001:00004736                 beq.s   loc_57FC        ;if(d2==0) goto
code0001:00004738                 eori.w  #$1021,d3       ;d3^#$1021->d3
code0001:0000473C
code0001:0000473C loc_57FC:                               ; CODE XREF: sub_57AE+48j
code0001:0000473C                 addq.w  #1,d4           ;循环计数器加1
code0001:0000473E                 cmpi.w  #7,d4           ;循环7次
code0001:00004742                 ble.s   loc_57DA        ;是否结束循环
code0001:00004744
code0001:00004744 loc_5804:                               ; CODE XREF: sub_57AE+28j
code0001:00004744                 addq.w  #1,a0           ;偏移计数器加1,继续下一个
code0001:00004746                 move.b  (a2,a0.w),d1    ;(a2+a0)->d1,取偏移计数器指向的字符
code0001:0000474A
code0001:0000474A loc_580A:                               ; CODE XREF: sub_57AE+1Aj
code0001:0000474A                 ext.w   d1              ;结束了么?
code0001:0000474C                 bne.s   loc_57CC        ;没有结束就继续处理
code0001:0000474E                 tst.w   d3              ;是否成功的计算出注册码
code0001:00004750                 bne.s   loc_5816        ;通向光明大道
code0001:00004752                 move.w  #$27BB,d3       ;嘿嘿,看到没有,#$27BB,万能注册码呃。不过使用的前提是你得有个非法

的用户名
code0001:00004756
code0001:00004756 loc_5816:                               ; CODE XREF: sub_57AE+62j
code0001:00004756                 move.w  d3,d0           ;d0中就是正确的注册码
code0001:00004758                 movem.l var_C(a6),d3-d4/a2
code0001:0000475E                 unlk    a6
code0001:00004760                 rts                     ;回去喽
code0001:00004760 ; End of function sub_57AE

分析到此结束。我们现在有三种方法搞定这个软件了。
1。爆破:修改00001D18处的代码为cmp.l d0,d0或者修改00001D1A处的转跳即可。
2。后门:构造一个非法的用户名,然后用10171作为注册码试试看:)不过非法的用户名可不是随便就可以构造出来的,最简单的实验方法就是,打开模拟器,不要修改默认的用户名,然后填入10171,自己看结果吧。
3。注册机:根据上面的分析,也基本上可以写出注册机了。现在QED的注册机满天飞,这里我也就不献丑了。

这次分析搞了很长时间(主要是偶太懒,而且每次看见鸟文就头晕,继而犯困,继而会周公,继而周而复始  ),结果犯了大忌,心浮气躁,分析只得草草结束。虽有虎头蛇尾之嫌,不过幸而没有中断,总算是搞完了。
第一次搞PALM程序,加上偶临阵磨枪的68k汇编,不免错误百出,不当之处望大虾指正。


今天是伟大的五四运动八十六周年纪念日,向八十六年前那些“真的勇士”们致敬!



monkeycz
2005年05月04日(纪念伟大的五四运动)