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日(纪念伟大的五四运动)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课