一、前言
随着计算机的发展,有些问题显得相当重要,那就是如何保护自己的知识产权不受侵犯,如何保护自己的知识产权呢?一种较方便的办法就是对自己的软件设置一些密码和序列号,只有正确地输入了密码和序列号后才能进入到系统中。有些软件确实编写得相当不错,它凝聚着软件设计人员的心血和汗水,站在这个角度我们当然应该尊重设计人员的劳动,爱惜其劳动成果,应当从正当渠道购买相应的软件,如果手头确实吃紧,也不妨试图去破解这些密码,不论成功与否,对自己也是一个好的锻炼,并能从软件设计人员的代码中学习到不少的东西,而这些东西也许从教科书中难以学到,正是基于这点,本人尝试着用手工的方式进行密码的破解,在破解的过程中积累了一点经验,也进行了一些尝试,下面将自己在探测密码的一些想法谈一下,供交流和切磋,不足之处也一并请同行指正。
二、提出问题
Dos的换向技术是非常成熟和有效的,它可以将输入换向到一个文件,也可将一个文件作为一个应用程序的输入,使用合理得当,可以达到事半功倍的效果。本文试图从探测进球霸主的密码入手,来研究如何去实现将手工输入密码问题改为文件输入密码问题,从而实现没有人的太多干预下即可实现密码的自动输入和测试。
最近有很多球迷热衷于赌球,作为业余消遣和娱乐也是很不错的,它一方面提高了球迷们对足球的审美观念的提升,另一方面也为球迷们互相交流提供了一个不错的机会,以削减紧张的工作和学习的压力,更好地享受生活,更好地工作。进入进球彩霸画面时,首先显示的是序列号,然后要求用户输入密码,若用户输入到不正确的密码,系统显示“密码不正确,按任意键继续”,那么现在面临的问题是如何找到该处的地址呢?另外,如何让用户在输入不正确的密码后,可以在该处运行用户编写的一段代码,让计算机将用户输入的密码保存到自己的文件中,然后退出系统呢?因为这样可以排除该密码,只有输入了正确的密码后才能够正确地进入到系统中,那么又遇到另一个问题就是如何在显示该错误信息时将用户的代码置入到此处?最后还需要实现批处理和一些小的应用程序的编制,比如说产生大序列密码存储问题,变更换向输入文件内容的问题等等。
三、分析和解决问题
进球彩霸软件是基于DOS平台编写的,但是能够在Windows平台上正确地运行,它对于系统的要求不高,甚至一个386CPU也足够运行该程序,它是自带自库的,不需要系统的字库,这是其它系统所不具备的。
1.
寻求切入点
下面我们简要地分析上述问题,一是如何找到“密码不正确,按任意键继续”的地址。首先进入到window的记事本,写一段文字“密码不正确,按任意键继续”,然后将其存入到一个文件t1中,通过Debug得到该汉字的十六进机内码为“C3 DC C2 EB B2 BB D5 FD C8 B7 A3 AC B0 B4 C8 CE D2 E2 ”,它就是我们在文件中查找地址的依据。
首先在dos下将霸主.exe拷贝成一个普通的不带扩展名的文件bz,然后用debug打开bz 文件,用r命令发展该文件装入了八个段,对于每个段分别使用s命令搜索上述字符串,可以得到在第八个段下有该字符串,找到该字符串所在地址,并预以记录。
C:/cwbz>debug bz
-q
C:/cwbz>debug bz
-r
AX=0000 BX=0007 CX=56CD DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=136C ES=136C SS=136C CS=136C IP=0100 NV UP EI PL NZ NA PO NC
136C:0100 4D DEC BP
-s 136c:0 lfeff c3 dc c2 eb b2 bb
-s 236c:0 lfeff c3 dc c2 eb b2 bb
-s 336c:0 lfeff c3 dc c2 eb b2 bb
-s 436c:0 lfeff c3 dc c2 eb b2 bb
-s 536c:0 lfeff c3 dc c2 eb b2 bb
-s 636c:0 lfeff c3 dc c2 eb b2 bb
-s 736c:0 lfeff c3 dc c2 eb b2 bb
736C:4DE0
通过查看该段代码确实与汉字的机内码是一致的,至此基本上找到了汉字串的地址,下面结合机器码反汇编后的源程序进行分析,加上断点调试最后确定F029子程序是显示机内码汉字串的,它是将字库文件中的点阵汉字以象像的方式显示在屏幕上,逐一找反汇编后的源程序中的Call F029,然后在每个调用处设置断点,最后确定在代码中的07C1处为可能为“密码不正确,按任意键继续”的信息,进入子程序F029内部逐一跟踪后,发现其访问的数据段为信息数据区,所对应的地方显示的刚好为密码不正确,按任意键继续。
在F029子程序中逐一运行程序得到的内存数据与上述汉字的机内码串相吻合,因此上述判断地址是正确的,据此我们可以在该处作一个切点,将用户的代码加入到系统中,每当运行到此处时就让计算机运行自己的代码而终止应用程序的执行退出系统,以表示一次测试失败。
2
.加载切入点程序
下面需要作一个口袋程序,就是当用户在输入了不正确的密码后,让系统转入到用户自己编写的程序中,并作一些必要的记录,并将控制权返回到操作系统。
用户编写的程序的大致想法是,从某个已知文件中得到上次输入的密码,并将该密码数据添加到一个文件nokey1.txt中。
-u 100 14b
13B0:0100 50
PUSH AX
13B0:0101 53
PUSH BX
13B0:0102 51
PUSH CX
13B0:0103 52
PUSH DX
13B0:0104 1E
PUSH DS
13B0:0105 B8023D
MOV AX,3D02;功能:打开指定的文件
13B0:0108 BAB953
MOV DX,53B9;根据exe文件的位置略作改写
13B0:010B CD21
INT 21
13B0:010D 89C3
MOV BX,AX ;保存文件句柄号
13B0:010F B80242
MOV AX,4202 ;移动文件指针到文件尾
13B0:0112 B90000
MOV CX,0000
13B0:0115 BA0000
MOV DX,0000
13B0:0118 CD21
INT 21
13B0:011A 53
PUSH BX
13B0:011B B8013D
MOV AX,3D01 ;打开另一个文件供读
13B0:011E BA0000
MOV DX,0000
13B0:0121 CD21
INT 21
13B0:0123 89C3
MOV BX,AX
13B0:0125 B43F
MOV AH,3F ;读入指定14字节的密码及回车换行符
13B0:0127 BA0000
MOV DX,0000
13B0:012A B90E00
MOV CX,000E
13B0:012D CD21
INT 21
13B0:012F B43E
MOV AH,3E
13B0:0131 CD21
INT 21
13B0:0133 5B
POP BX
13B0:0134 B440
MOV AH,40 ;写入到指定的文件中
13B0:0136 B90E00
MOV CX,000E
13B0:0139 BA0000
MOV DX,0000
13B0:013C CD21
INT 21
13B0:013E B43E
MOV AH,3E ;关闭文件
13B0:0140 CD21
INT 21
13B0:0142 B44C
MOV AH,4C ;返回到DOS
13B0:0144 CD21
INT 21
13B0:0146 1F
POP DS
13B0:0147 5A
POP DX
13B0:0148 59
POP CX
13B0:0149 5B
POP BX
13B0:014A 58
POP AX
13B0:014B C3
RET
-d 14e l10
13B0:0140
72 69 ri
13B0:0150 61 6C 2E 74 78 74 00 6E-6F 6B 65 79 31 2E
al.txt.nokey1.
-
这是一段口袋程序,每当程序运行该处时就会自动将某一个固定文件中的数据添加到另一个文件中,同时退出到操作系统。
现在需要作两件事情,一是在程序运行到71c处时,填入用户代码的程序不能被运行,否则就会出错;二是EXE文件为浮动目标代码,需要修改上述各处的打开文件的偏移地址为一个给定值。
首先利用G命令在进球霸主的运行文件中设置两个断点,断点的布设可以参阅相应的运行文件反汇编成源文件,其中一个断点是7c1,另一个断点布设在相应的源文件中的较大的子程序处,并近可能远离该段代码,我尝试了一下g 7c1 8718 ,结果在7c1H处中断,因此可以比较安全地在8718H处加入用户编写的口袋代码程序。
同时运行三份命令处理程序,一份装入用户编写的口袋代码,另一份加载运行文件换成不带扩展名exe的普通文件(目的是能够改写该程序),再一份是装入运行文件进行比对。
在第一份命令处理程序中写入口袋程序,在另一份非exe文件中搜索运行文件7C1H处的特征串8D 16 B0 07 E8 61 E8和运行文件8718H处的特征字符串E8 96 62 2E 83 06 4D F4 04,分别位于非EXE文件下的第七个段的6b11H和EA68H处,这样在非EXE文件中进行修改时可以进行代码的定位。
现分别打开自己编写的代码窗口和非EXE文件所变更代码处,在非EXE文件窗口的当前DS的基础上加上6000,以此作为基准数据段,然后在偏移6B11H处输入一条调用指令CALL 8718,然后转移到该段的EA68H处再输入与自己编写的程序代码相似的代码,只不过是相应文件寻址的偏移要有所变更,在这里只需要修改对应的DX寄存器的值即可,当然DX寄存器的值需要根据相应的EXE文件的代码来填写,在非EXE文件中汇编代码完成后,然后将该非EXE文件拷贝成一个运行文件如bz.exe。
在DEBUG状态下反汇编8718看自己编写的代码,再在一些DX的地方参照文件名串在运行文件中的偏移以进行定位,当定位完成后,再回过头来修改非EXE文件中的代码,再行拷贝,经查无错后,即大功告成。
这样每次当输入密码时,若出错则会自动跳转到用户布设的口袋程序运行用户的代码,达到快速退出,当输入正确的密码后就会进入到系统中。
3
.输入换向
在DOS中可以采用换向符进行输入输出的换向,“>”换向输出,而“<”则称之为换向输入,在进入到进球霸主后,要求用户输入12位密码和回车及按一个键,因此可以编写一个文件serial.txt,其内容为“000000000000”后跟回车换行符,在DOS提示符下键入命令bz<serial.txt,这时原来需要输入密码的地方现在改由文件输入而不需要密码,而这正是想利用的,它避开了手工输入密码的繁琐,改由文件输入,只需要设计一个程序能够自动变更serial.txt中的内容就可以循环变更密码的输入,而这正是输入换向带来的好处。
下面介绍一下变更文件serial.txt的方法
² 创建文件serial.txt
² 打开文件allkey.txt,并头开始读入14字节数据到缓冲区
² 将该14字节数据写入到serial.txt,并关闭serial.txt文件
² 再次打开allkey.txt,从第二个密码序列开始(即下偏移14始),数据均上移一个密码序列
² 再关闭文件allkey.txt
下面是上述思路的文件清单:
.model small
.data
file db 'c:/allkey.txt',0
fileno dw ?
twofileno dw ?
filelen dw ?
file1 db 'c:/cwbz/serial.txt',0
fileno1 dw ?
buf db 512 dup(?)
.code
main:
mov ax,@data
mov ds,ax
mov ah,3ch
mov dx,offset file1
mov cx,0
int 21h
mov fileno1,ax
mov ah,3dh
mov dx,offset file
mov al,2
int 21h
mov fileno,ax
mov ah,3dh
mov dx,offset file
mov al,2
int 21h
mov twofileno,ax
mov ah,3fh
mov cx,14
mov bx,fileno
mov dx,offset buf
int 21h
mov ah,40h
mov cx,14
mov dx,offset buf
mov bx,fileno1
int 21h
mov ah,3eh
int 21h
mov ax,4200h
mov cx,0
mov dx,14
mov bx,fileno
int 21h
call changefile
mov bx,fileno
mov ah,3eh
int 21h
mov ah,3eh
mov bx,twofileno
int 21h
mov ah,4ch
int 21h
changefile:
push ax
push bx
push cx
push dx
read:
mov ah,3fh
mov bx,fileno
mov cx,512
mov dx,offset buf
int 21h
mov cx,ax
or ax,ax
jz nodata
mov ah,40h
mov dx,offset buf
mov bx,twofileno
int 21h
jmp read
nodata:
pop dx
pop cx
pop bx
pop ax
ret
end main
下面是产生allkey.txt文件的设计思路和程序清单
设计思路是:
设定初始数据9200206,然后将其后续的数据设定为“00000”,然后以追加的方式打开文件allkey.txt,变更后续的5位数据,并将其形成一个完整的12位密码和一个回车换行符后写入到文件allkey.txt中,直至后续密码再次回到“00000”为止。下面是实现上述思想的程序清单:
.model small
.data
file db "c:/allkey.txt",0
msg db '9200206'
msg1 db '00000',0dh,0ah
.code
main:
mov ax,@data
mov ds,ax
mov ax,3d02h
mov dx,offset file
int 21h
mov bx,ax
go:
call adddata
call chkdata
jc quit
mov ax,4202h
mov cx,0
mov dx,0
int 21h
mov ah,40h
mov cx,14
mov dx,offset msg
int 21h
jmp go
quit:
mov ah,3eh
int 21h
mov ah,4ch
int 21h
adddata:
push ax
push si
mov si,offset msg1+5
rept 5
dec si
mov al,[si]
inc al
mov [si],al
cmp al,3ah
jne q1
mov byte ptr [si],'0'
endm
q1:
pop si
pop ax
ret
chkdata:
push si
clc
mov si,offset msg1+5
rept 5
dec si
cmp byte ptr[si],'0'
jnz k1
endm
stc
k1:
pop si
ret
end main
4.
组装批处理
下面是一个批处理文件,它可以反复执行上述密码探测,直到一个给定的文件serial.txt不存在为止,当给定文件不存在时表示已经探测了一万次密码,再重新建立一个serial.txt文件,其内容可以为任意的数据,又可再次运行批处理,再次执行批处理又可执行一万次,基于这种方式希望尽可能将密码探测出来,如果某次运行时进入了进球霸主界面则表示该密码正确;当然,可能永远探测不出密码,但作为自己编程仍是一个有益的尝试。
@echo off
:1
if not exist serial.txt goto exit
newthink
type serial.txt
rem key
bz1<serial.txt
changemt
cls
goto 1
:exit
当然,上述程序仅是一个探测密码的尝试,从理论上讲如果密码数字是由该序列组成的,应该可以探测出来,我编写上述程序已经测试了近10万条数据的可能性没有得到正确的密码,对于12位的密码理论上的数值是10的12次方,如果有一个高性能的计算机按照上述方式进行测试最终将会得到正确的密码序列。