?? 內存注冊機masm源碼.txt
字號:
內存注冊機的工作原理及masm源碼
[編程工具]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 ;再次寫入中斷代碼,對付同一地址多次中斷。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -