【破解作者】 qfejj
【作者邮箱】 qfejj@163.com
【使用工具】 OllyDbgv1.10;PEIDv0.93;RadASM2.1.0
【破解平台】 WindowsXP
【软件名称】 Graphic Workshop Professional v2.0a(patch98)
【下载地址】 http://crc.onlinedown.net:82/files/GraphicWorkshop.exe
【软件简介】 按照它自己的介绍,是一个专业的图片管理软件包,可以浏览图片(废话),转换图片格式,打印,建立可搜索图片数据库,帮助用户完成各种复杂的图形处理。我认识它才两天,不知道真的假的。不过那段提示用户注册的语音,倒是令人耳目一新。
【软件大小】 8.05MB
【加壳方式】 无壳
【破解声明】 最近闷的慌,昨天去华军拖了这个软件破了玩玩,纯粹兴趣使然。
--------------------------------------------------------------------------------
【破解内容】
册码格式xxxxx-xx-xxxxx-xx,软件注册码和安装程序注册码相通,我就通过调试安装程序获得软件的注册码。调试后发现软件处理用户名和注册码的大致过程如下:
第1步:用户名变换,第二段注册码变换,两者结果比较,通过则继续第2步,否则提示出错。
第2步:第一段和第三段注册码一起变换,第四段注册码变换,两者结果比较,通过则完成注册,否则提示出错。
下面详细分析该软件处理用户名和注册码所使用的具体算法。
OD载入目标程序,bpx GetWindowTextA,运行程序,输入注册码,确定后OD中断在每个GetWindowTextA,关键代码分析如下:
...
0040415A push eax
0040415B call < jmp.&USER32.GetWindowTextA> ; <--取用户名保存到eax所指地址4148b8
...(省略部分)
00404177 push eax
00404178 call < jmp.&USER32.GetWindowTextA> ; <--取第1段注册码保存到缓冲区
0040417D lea eax,dword ptr ss:[ebp-810]
00404183 push eax
00404184 lea edx,dword ptr ss:[ebp-40C]
0040418A push edx
0040418B call GraphicW.0040A110 ; <--从缓冲区复制第1段注册码到eax所指的地址12CCC0
00404190 add esp,8
00404193 push GraphicW.0041628F
00404198 lea ecx,dword ptr ss:[ebp-40C]
0040419E push ecx
0040419F call GraphicW.0040A110 ; <--在第1段注册码后添加短横线
...(省略部分)
004041BE push eax
004041BF call < jmp.&USER32.GetWindowTextA> ; <--取第2段注册码保存到缓冲区
004041C4 lea edx,dword ptr ss:[ebp-810]
004041CA push edx
004041CB lea ecx,dword ptr ss:[ebp-40C]
004041D1 push ecx
004041D2 call GraphicW.0040A110 ; <--从缓冲区复制第2段注册码到eax所指的地址12CCC6
004041D7 add esp,8
004041DA push GraphicW.00416291
004041DF lea eax,dword ptr ss:[ebp-40C]
004041E5 push eax
004041E6 call GraphicW.0040A110 ; <--在第2段注册码后添加短横线
...(省略部分)
00404205 push eax
00404206 call < jmp.&USER32.GetWindowTextA> ; <--取第3段注册码保存到缓冲区
0040420B lea ecx,dword ptr ss:[ebp-810]
00404211 push ecx
00404212 lea eax,dword ptr ss:[ebp-40C]
00404218 push eax
00404219 call GraphicW.0040A110 ; <--缓冲区复制第3段注册码到eax所指的地址12CCC9
0040421E add esp,8
00404221 push GraphicW.00416293
00404226 lea edx,dword ptr ss:[ebp-40C]
0040422C push edx
0040422D call GraphicW.0040A110 ; <--在第3段注册码后添加短横线
...(省略部分)
0040424C push eax
0040424D call < jmp.&USER32.GetWindowTextA> ; <--取第4段注册码保存到缓冲区
00404252 lea eax,dword ptr ss:[ebp-810]
00404258 push eax
00404259 lea edx,dword ptr ss:[ebp-40C]
0040425F push edx
00404260 call GraphicW.0040A110 ; <--从缓冲区复制第4段注册码到eax所指的地址12CCCF
00404265 add esp,8
00404268 lea ecx,dword ptr ss:[ebp-40C]
0040426E push ecx
0040426F push GraphicW.00414CBA
00404274 call GraphicW.0040A1A0 ; <--把所有注册码复制到地址414CBA
...
上面的代码可以不看,关键的在下面。
(1)这是软件处理用户名的具体过程:
...
004042A7 xor eax,eax
004042A9 mov al,byte ptr ds:[edi+4148B8] ; <--用户名每一位字母送入al开始处理,4148b8保存着用户名
004042AF push eax
004042B0 call GraphicW.0040F9E0
004042B5 pop ecx
004042B6 mov edx,edi
004042B8 and edx,7 ;因为下面[41308C]指向的数组中元素有限,以此更改元素地址,循环使用
004042BB xor ecx,ecx
004042BD mov cl,byte ptr ds:[edx+41308C] ;取出一个数组元素,调试后得到该数组为[80,40,20,10,08,04,02,01]
004042C3 xor eax,ecx ;<--用户名ASSII值与上面取出的元素值异或
004042C5 add dword ptr ss:[ebp-8],eax ;<--保存异或结果,累计
004042C8 inc edi ;<--edi增1,用于定位用户名下一个用户名字符
004042C9 cmp byte ptr ds:[edi+4148B8],0 ;<--比较用户名是否已经取完
004042D0 jnz short GraphicW.004042A7
004042D2 mov eax,dword ptr ss:[ebp-8] ;<--取出前面累计结果,进行下一步处理
004042D5 mov ecx,64
004042DA cdq ;<--将EAX中的数符号扩展为EDX:EAX中的64位数,EDX存放高32位
004042DB idiv ecx ;<--处理后的用户名除以64,AX中的符号位扩到DX中(关键)
004042DD mov edi,edx ;保存最终处理结果到edi
004042DF test edi,edi
004042E1 jnz short GraphicW.004042E4
...
(2)这是软件处理第1段和第3段注册码的具体过程,阅读时参考用户名的处理过程:
...
0040430B mov al,byte ptr ds:[edi+414CBA] ;<--第1段注册码每一位字母送入al开始处理
00404311 mov edx,edi
00404313 and edx,7
00404316 xor al,byte ptr ds:[edx+41308C] ;数组,[80,40,20,10,08,04,02,01]
0040431C xor ecx,ecx
0040431E mov cl,al
00404320 add dword ptr ss:[ebp-8],ecx ;<--保存处理结果,累计
00404323 inc edi
00404324 cmp byte ptr ds:[edi+414CBA],0 ;<--第1段注册码是否取完
0040432B je short GraphicW.00404332
0040432D cmp edi,5
00404330 jl short GraphicW.0040430B
00404332 xor edi,edi
00404334 jmp short GraphicW.0040434F
00404336 mov al,byte ptr ds:[edi+414CC3] ;<--第3段注册码每一位字母送入al开始处理
0040433C mov edx,edi
0040433E and edx,7
00404341 xor al,byte ptr ds:[edx+41308C] ;数组,[80,40,20,10,08,04,02,01]
00404347 xor ecx,ecx
00404349 mov cl,al
0040434B add dword ptr ss:[ebp-8],ecx ;<--保存处理结果,累计
0040434E inc edi
0040434F cmp byte ptr ds:[edi+414CC3],0 ;<--第3段注册码是否取完
00404356 je short GraphicW.0040435D
00404358 cmp edi,5
0040435B jl short GraphicW.00404336
0040435D mov eax,dword ptr ss:[ebp-8] ;<--取出第1段和第3段注册码处理的累计结果
00404360 mov ecx,64
00404365 cdq ;将EAX中的数符号扩展为EDX:EAX中的64位数,EDX存放高32位
00404366 idiv ecx ;<--处理后的注册码除以64,AX中的符号位扩到DX中(关键)
00404368 mov edi,edx ;<--保存最终处理结果到edi
0040436A test edi,edi
0040436C jnz short GraphicW.0040436F
...
(3)处理第2段和第4段注册码用的是同一个函数,具体分析如下:
...
0040FBCC push ebp
0040FBCD mov ebp,esp
0040FBCF push ebx
0040FBD0 push esi
0040FBD1 mov edx,dword ptr ss:[ebp+8] ;<--指向第2(4)段注册码地址
0040FBD4 xor ecx,ecx
0040FBD6 mov al,byte ptr ds:[edx] ;<--第2段注册码第1位字母送入al开始处理
0040FBD8 inc edx
0040FBD9 movsx ebx,al ;<--扩展到ebx
0040FBDC test byte ptr ds:[ebx+418A0D],1
0040FBE3 jnz short GraphicW.0040FBD6
0040FBE5 cmp al,2B ; +
0040FBE7 je short GraphicW.0040FBED
0040FBE9 cmp al,2D ; -
0040FBEB jnz short GraphicW.0040FBFC
0040FBED cmp al,2D
0040FBEF sete al
0040FBF2 and eax,1
0040FBF5 mov esi,eax
0040FBF7 mov al,byte ptr ds:[edx]
0040FBF9 inc edx
0040FBFA jmp short GraphicW.0040FC10
0040FBFC xor esi,esi
0040FBFE jmp short GraphicW.0040FC10
0040FC00 movsx eax,al ;---真正的处理过程,后面会以圆括弧内数字为顺序进行分析
0040FC03 add ecx,ecx ;<--第一次0+0=0,第二次(3)b+b=c
0040FC05 lea ecx,dword ptr ds:[ecx+ecx*4] ;<--第一次0+0*4=0,第二次(4)c+c*4=d
0040FC08 add ecx,eax ;<--第一次(1)ecx=注册码[1]+ecx=a,第二次(5)注册码[2]+d=e
0040FC0A mov al,byte ptr ds:[edx] ;<--第二(四)段注册码第2位字母送入al开始处理
0040FC0C add ecx,-30 ;<--第一次(2)ecx-30=a-30=b,第二次(6)eax-30=e-30=f
0040FC0F inc edx
0040FC10 cmp al,30 ; 30是数字0的ASCII值,表示这里需要数字
0040FC12 jl short GraphicW.0040FC18
0040FC14 cmp al,39 ; 39是数字9的ASCII值
0040FC16 jle short GraphicW.0040FC00
0040FC18 test esi,esi
0040FC1A je short GraphicW.0040FC22
0040FC1C mov eax,ecx
0040FC1E neg eax
0040FC20 jmp short GraphicW.0040FC24
0040FC22 mov eax,ecx ; <--(7)保存注册码的处理结果
0040FC24 pop esi
0040FC25 pop ebx
0040FC26 pop ebp
0040FC27 retn
...
下面是第2(4)段注册码处理算法的详细分析,咱们先来个约定:
注册码[1]表示第一个数字的ASCII值,注册码[2]表示第二个数字的ASCII值,我输入的第2(4)段注册码是78,
所以有注册码[1]=37,注册码[2]=38,算法如下:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
(1)注册码[1]+ecx=a
;ecx=0,结果a=注册码[1]--------------------------------------------------------37
(2)a-30=b
;b=注册码[1]-30---------------------------------------------------------------7
(3)b+b=c
;c=注册码[1]-30+注册码[1]-30--------------------------------------------------E(十进制14)
(4)c+c*4=d
;d=注册码[1]-30+注册码[1]-30+{c=注册码[1]-30+注册码[1]-30}*4------------------46
(5)注册码[2]+d=e
;e=注册码[2]+注册码[1]-30+注册码[1]-30+{注册码[1]-30+注册码[1]-30}*4----------7E(十进制126)
(6)e-30=f
;f=注册码[2]+注册码[1]-30+注册码[1]-30+{注册码[1]-30+注册码[1]-30}*4-30-------4E
(7)mov eax,f
;保存注册码处理结果到eax,前面已经分析过,用户名处理后的结果保存在esi
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
综上,得到eax=f=注册码[2]+注册码[1]-30+注册码[1]-30+{注册码[1]-30+注册码[1]-30}*4-30
整理一下,即
=============================
eax=注册码[2]+注册码[1]*A-210
=============================
注意是16进制的,不要弄错了;到此,所有的算法分析完毕,下面说说注册机的编写。
既然知道了算法,下一步当然是写注册机了。通过上面的分析,我们可以按照软件的变换方法处理用户名和第1、3段注册码,得到处理结果(软件使用esi保存),再由处理结果(esi)反推出第2、4注册码。也可以先得到2、4段注册码处理结果,再以此计算用户名,不过这样好像不妥,因为不容易得到自己喜欢的用户名。既然这样,我还是用第一种方法吧。那么,如何依靠esi计算出注册码呢?我们来看,由于注册码必须是数字,所以正确的注册码必须满足下面的关系:
esi+210-39 < 注册码[2]+注册码[1]*A < esi+210-30 ;<--39和30分别是9和0的ASCII值
即 esi+1D7 < 注册码[2]+注册码[1]*A < esi+1E0
既然可以确定范围,那反推注册码也就容易多了,这里只有两个数,我就用穷举来实现了。另外,第一、三、四段注册码和用户名无关,只要满足一定关系就可以,我就拿用户名换算了一下作为第一,三段的注册码了,最后以此求解第四段注册码。如果哪位哥们有更好的求解方法,还望赐教。
--------------------------------------------------------------------------------
注册机源码
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>> Graphic Workshop v2.0a(pach98) 注册机 >>>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.586
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include kernel32.inc
include user32.inc
include comctl32.inc
include gdi32.inc
includelib kernel32.lib
includelib user32.lib
includelib comctl32.lib
includelib gdi32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDDIALOG equ 10
IDOK equ 1
IDABOUT equ 2
IDREGNAME equ 4
IDREGCODE equ 3
IDC_STC1 equ 5
IDC_STC2 equ 6
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinDialog proto :HWND,:UINT,:WPARAM,:LPARAM
CalCode proto
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
RGB macro red,green,blue
xor eax,eax
mov ah,blue
shl eax,8
mov ah,green
mov al,red
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
MsgBoxText db 'Graphic Workshop v2.0a注册机',0dh
db ' 编写:qfejj',0dh
db ' Email:qfejj@163.com',0
MsgBoxCaption db 'About',0
MsgBoxTextErr db '用户名不能为空',0
MsgBoxWarning db '错误',0
String db 'Keygen For Everyone ',0
FontName db 'script',0
xorBuffer db 1 dup (80h,40h,20h,10h,08h,04h,02h,01h)
CodeBase db 1 dup (30h,31h,32h,33h,34h,35h,36h,37h,38h,39h)
.data?
hInstance HINSTANCE ?
hButton HINSTANCE ?
hNameEdit HINSTANCE ?
NameBuffer db 33 dup(?)
CodeBuffer db 20 dup(?)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
start:
invoke InitCommonControls
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke DialogBoxParam,hInstance,IDDIALOG,NULL,offset WinDialog,0
invoke ExitProcess,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WinDialog proc hWin,uMsg,wParam,lParam
LOCAL pc:PAINTSTRUCT
LOCAL hdc:HDC
LOCAL hfont:HFONT
mov eax,uMsg
.if eax==WM_CLOSE
invoke EndDialog,hWin,NULL
.elseif eax==WM_PAINT
invoke BeginPaint,hWin,addr pc
mov hdc,eax
invoke CreateFont,50,14,0,0,900,0,0,0,GB2312_CHARSET,\
OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,\
DEFAULT_QUALITY,FF_MODERN,addr FontName
invoke SelectObject, hdc, eax
mov hfont,eax
RGB 0,255,0
invoke SetTextColor,hdc,eax
RGB 165,61,107
invoke SetBkColor,hdc,eax
invoke TextOut,hdc,0,0,addr String,sizeof String
invoke SelectObject,hdc, hfont
invoke EndPaint,hWin,addr pc
.elseif eax==WM_INITDIALOG
invoke GetDlgItem,hWin,IDREGNAME
mov hNameEdit,eax
invoke GetDlgItem,hWin,IDOK
mov hButton,eax
invoke GetWindowTextLength,hNameEdit
invoke EnableWindow,hButton,FALSE
.elseif eax==WM_COMMAND
invoke GetWindowTextLength,hNameEdit
.if eax<6
invoke EnableWindow,hButton,0
.else
invoke EnableWindow,hButton,1
.endif
mov eax,wParam
.if eax==IDOK
invoke GetDlgItemText,hWin,IDREGNAME,addr NameBuffer,sizeof NameBuffer
.if eax==0
invoke MessageBox,hWin,addr MsgBoxTextErr,addr MsgBoxWarning,MB_ICONWARNING
.else
invoke CalCode ;计算注册码的函数
invoke SetDlgItemText,hWin,IDREGCODE,addr CodeBuffer
xor edx,edx
mov byte ptr[CodeBuffer+5],dl
mov byte ptr[CodeBuffer+8],dl
mov byte ptr[CodeBuffer+14],dl
.endif
.elseif eax==IDABOUT
invoke MessageBox,hWin,addr MsgBoxText,addr MsgBoxCaption,MB_OK
.endif
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
WinDialog endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
CalCode proc
LOCAL ResultName:dword
LOCAL ResultCode:word
LOCAL TempResult:dword
LOCAL MulResult:byte
;--------------
;用户名处理过程
;--------------
xor edi,edi
mov dword ptr[TempResult],0
xor eax,eax
ConvertName:
mov al,byte ptr[NameBuffer+edi]
push eax
mov dl,al
pop ecx
mov edx,edi
and edx,7
xor ecx,ecx
mov cl,byte ptr[xorBuffer+edx]
xor eax,ecx
add dword ptr[TempResult], eax
inc edi
cmp byte ptr ds:[NameBuffer+edi],0
jnz ConvertName
mov eax,dword ptr[TempResult]
mov ecx,64h
cdq
idiv ecx
mov dword ptr[ResultName],edx ;保存用户名处理结果
;-----------------------------------
;获得第2 段注册码,我用穷举来实现
;-----------------------------------
xor edi,edi
@1:
mov al,byte ptr[CodeBase+edi]
mov dl,0Ah
mul dx
mov byte ptr[MulResult],al
xor edx,edx
@2:
mov al,byte ptr[CodeBase+edx]
mov cl,byte ptr[MulResult]
add al,cl
mov ecx,dword ptr[ResultName]
add ecx,210h
cmp al,cl
je SaveCode1
inc edx
cmp edx,9
jl @2
inc edi
cmp edi,9
jl @1
SaveCode1:
mov al,byte ptr[CodeBase+edi]
mov byte ptr[CodeBuffer+6],al ;保存第二段第一位值
mov al,byte ptr[CodeBase+edx]
mov byte ptr [CodeBuffer+7],al ;保存第二段第一位值
;--------------------------------------
;获得第1、3 段注册码,我以变换用户名实现
;--------------------------------------
xor edi,edi
GetCode:
mov dl,byte ptr[NameBuffer+edi]
add edx,edx
and edx,7
mov al,byte ptr[CodeBase+edx] ;取单个注册码
mov byte ptr[CodeBuffer+edi],al ;填入第一段注册码
add edx,5
and edx,7
mov al,byte ptr[CodeBase+edx] ;取单个注册码
mov byte ptr[CodeBuffer+9+edi],al ;填入第三段注册码
inc edi
cmp edi,5
jl GetCode
;-----------------
;处理1、3 段注册码
;-----------------
xor edi,edi
mov dword ptr[TempResult],0
xor eax,eax
ConvertCode1:
mov al,byte ptr[CodeBuffer+edi]
push eax
mov dl,al
pop ecx
mov edx,edi
and edx,7
xor ecx,ecx
mov cl,byte ptr[xorBuffer+edx]
xor eax,ecx
add dword ptr[TempResult], eax
inc edi
cmp byte ptr ds:[CodeBuffer+edi],0
jnz ConvertCode1
xor edi,edi
xor eax,eax
ConvertCode2:
mov al,byte ptr[CodeBuffer+9+edi]
push eax
mov dl,al
pop ecx
mov edx,edi
and edx,7
xor ecx,ecx
mov cl,byte ptr[xorBuffer+edx]
xor eax,ecx
add dword ptr[TempResult], eax
inc edi
cmp byte ptr ds:[CodeBuffer+9+edi],0
jnz ConvertCode2
mov eax,dword ptr[TempResult]
mov ecx,64h
cdq
idiv ecx
mov word ptr[ResultCode],dx
;-----------------------------------
;获得第4 段注册码,我用穷举来实现
;-----------------------------------
xor edi,edi
@3:
mov al,byte ptr[CodeBase+edi]
mov dl,0Ah
mul dx
mov byte ptr[MulResult],al
xor edx,edx
@4:
mov al,byte ptr[CodeBase+edx]
mov cl,byte ptr[MulResult]
add al,cl
mov cx,word ptr[ResultCode]
add cx,210h
cmp al,cl
je SaveCode2
inc edx
cmp edx,9
jl @4
inc edi
cmp edi,9
jl @3
SaveCode2:
mov al,byte ptr[CodeBase+edi]
mov byte ptr[CodeBuffer+15],al ;保存第四段第一位值
mov al,byte ptr[CodeBase+edx]
mov byte ptr [CodeBuffer+16],al ;保存第四段第二位值
;-------------------
;为注册码添加分隔线
;-------------------
mov byte ptr[CodeBuffer+5],2Dh
mov byte ptr[CodeBuffer+8],2Dh
mov byte ptr[CodeBuffer+14],2Dh
ret
CalCode endp
end start
--------------------------------------------------------------------------------
资源文件
;>>>>>>>>>>>>>>>>>
;>>> rsrc.rc >>>>>
;>>>>>>>>>>>>>>>>>
#define IDDIALOG 10
#define IDREGNAME 4
#define IDREGCODE 3
#define IDC_STC1 5
#define IDC_STC2 6
#define IDOK 1
#define IDABOUT 2
IDDIALOG DIALOGEX 6,5,196,104
CAPTION "GraphicWorkshop v2.0a注册机"
FONT 8,"MS Sans Serif"
STYLE 0x10CA0800
EXSTYLE 0x00000000
BEGIN
CONTROL "",IDREGNAME,"Edit",0x50810000,76,38,106,13,0x00000000
CONTROL "",IDREGCODE,"Edit",0x50810800,76,60,106,13,0x00000000
CONTROL "Registration Name",IDC_STC1,"Static",0x50000201,12,38,60,13,0x00000000
CONTROL "Registration Code",IDC_STC2,"Static",0x50000201,12,60,58,11,0x00000000
CONTROL "计算",IDOK,"Button",0x50010000,40,81,43,15,0x00000000
CONTROL "关于",IDABOUT,"Button",0x50010000,114,81,43,15,0x00000000
END
----------------------------------------------------------------------------------
【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)