[编程工具]masm32(9.00)+RadAsm2.2.0.9
[调试工具]OD1.10
[调试平台]WinXP/SP2
先说明一下,我编写这个内存注册机只是为了学习一点程序的调试原理,它的实用性不说也罢,因为现的软件保护谁都知道,想这么容易取得注册码,门都没有,这也许正是刘健英放弃KeyMake的原因吧。我不想,也没有能力去步他的后尘,只是好奇,玩玩而已。
内存注册机实际是个超小的调试器,以调试的方式打开目标软件进程,在特定的地址设置 int 3 断点,程序运行到断点处被挂起,这个时候就可以用 ReadProcessMemory 或 GetThreadContext 函数取得目标软件特定内存或特定寄存器中的内容,显示出来就好了。只是前面提到的地址、寄存器等只有你跟踪了目标软件的注册算法后才会得到。根据注册算法的不同,注册机中的某些代码也可能要调整,所以我的代码对不会编程的人来说就是1000%的垃圾。
有些软件加了壳,不过我终于想出办法来对付它了,有壳我照样可以断下来,取到我想要的数据。杀毒软件对我的注册机也不感兴趣,大概是太臭了吧~O~。
内存注册机.Asm文件:
.386
.model flat ,
stdcall ;32 bit memory model
option casemap :
none ;case sensitive
include 内存注册机.inc
.code
start:
invoke InitCommonControls
invoke GetRegKey
invoke ExitProcess,0
;########################################################################
GetRegKey
proc uses edi esi
LOCAL @ThreadConText:CONTEXT
LOCAL @StartInfo:STARTUPINFO
LOCAL @DBGEvent:DEBUG_EVENT
LOCAL @TmpCode,@BakCode
LOCAL @FullPath[512]:
byte
invoke GetCommandLine
lea esi ,@FullPath
invoke lstrcpy,
esi ,
eax
mov edi ,
esi
xor edx ,
edx
xor ecx ,
ecx
cld
.while TRUE ;找最后一个“\”
lods byte ptr [
esi ]
inc ecx
.break .if !
al
.continue .if al !='\'
mov edx ,
ecx ;记下最后一个“\”的位置
.endw
add edi ,
edx
invoke lstrcpy,
edi ,
addr szEXEName
;生成绝对路径,否则由CreateProcess创建的进程可能会出问题
invoke GetStartupInfo,
addr @StartInfo
invoke CreateProcess,NULL,
addr @FullPath,NULL,NULL,
FALSE ,DEBUG_PROCESS+DEBUG_ONLY_THIS_PROCESS,NULL,NULL,
addr @StartInfo,
addr PI
.if !
eax ;创建debugee进程失败的话提示、结束
invoke MessageBox,NULL,
addr szMsgErr,NULL,MB_ICONERROR
jmp ENDPROC
.endif
.while TRUE
invoke WaitForDebugEvent,
addr @DBGEvent,INFINITE
;等待调试事件的发生
.break .if @DBGEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
;你退出了我也就退出吧!
.if TimesBreak
;debugee执行第一条指令之前我们不必做无用功
.if TimesBreak<BREAKFLAG
;已经取过注册码了吗?
mov eax ,@BakCode
.if al !=
byte ptr [OldCode]
;如果debugee加了壳,而且还没正确设置int 3断点
invoke ReadProcessMemory,PI.hProcess,BreakPoint,
addr @TmpCode,sizeof @TmpCode,NULL
mov eax ,@TmpCode
.if al ==
byte ptr [OldCode]
;壳已经解码完毕,可以设断点了
mov @BakCode,
eax ;记住,我们在此已经役过断点了,用它作标志,还要用它恢复现场
invoke WriteProcessMemory,PI.hProcess,BreakPoint,
addr BreakCode,sizeof BreakCode,NULL
;设断点
.endif
.endif
.endif
.endif
.if @DBGEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if @DBGEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
.if TimesBreak
;已经不止一次发生int 3中断的话
invoke CloseHandle,PI.hThread
;释放旧hThread的内存
invoke OpenThread,THREAD_GET_CONTEXT
or THREAD_QUERY_INFORMATION,
FALSE ,@DBGEvent.dwThreadId
;由ThreadID得到hThread 想依此来解决多线程问题,会不会有问题,请高人指点
mov PI.hThread,
eax ;保存备用
mov @ThreadConText.ContextFlags,CONTEXT_FULL
;访问所有的寄存器
invoke GetThreadContext,PI.hThread,
addr @ThreadConText
;取debugee的数据
dec @ThreadConText.regEip
;EIP已经指向int 3中断的下一条代码了,让它重新指向int 3
mov ecx ,@ThreadConText.regEip
;取debugee的EIP
.if ecx ==BreakPoint
;是我们要断下的地方吗?是就取出数据,并恢复现场
invoke WriteProcessMemory,PI.hProcess,
ecx ,
addr @BakCode,sizeof @BakCode,NULL
;恢复debugee原来的代码
invoke SetThreadContext,PI.hThread,
addr @ThreadConText
;修改debugee的EIP,让其继续运行
invoke GetModuleHandle,NULL
lea edx ,@ThreadConText
;把此结构的地址传给窗口,以便于显示
invoke DialogBoxParam,
eax ,IDD_DLGMAIN,NULL,
addr DlgProc,
edx ;弹出窗口,输出注册码
mov TimesBreak,BREAKFLAG
;设置标志,已经取过注册码了
.endif
.else ;如果debugee未加壳,就此两句可以设好断点,若加了壳就不起作用了,因为壳会修改掉我们的修改
invoke ReadProcessMemory,PI.hProcess,BreakPoint,
addr @BakCode,sizeof @BakCode,NULL
invoke WriteProcessMemory,PI.hProcess,BreakPoint,
addr BreakCode,sizeof BreakCode,NULL
.endif
inc TimesBreak
.endif
mov eax ,DBG_CONTINUE
jmp @F
.endif
mov eax ,DBG_EXCEPTION_NOT_HANDLED
@@:
invoke ContinueDebugEvent,@DBGEvent.dwProcessId,@DBGEvent.dwThreadId,
eax
.endw
invoke CloseHandle,PI.hProcess
invoke CloseHandle,PI.hThread
ENDPROC:
ret
GetRegKey
endp
DlgProc
proc uses ebx hWin:
HWND ,uMsg:
UINT ,
wParam :
WPARAM ,
lParam :
LPARAM
mov eax ,uMsg
.if eax ==WM_INITDIALOG
mov ebx ,
lParam
assume
ebx :
ptr CONTEXT
;ebx作为CONTEXT类型指针使用
;要显示什么数据,怎么显示,那你就去分析debugee的注册算法吧,我这里只是示例
;invoke ReadProcessMemory,PI.hProcess,RegKeyAddr,addr Buffer,sizeof Buffer,NULL ;从特定内存地址中取注册码
invoke wsprintf,
addr Buffer,
addr szFmt,[
ebx ].regEax
;从特定寄存器中取注册码
invoke SetDlgItemText,hWin,IDC_REGKEY,
addr Buffer
assume
ebx :nothing
.elseif eax ==WM_CLOSE
invoke EndDialog,hWin,0
.else
mov eax ,
FALSE
jmp @F
.endif
mov eax ,
TRUE
@@:
ret
DlgProc
endp
end start
内存注册机.Inc文件:
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc
includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib
DlgProc
PROTO :
HWND ,:
UINT ,:
WPARAM ,:
LPARAM
GetRegKey
PROTO
.const
IDD_DLGMAIN
equ 101
IDC_REGKEY
equ 1003
BREAKFLAG
equ 100000h
.data
szFmt
db '%1X',0
szMsgErr
db '本注册机必须与下面的软件放在同一目录内:',0dh
;中间不要有其它变量定义
szEXEName
db '例子.exe',0
;换成要注册的软件可执行文件名
BreakPoint
dd 0401131h
;可以改成要设断点的地址
OldCode
db 050h
;BreakPoint处的第一字节内容,帮我们判断壳是否操作完毕,完毕才能设断点
;RegKeyAddr dd 40305ch ;注册码在debugee中的存放地址
BreakCode
db 0CCh,90h,90h,90h
;最好别改动
TimesBreak
dd 0
;最好别改动
;#########################################################################
.data?
PI PROCESS_INFORMATION<>
Buffer
db 256dup(?)
************************************************************************************************************
我这个贴子只说了内存注册机的一般原理,未做成通用模板,因此需要会编程的使用者视具体情况添加或改动代码,比如多线程、多次中断在同一地址等。后面有人跟贴提到这两个问题。对于多线程问题,我想了个对策,代码见上文,调试了没问题。对于第二个问题,最好去读一下我转贴的《Win32调试API教程》之第三部分。我的大致思路是:
1
. 第一次正确发生int3中断时,设为单步模式,为的是后面有机会再写入断点。也可以设硬件断点,那是另一套思路了。
2
. 在单步事件到来时,重写入断点代码,暂不单步了
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
(前面的判断代码此处略)
mov @ThreadConText.ContextFlags, CONTEXT_CONTROL
invoke GetThreadContext, PI.hThread,
addr @ThreadConText
or @ThreadConText.regFlag,100h
;设为单步模式,为的是后面有机会再写入断点
invoke WriteProcessMemory,PI.hProcess,
ecx ,
addr @BakCode,sizeof @BakCode,NULL
;恢复debugee原来的代码
invoke SetThreadContext,PI.hThread,
addr @ThreadConText
;修改debugee的EIP,让其继续运行
inc TimesBreak
(后面的代码此处略)
.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP
;int3中断后紧接着单步一次
invoke WriteProcessMemory,PI.hProcess,BreakPoint,
addr BreakCode,sizeof BreakCode,NULL
;再次写入中断代码,对付同一地址多次中断。
马上要放假了,比较忙,没有时间详细设计和调试,只能这样了,算是抛砖引玉吧。假期我要好好休息一下,不会来这里了(要扔砖头的尽管扔,反正我不知道^O^)。希望收假后能看到更好的解决方法。
********************************************************************************************************************
附件中是asm源码和例子程序
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: