上篇写了通过补丁的方式利用软件本身的算法模块做出注册机的实例,但是都是针对无壳的情况。这篇来讲一下通过硬件断点利用同样的原理对加壳软件打补丁。
关于调试寄存器DRx的作用可以查Intel的手册,加密软件技术内幕也有提及到.其中Dr0-Dr3的四个调试寄存器是存放中断的地址,用于设置硬件断点,Dr4、Dr5英特尔保留使用功能,至少现在没什么用。Dr6、Dr7这两个寄存器的作用是用来记录Dr0-Dr3中下断的地址的属性,如下的硬件断点读还是写,或者是执行;是对字节还是对字,或者是双字。
我们还是以上次的那个CRACKME作为例子,我对它加了个简单的ASPACK压缩壳。
00405001 > 60 PUSHAD ; 壳入口
00405002 E8 03000000 CALL crackme.0040500A
00405007 - E9 EB045D45 JMP 459D54F7
0040500C 55 PUSH EBP
0040500D C3 RETN
...
//机器码运算
004013B4 |. E8 01080000 call <jmp.&user32.GetDlgItemTextA>
004013B9 |. 8D05 98334000 lea eax, dword ptr [403398] ; 压入机器码
004013BF |? 68 98334000 push 00403398 ; EAX中放着机器码
...
00401B90 |. 50 push eax ; EAX中就是暗码
00401B91 |? 68 7E324000 push 0040327E ; 转字符串
00401B96 |? 68 48334000 push 00403348
00401B9B |? E8 08000000 call <jmp.&user32.wsprintfA>
00401BA0 |. 83C4 0C add esp, 0C
...
//注册码变形
0040131E |. E8 97080000 call <jmp.&user32.GetDlgItemTextA> ; 取注册码
00401323 |. 6A 08 push 8
00401325 |. 68 A8324000 push 004032A8
0040132A |. E8 B4FEFFFF call 004011E3 ; 转16进制
0040132F |. 8BF0 mov esi, eax
00401331 |. 81C6 A679F3FF add esi, FFF379A6 ; 加FFF379A6h
00401337 |. 81F6 DDAEEC04 xor esi, 4ECAEDD ; 异或4ECAEDDh
0040133D |. 81EE C78AA900 sub esi, 0A98AC7 ; 减0A98AC7h
00401343 |. 56 push esi ; 结果
00401344 |. 68 7E324000 push 0040327E ; 转字符串
00401349 |. 68 F8324000 push 004032F8
0040134E |. E8 55080000 call <jmp.&user32.wsprintfA>
...
//对比结果
004010E8 |. 68 48334000 push 00403348 ; 机器码运算后的字符串
004010ED |. 68 F8324000 push 004032F8 ; 注册码运算后的字符串
004010F2 |. E8 F30A0000 call <jmp.&kernel32.lstrcmpA> ; 比较
004010F7 |. 0BC0 or eax, eax
004010F9 |. 75 19 jnz short 00401114
假如以上次的方法对004013BF和00401B90处下INT3断点,当外壳把程序代码解密后,所修改的0CCh字节就会被解密后的代码覆盖,将导致int3断点不起作用。所以我们改下硬件断点。
我们先让程序停在原始的入口处,再在004013BF和00401B90处下硬件断点,断下后就可以为所欲为了...,详细代码如下:
.586
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include comdlg32.inc
include macros.inc
include masm32.inc
includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib comdlg32.lib
includelib masm32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDD_DLG1 equ 1000
IDC_NAME equ 1001
IDC_CODE equ 1002
IDC_OK equ 1005
IDC_ABOUT equ 1006
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
dwOrgOEP equ 00405001h ;程序原始入口
BREAK_POINT1 equ 004013BFh ;第一个断点
BREAK_POINT2 equ 00401B90h ;第二个断点
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
DlgProc proto :HWND,:UINT,:WPARAM,:LPARAM
GetKey proto
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.CONST
DR0_ENABLED EQU 000000001b
LOCAL_EXACT_BPM_ENABLED EQU 100000000b
.data
MsgboxText db ' -=Author: langxang=-',0dh
db ' -=Email:langxang@126.com=-',0
MsgboxCaption db '关于',0
FileName db 'crackme.exe',0
int3 db 0cch
value db 8 dup(?)
buffer db 8 dup(?)
oldbyte db 8 dup(?)
szFormat db "%X",0
dwCountSS dd 0
dwCountBP dd 0
ProcessInfo db "File Handle: %lx ",0dh,0Ah
db "Process Handle: %lx",0Dh,0Ah
db "Thread Handle: %lx",0Dh,0Ah
db "Image Base: %lx",0Dh,0Ah
db "Start Address: %lx",0
Startup STARTUPINFO <>
processinfo PROCESS_INFORMATION <>
.data?
UserID db 80 dup (?)
Serial db 80 dup (?)
uExitCode dd ?
hInstance HINSTANCE ?
hDlg HINSTANCE ?
startinfo STARTUPINFO <>
pi PROCESS_INFORMATION <>
DBEvent DEBUG_EVENT <>
context CONTEXT <>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.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,CTXT("请输入远程机器码"),CTXT("提示!"),MB_OK
.elseif
invoke GetKey
.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,CTXT("不能创建进程"),CTXT("错误!"),MB_OK
invoke ExitProcess,NULL
.endif
xor eax,eax
mov dwCountBP, eax
mov dwCountSS, eax
;********************************************************************
; 调试进程,进入循环调试
;********************************************************************
.while TRUE
invoke WaitForDebugEvent, addr DBEvent, INFINITE
.if DBEvent.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT
invoke MessageBox, 0, CTXT("退出进程..."), CTXT("提示!"), MB_OK+MB_ICONINFORMATION
.break
;********************************************************************
; 异常中断
;********************************************************************
.elseif DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT
.if DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_BREAKPOINT
;********************************************************************
; 第一次中断时在原始入口点处设置断点
;********************************************************************
inc dwCountBP
.if dwCountBP==1
invoke ReadProcessMemory, pi.hProcess, dwOrgOEP, addr oldbyte, 1, 0 ;在dwOrgOEP中读出一个字节
invoke WriteProcessMemory, pi.hProcess, dwOrgOEP, addr int3, 1, 0 ;写入INT3断点
;********************************************************************
; 第二次中断,中断在起先设置的原始入口点,恢复代码,在机器码处设置硬件断点
;********************************************************************
.elseif dwCountBP==2
mov context.ContextFlags, CONTEXT_CONTROL
invoke GetThreadContext, pi.hThread, addr context
dec context.regEip
invoke WriteProcessMemory, pi.hProcess, dwOrgOEP, addr oldbyte, 1, 0 ;恢复入口代码
invoke SetThreadContext, pi.hThread, addr context
mov context.ContextFlags, CONTEXT_DEBUG_REGISTERS
invoke GetThreadContext, pi.hThread, addr context
mov context.iDr0, BREAK_POINT1 ;设置硬件断点
mov context.iDr7, LOCAL_EXACT_BPM_ENABLED + DR0_ENABLED
invoke SetThreadContext, pi.hThread, addr context
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
.elseif DBEvent.u.Exception.pExceptionRecord.ExceptionCode==EXCEPTION_SINGLE_STEP ;单步运行模式
;********************************************************************
; 第三次中断,中断在机器码处,清除断点,在注册码处设置硬件断点
;********************************************************************
inc dwCountSS
.IF dwCountSS == 1
mov context.ContextFlags, CONTEXT_FULL
invoke GetThreadContext, pi.hThread, addr context
mov eax,context.regEax ;机器码在EAX中
invoke WriteProcessMemory,pi.hProcess,eax,addr UserID,sizeof UserID,NULL ;写入机器码
invoke SetThreadContext, pi.hThread, addr context
; invoke ReadProcessMemory,pi.hProcess,context.regEax,addr buffer,0ch,NULL ;用于检测修改的机器码是否已经生效
; invoke MessageBox,0, addr buffer,CTXT("输入的新机器码为:"), MB_OK+MB_ICONINFORMATION
mov context.ContextFlags, CONTEXT_DEBUG_REGISTERS
invoke GetThreadContext, pi.hThread, addr context
mov context.iDr0, BREAK_POINT2
mov context.iDr7, LOCAL_EXACT_BPM_ENABLED + DR0_ENABLED
invoke SetThreadContext, pi.hThread, addr context
invoke ContinueDebugEvent, DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE
.continue
;********************************************************************
; 第四次中断,中断在注册码处,读出注册码,清除硬件断点,恢复线程
;********************************************************************
.elseif dwCountSS == 2
mov context.ContextFlags, CONTEXT_FULL
invoke GetThreadContext, pi.hThread, addr context
mov context.ContextFlags, CONTEXT_FULL
mov EAX,context.regEax ;暗码在EAX中
add EAX,0A98AC7h
xor EAX,4ECAEDDh
sub EAX,0FFF379A6h
invoke wsprintf,addr value,addr szFormat,eax
invoke SetDlgItemText,hDlg,IDC_CODE,addr value
mov context.iDr0, 0
mov context.iDr7, 0
invoke SetThreadContext,pi.hThread, addr context
invoke TerminateProcess,pi.hProcess,uExitCode ;强行退出
.break
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId,DBEvent.dwThreadId, DBG_CONTINUE
.endif
.endif
invoke ContinueDebugEvent, DBEvent.dwProcessId,DBEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED
.endw
;********************************************************************
; 结束线程
;********************************************************************
invoke CloseHandle, pi.hThread
invoke CloseHandle, pi.hProcess
popad
ret
GetKey endp
end start