日前,帮朋友分析某软件算法,找到注册码很简单,因为是明码的,但是人家要求写个算法注册机,稍微看了下,大致是用GetSystemTime是取GTM时间参数,然后跟机器码经过一系列运算得到注册码,所以注册码也是动态的。写个算法注册机挺麻烦的。所以想做个内存注册机给对方,哪知人家说还要替别人算号的,况且该系列的算法模块都是一样的,不喜欢KEYMAKE做的内存注册机。
想起以前在论坛好像碰到过情况,搜索论坛,看了几篇有关进程、线程的文章后,又看了几位前辈写的LOADER源码,自己也动手实践了下。
原理非常简单,写个类似补丁的东西,创建一个进程启动目标程序,在机器码和注册码处插入INT3断点,然后调试进程,当停在机器码位置时,恢复代码,写入新机器码,通过原来软件的算法流程得到新的注册码。读出注册码就可以了。
为了更象算法注册机,我加入了注册机的界面。
先分析一下软件:
0046CDE8 |. 8B83 08030000
MOV EAX ,
DWORD PTR DS :[
EBX +308]
0046CDEE |. E8 71D0FCFF
CALL <test.@TControl@GetText>
; 取出机器码
0046CDF3 |. 8B45 E8
MOV EAX ,
DWORD PTR SS :[
EBP -18]
; 机器码
0046CDF6 |. 50
PUSH EAX ; EAX为机器码
0046CDF7 |. 8D55 E4
LEA EDX ,
DWORD PTR SS :[
EBP -1C]
0046CDFA |. 8B83 08030000
MOV EAX ,
DWORD PTR DS :[
EBX +308]
0046CE00 |. E8 5FD0FCFF
CALL <test.@TControl@GetText>
0046CE05 |. 8B55 E4
MOV EDX ,
DWORD PTR SS :[
EBP -1C]
; 机器码
0046CE08 |. 8B45 FC
MOV EAX ,
DWORD PTR SS :[
EBP -4]
; 注册码
0046CE0B |. 59
POP ECX ; 00E52224
0046CE0C |. E8 DFE8FFFF
CALL <test.sub_46B6F0>
; 关键算法,跟入
0046CE11 |. 84C0
TEST AL ,
AL
0046CE13 |. 0F84 EE000000
JE <test.loc_46CF07>
; 爆破
================== 关键算法
0046B6F0 >/$ 55
PUSH EBP
0046B6F1 |. 8BEC
MOV EBP ,
ESP
0046B6F3 |. 83C4 F0
ADD ESP ,-10
0046B6F6 |. 53
PUSH EBX
0046B6F7 |. 56
PUSH ESI
0046B6F8 |. 33DB
XOR EBX ,
EBX
0046B6FA |. 895D F0
MOV DWORD PTR SS :[
EBP -10],
EBX
0046B6FD |. 894D F4
MOV DWORD PTR SS :[
EBP -C],
ECX ; 机器码
0046B700 |. 8955 F8
MOV DWORD PTR SS :[
EBP -8],
EDX
0046B703 |. 8945 FC
MOV DWORD PTR SS :[
EBP -4],
EAX ; 假注册码
0046B706 |. 8B45 FC
MOV EAX ,
DWORD PTR SS :[
EBP -4]
0046B709 |. E8 8A92F9FF
CALL <test.@@LStrAddRef>
0046B70E |. 8B45 F8
MOV EAX ,
DWORD PTR SS :[
EBP -8]
0046B711 |. E8 8292F9FF
CALL <test.@@LStrAddRef>
0046B716 |. 8B45 F4
MOV EAX ,
DWORD PTR SS :[
EBP -C]
0046B719 |. E8 7A92F9FF
CALL <test.@@LStrAddRef>
0046B71E |. 33C0
XOR EAX ,
EAX
0046B720 |. 55
PUSH EBP
0046B721 |. 68 D1B74600
PUSH <test.sub_46B7D1>
0046B726 |. 64:FF30
PUSH DWORD PTR FS :[
EAX ]
0046B729 |. 64:8920
MOV DWORD PTR FS :[
EAX ],
ESP
0046B72C |. 33DB
XOR EBX ,
EBX
0046B72E |. 8D4D F0
LEA ECX ,
DWORD PTR SS :[
EBP -10]
0046B731 |. 8B55 F4
MOV EDX ,
DWORD PTR SS :[
EBP -C]
; 机器码
0046B734 |. 8B45 F8
MOV EAX ,
DWORD PTR SS :[
EBP -8]
0046B737 |. E8 20FEFFFF
CALL <test.sub_46B55C>
; 算法2,跟入
0046B73C |. A1 2CFA4600
MOV EAX ,
DWORD PTR DS :[46FA2C]
0046B741 |. 0FB600
MOVZX EAX ,
BYTE PTR DS :[
EAX ]
0046B744 |. 8B4D F0
MOV ECX ,
DWORD PTR SS :[
EBP -10]
; 算法2结果
0046B747 |. 8A4C01 CF
MOV CL ,
BYTE PTR DS :[
ECX +
EAX -31]
; 结果放入ECX,ECX就是注册码
0046B74B |. 8B75 FC
MOV ESI ,
DWORD PTR SS :[
EBP -4]
由此可见,0046CDF6中的EAX指向的是机器码,0046B747中的ECX指向的是注册码。知道这两点就可以写LOADER了,下面是完整代码:
.586
.model flat ,
stdcall
option casemap :
none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
include comdlg32.inc
includelib comdlg32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDD_DLG1
equ 1000
IDC_NAME
equ 1001
IDC_CODE
equ 1002
IDC_OK
equ 1005
IDC_ABOUT
equ 1006
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
BREAK_POINT1
equ 0046CDF6h
;第一个断点
BREAK_POINT2
equ 0046B747h
;第二个断点
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DlgProc
proto :HWND,:UINT,:WPARAM,:LPARAM
GetKey
proto
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
MsgboxText
db ' -=Author: langxang=-' ,0dh
db ' -=Email:langxang@126.com=-' ,0
MsgboxCaption
db '关于' ,0
MsgBoxText1
db '请输入远程机器码!' ,0
MsgBoxCaption1
db '提示' ,0
MsgBoxText2
db '请置于TEST.EXE软件的根目录!' ,0
MsgBoxText3
db '退出进程!' ,0
FileName
db ".\test.exe" ,0
Startup STARTUPINFO <>
processinfo PROCESS_INFORMATION <>
TotalInstruction
dd 0
AppName
db "test" ,0
int3
db 0cch
BUFFER
db 8
dup (?)
value
db 8
dup (?)
MCode
db 8
dup (?)
oldbyte1
db 50h
oldbyte2
db 8Ah
szFormat
db "%X" ,0
.data?
buffer
db 512
dup (?)
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>
context CONTEXT <>
uExitCode
dd ?
hInstance HINSTANCE ?
hDlg HINSTANCE ?
UserID
db 80
dup (?)
Serial
db 80
dup (?)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
start:
invoke GetModuleHandle ,NULL
mov hInstance,
eax
invoke DialogBoxParam ,hInstance,IDD_DLG1,NULL,
addr DlgProc,NULL
invoke ExitProcess ,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DlgProc
proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
mov eax ,uMsg
.if eax ==WM_CLOSE
invoke EndDialog ,hWin,0
.elseif eax ==WM_INITDIALOG
push hWin
pop hDlg
.elseif eax ==WM_COMMAND
mov eax ,wParam
.if eax ==IDC_OK
invoke GetDlgItemText ,hDlg,IDC_NAME,
addr UserID,
Sizeof UserID
.if eax ==0
invoke MessageBox ,hDlg,
addr MsgBoxText1,
addr MsgBoxCaption1,MB_OK
.elseif
invoke GetKey
invoke SetDlgItemText ,hDlg,IDC_CODE,
addr value
.endif
.elseif eax ==IDC_ABOUT
invoke MessageBox ,hDlg,
addr MsgboxText,
addr MsgboxCaption,MB_OK
.endif
.else
mov eax ,
FALSE
ret
.endif
mov eax ,
TRUE
ret
DlgProc
endp
GetKey
proc
pushad
;********************************************************************
; 创建进程
;********************************************************************
invoke CreateProcess ,
addr FileName, NULL, NULL, NULL,
FALSE , DEBUG_PROCESS
or DEBUG_ONLY_THIS_PROCESS, NULL, NULL,
addr startinfo,
addr pi
.if !
eax
invoke MessageBox ,hDlg,
addr MsgBoxText2,
addr MsgBoxCaption1,MB_OK
invoke ExitProcess ,NULL
.endif
;********************************************************************
; 调试进程
;********************************************************************
.while TRUE
invoke WaitForDebugEvent ,
addr DBEvent, INFINITE
.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
invoke MessageBox , 0, MsgBoxText3,
addr MsgBoxCaption1, MB_OK+MB_ICONINFORMATION
.break
;********************************************************************
; 如果进程开始,则将两个断点地址的代码改为INT3中断
;********************************************************************
.elseif DBEvent.dwDebugEventCode==CREATE_PROCESS_DEBUG_EVENT
invoke WriteProcessMemory ,pi.hProcess,BREAK_POINT1,
addr int3,1,NULL
;插入INT3
invoke WriteProcessMemory ,pi.hProcess,BREAK_POINT2,
addr int3,1,NULL
;插入INT3
;********************************************************************
; 中断触发的异常事件,并进入安全模式读写状态
;********************************************************************
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
mov context.ContextFlags, CONTEXT_FULL
invoke GetThreadContext , pi.hThread,
addr context
;********************************************************************
; 如果在机器码处中断,则恢复原来的代码,并写入新的机器码
;********************************************************************
.if context.regEip == BREAK_POINT1+1
dec context.regEip
invoke WriteProcessMemory ,pi.hProcess,BREAK_POINT1,
addr oldbyte1,1,NULL
;恢复代码
mov eax ,context.regEax
;机器码在EAX中
invoke WriteProcessMemory ,pi.hProcess,
eax ,
addr UserID,16h,NULL
;读入22位机器码
invoke SetThreadContext ,pi.hThread,
addr context
;设置生效
;invoke ReadProcessMemory,pi.hProcess,context.regEax,addr BUFFER,16h,NULL ;用于检测修改的机器码是否已经生效
;invoke MessageBox,0, addr BUFFER,addr AppName, MB_OK+MB_ICONINFORMATION
;********************************************************************
; 如果在注册码处中断,则恢复原来的代码,并读出注册码
;********************************************************************
.elseif context.regEip == BREAK_POINT2+1
dec context.regEip
invoke WriteProcessMemory ,pi.hProcess,BREAK_POINT2,
addr oldbyte2,1,NULL
;恢复代码
invoke ReadProcessMemory ,pi.hProcess,context.regEcx,
addr value,14h,NULL
;注册码在ECX中
;invoke SetDlgItemText,hDlg,IDC_CODE,addr value ;输出到注册码框
invoke SetThreadContext ,pi.hThread,
addr context
invoke TerminateProcess ,pi.hProcess,uExitCode
;强行退出
.break
.endif
invoke ContinueDebugEvent , DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.endif
.endif
invoke ContinueDebugEvent , DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
;********************************************************************
; 结束线程
;********************************************************************
invoke CloseHandle ,pi.hProcess
invoke CloseHandle ,pi.hThread
popad
ret
GetKey
endp
end start
文本框2中得到的就是利用原程序算法流程计算后得到的注册码,怎么样,俨然一个算法注册机。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: