首页
社区
课程
招聘
如何从PB写的程序中提取注册算法,写出注册机
2006-4-9 00:54 8563

如何从PB写的程序中提取注册算法,写出注册机

2006-4-9 00:54
8563
如何从PB写的程序中提取注册算法,写出注册机

目标软件:机动车驾驶员模拟考试系统
加密方式:机器码+注册码,未注册有功能限制
作   者:  雷电(lzrlzr)
破解时间:2006-04-08
解密工具:PBKiller  Powerbuild10
破解说明:主要是说明如何去分析PB程序,并找出注册算法,Powerbuild的编程方法就不提了吧。

  太忙,好久没写了,把此文送给我的朋友。

  朋友发来一个程序:机动车驾驶员模拟考试系统,说让我试试,在安装时就发现安装进度上提示正在安装PB*.dll,我想肯定是个

Powerbuild写的程序。安装完以后,在安装目录中发现有PBVM80.DLL,确认了这个程序是用PB8.0写的软件。

  软件一启动主程序jdriver.exe,就会弹出一个要求注册的窗口,窗口的标题是:软件注册,上面有两个按钮,一个是:注册确认

,一个是:继续试用。随意输入一个注册码,就会提示当前注册码错误。

  于是打开PBKiller(感谢PBKiller的作者kivens,写出这个超强的反编译PB程序的工具,作者好象也在论坛中),对软件的安装目

录中的主程序和 dll 进行反编译,最后发现在private.dll 中有一个窗体w_registe,这个窗体的propertier(属性)中有
string title = "软件注册"
在窗体的controls(控制)中有几个按钮,
按钮cb_1 的属性是: string text = "注册确认"
按钮cb_2 的属性是:  string text = "继续试用"

那么从这个可以判断出w_registe窗体就是软件启动时的注册界面,按钮cb_1就是注册确认。那么在按钮cb_1的events(事件)
中可以找到clicked事件,事件中的代码如下(为了减少长度,我删掉了代码中的注释):

integer li_row
string ls_reg

if tag <> "" then
        return
end if

parent.sle_2.text = upper(trim(parent.sle_2.text))

if parent.sle_2.text = "" then
        msg("注册码不能为空,请重新输入!")
        parent.sle_2.setfocus()
        return
end if

SELECT count ( *) FROM 系统设置表 WHERE 编号 =0  using sqlca;

if li_row = 1 then
        update 系统设置表 SET 机器码 =' ' , 注册码 =' ' WHERE 编号 =0  using sqlca;

else
        DELETE FROM 系统设置表 WHERE 编号 =0  using sqlca;

        INSERT INTO 系统设置表 ( 编号 , 机器码 , 注册码 , 启用时间 , 剩余次数 ) VALUES ( 0 , ' ' , ' ' , getdate

( ) , 0 )  using sqlca;

end if

if sqlca.sqlcode <> 0 then
        rollback using sqlca;
        msg("保存注册码到系统中失败,请重启后再次注册!")
        return
end if

commit using sqlca;

if not gnv_app.of_reged() then              ;如果返回值不为真时,注册不成功
        msg("当前注册码错误,如果您申请注册码使用的是“" + iff

(parent.rb_2.checked,parent.rb_1.text,parent.rb_2.text) + "”,请先选中“" + iff

(parent.rb_2.checked,parent.rb_1.text,parent.rb_2.text) + "”再注册确认!")
        parent.sle_2.setfocus()
        return
end if

msg("感谢您对我们软件的注册使用。~n" + "您无私的帮助将是我们发展的最大动力!~n" + "为使您的注册立即生效,系统将退出

并请重新运行!")
parent.cb_2.tag = "T"
close(parent)
halt close
return

这段代码比较容易读懂,注册成功的关键就是gnv_app.of_reged(),这个过程反回值为真时,注册就会成功。
于是我继续反编译找,在jdriver.dll的nvo_app的function中找到如下代码of_reged () returns boolean

string ls_code
string ls_reg
string ls_cpu
string ls_disk

SELECT 注册码 FROM 系统设置表 WHERE 编号 =0 ORDER BY 编号 ASC  using sqlca;

if isnull(ls_reg) then
        ls_reg = ""
end if

ls_reg = trim(ls_reg)
ls_cpu = trim(of_reg_disk())
ls_disk = trim(of_reg_cpu())

if ls_cpu = "" and ls_disk = "" then
        msg("读取系统硬件序列号出错,可能原因如下:~n~n" + "1. 安装目录中文件 GetCpu.DLL 丢失或错误;~n" + "2. 安装

目录中文件 Md5.DLL 丢失或错误;~n" + "3. 本系统在当前操作系统中不能读取硬件数据;~n" + "4. 本机CPU序列号非法(此原因

需更换CPU);~n" + "5. 本机硬盘序列号非法(此原因需更换硬盘);~n" + "6. 其它原因......;")
        return false
end if

gnv_info.reg_code_registe = ls_reg

if ls_reg <> "" and upper(ls_reg) = upper(of_reg_arithmetic(ls_disk)) then
        gnv_info.reg_code_machine = ls_disk
        return true
end if

if ls_reg <> "" and upper(ls_reg) = upper(of_reg_arithmetic(ls_cpu)) then
        gnv_info.reg_code_machine = ls_cpu
        return true
end if

gnv_info.reg_code_machine = ls_disk + "," + ls_cpu
return false

从这个过程中可能看到,注册成功的关键条件就是
upper(ls_reg) = upper(of_reg_arithmetic(ls_disk))
或者是
upper(ls_reg) = upper(of_reg_arithmetic(ls_cpu))

这也就是作者设计的,注册码可以和本机的cpu绑定,也可以和本机的硬盘号绑定。

呵呵,继续找,在jdriver.dll的nvo_app的function中找到如下代码:
of_reg_arithmetic (string as_code)  returns string

char lc_key1[]
char lc_key2[]
char lc_base_char
integer li_key1
integer li_key2
integer li_key
string ls_base
string ls_usercode
integer li_len
integer li_loop
integer li_tmp
integer li_right_mod
integer li_strlen

if as_code = "" then
        return as_code
end if

ls_base = gnv_info.basestr
li_len = 50

if len(as_code) < 50 then
        li_len = len(as_code)
end if

for li_loop = 1 to li_len
        lc_key1[li_loop] = mid(as_code,li_loop,1)
        li_key1 += asc(lc_key1[li_loop])
next

for li_loop = 1 to li_len step 2
        lc_key2[li_loop] = mid(as_code,li_loop,1)
        li_key2 += asc(lc_key2[li_loop])
next

li_key = li_key1 + li_key2 + 50
li_strlen = 25

if gnv_info.ib_project then
        li_strlen = 13
end if

for li_loop = 1 to li_strlen
        li_len = len(ls_base)
        li_tmp = mod(li_key,li_len)
        li_tmp = li_tmp + li_right_mod

        if li_tmp > li_len then
                li_tmp = mod(li_tmp,li_len)
        end if

        if li_tmp = 0 then
                li_tmp = 1
        end if

        lc_base_char = char(mid(ls_base,li_tmp,1))
        ls_usercode = ls_usercode + string(lc_base_char)
        li_right_mod = li_tmp
        ls_base = left(ls_base,li_tmp - 1) + right(ls_base,li_len - li_tmp)
next

return f_public_replaceall(upper(ls_usercode),"O","0")    ;把字母“O”,全部用数字“0”代替

呵呵,OK,这就是根据机器码生成注册码的字符串变换算法了,用这个就可以写出注册机了。

算法总结:
  这个程序根据机器的cpu,硬盘号,经过变换得到机器码(机器码的生成算法可以在of_reg_cpu ()  ,of_reg_disk ()中得到

,就是把机器的cpuid或硬盘id分成两部分,分别进行md5变换,再组合成机器码。),根据机码变换得到注册码,再和输入的注册码

字符串进行比较,如果相同就注册成功。作者在验证注册码是否正确时使用的注册算法是正向算法,不需要写出逆算法就可以写出注

册机。作者在算法中设计的比较人性化的一点就是注册码中不使用字母“O”,全部用数字“0”代替。

到此,在 pb中新建一个工程,增加一个窗体,写出注册机,我用的是Powerbuild10(pb我用的也不熟,只会作注册机,呵)。
(算法中用到的f_public_replaceall()定义如下,
f_public_replaceall (string as_string1,string as_string2,string as_string3)  returns string
存在于OBJECT.dll中)

最后,就此程序加密方法的改进,给作者一点建议(呵呵,不好意思啊):

1、程序中不要包含注册机生成器窗体部分(private.dll 中有一个窗体w_register,就是作者的注册码生成器的窗体,更要命的是包

含完整的注册码生成算法,和我写的注册机代码几乎是一样的,呵呵)

2、取机器 cpuid和硬盘id时,最好放到一个比较好的程序中,最好包含在程序中,不要使用单独的dll文件(我想作者可能是使用了

别人提供的标准函数),这种dll自身就不安全,可以对dll进行修改,让它在所有的机器上生成一模一样的机器码,只需注册一套,

就可以达到复制分发的目的。
3、注册算法的使用要合理,尽理使用一些非对称的加密算法,注册码的生成算法和校验算法不要同时出现在发布的程序中,这样很难

逆出加密算法。 

最后,请大家支持国产软件,如果经济条件可以,对软件有需要,还是可以注册一下。

附作者的注册机生成算法(我的就不写出来了,和这个差不多):

parent.sle_2.text = trim(parent.sle_2.text)

if tag = "" then
        parent.cb_1.triggerevent(clicked!)
        return
end if

parent.mle_1.text = ""

if len(parent.sle_2.text) <> 13 then
        msg("机器码长度必须为13位!")
        return
end if

parent.sle_2.text = f_public_replaceall(parent.sle_2.text,"O","0")
parent.sle_3.text = upper(gnv_app.of_reg_arithmetic(parent.sle_2.text))

if gnv_info.ib_project then
        return
end if

parent.mle_1.text = "尊敬的客户,您好:~r~n" + "感谢你注册软件《" + gnv_info.soft_name_cn + "》成功!~r~n" + "你

的机器码:" + parent.sle_2.text + "~r~n" + "您的注册码:" + parent.sle_3.text + "~r~n" + "注册时间:" + string

(today(),"YYYY年MM月DD日") + "~r~n~r~n" + "希望本软件能为你的驾车增添更多的乐趣!~r~n~r~n" + "软件升级与题库更新请

访问:" + gnv_info.comp_http + "~r~n" + "如有软件疑问请发邮件到:" + gnv_info.comp_mail + "~r~n" + "再次感谢你对

本软件的支持,祝你顺利过关!~r~n" + "                      劲维科技~r~n" + "                       " + string

(today(),"yyyy-mm-dd")
return

[培训]《安卓高级研修班(网课)》月薪三万计划,掌 握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞0
打赏
分享
最新回复 (14)
雪    币: 97
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
魔女 2006-4-9 09:30
2
0
哈哈哈哈,今天坐到沙发了.哈哈哈哈
雪    币: 97
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
魔女 2006-4-9 09:37
3
0
雪    币: 231
活跃值: (40)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
lzq1973 3 2006-4-9 09:39
4
0
高,好文
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
keygen 2006-6-25 16:57
5
0
利害,可惜我看不懂
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
machine 2006-8-15 14:45
6
0
哈哈,这个软件我也破了一次,不过注册机是用VFP写的,我只会用VFP程序!!
雪    币: 200
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
backuper 2006-8-15 15:19
7
0
顶一下 ,, !!  老大  好崇拜
雪    币: 143
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
二毛 2006-8-16 19:15
8
0
不是汇编的不看
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
皮蛋瘦肉 2006-8-17 10:04
9
0
学习中~~
今天搞定了一个软件
keymake的内存注册机也做出来了
高兴就到这里来看看
没有想到有学了不少的东西
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tebie 2006-8-17 18:22
10
0
可惜就是很少人用PB做出一些很有价值的软件...
雪    币: 215
活跃值: (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wch2004 2006-8-18 08:57
11
0
这个软件算法简单,很容易破解,如果算法设计难点,不容易破的,如果作者用混淆器将PB文件加密了,那个PBKILL就无法看到上面的代码了。
雪    币: 204
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhizhengfu 2006-9-13 11:03
12
0
高手,学习中,
雪    币: 199
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caiweb 2006-9-13 14:21
13
0
注册的时候代码如下:
略了一些次要的东西。
reg_rand1 = mid(trim(parent.sle_1.text),2,1)
reg_temp1 = trim(f_conversion(trim(mid(trim(parent.sle_1.text),3,100)),reg_rand1,"2"))
reg_rand2 = mid(trim(parent.sle_1.text),1,1)
reg_temp2 = f_conversion(trim(reg_temp1),reg_rand2,"2")

if trim(f_hdd_id()) <> trim(reg_temp2) then
        messagebox("提示信息","您的注册码错误!")
        return
end if

update "register" SET "enregister" =' ' , "register" =' ' , "save_id" =' ' WHERE "register"."serno" =1  using sqlca;
/* SQL Parameters List
0-> :parent.sle_djm.text
1-> :parent.sle_1.text
2-> :parent.sle_id.text
*/

if sqlca.sqlcode = 0 then
        commit using sqlca;
        w_main.triggerevent(open!)
        m_right_mouse.m_20.enabled = true
        messagebox("提示信息","注册码成功! 请重新启动本软件!")
        parent.cb_2.triggerevent(clicked!)
        close(w_main)
else
        rollback using sqlca;
        messagebox("提示信息","无法注册!!!~n错误代码:" + string(sqlca.sqlcode) + "~n错误内容:" + sqlca.sqlerrtext)
        return
end if

其中,f_conversion()涵数已知,f_hdd_id()已知就是id号,登记码已知,如何反算出注册码?
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jinhf 2006-9-14 11:29
14
0
8月份考驾照,碰见过这个软件,偶已经搞定,所以楼主的文章我不用全看就知道怎么搞的.不过还是谢谢楼主的共享精神,由于我比较忙,当时注册后没写下任何破文,楼主正好代劳了,谢谢
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jinhf 2006-9-14 11:31
15
0
最初由 caiweb 发布
注册的时候代码如下:
略了一些次要的东西。
reg_rand1 = mid(trim(parent.sle_1.text),2,1)
reg_temp1 = trim(f_conversion(trim(mid(trim(parent.sle_1.text),3,100)),reg_rand1,"2"))
reg_rand2 = mid(trim(parent.sle_1.text),1,1)
........

可以看看我的一个破文,见我的博客http://21jhf.52blog.net 或许对你有所帮助
游客
登录 | 注册 方可回帖
返回