注入优势
不需要远程创建线程API
API介绍
1挂起线程SuspendThread
2获取对方线程信息
BOOL GetThreadContext(
HANDLE hThread, //线程句柄
LPCONTEXT lpContext ); //传入传出参数:设备上下文
//注意:Context获取的寄存器信息需根据 Context.ContextFlags来决定
//如果ContextFlags为 CONTEXT_INTEGER:拿到的是普通寄存器 eax ebx ecx edx esi edi
//如果ContextFlags为 CONTEXT_CONTROL:拿到的是特殊寄存器 ebp eip eflags esp SegSs SegCs
3设置线程寄存器信息SetThreadContext,注意点:如果获取的不是主线程的话会出现有时能注入有时不能注入
4恢复线程ResumeThread
代码示例
.386
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive
include inject.inc
.code
inject_code:
pushad ;运行注入代码前请保存寄存器环境
pushfd
push 00646c72h
push 6f57206fh
push 6c6c6568h
mov ebx, esp
push 00000065h
push 6c746954h
mov ecx, esp
push MB_OK
push ecx
push ebx
push NULL
LABEL1:
mov eax, 12345678h
call eax
add esp,20
popfd ;运行完注入的代码之后还原寄存器环境,然后跳转回原来的位置继续执行代码
popad
REL: ;跳转回原来的位置继续执行
mov eax, 12345678h ;注意寄存器eax的破坏,最好不要用寄存器,可以直接写二进制代码,这样的话就用不上寄存器,也就不会破坏寄存器,因为用jmp 内存还是立即数跳转的是偏移,只有jmp 寄存器才会是当直接的地址跳转,所以我为了方便就写寄存器了,这样有可能会崩溃,因为你修改了寄存器,不知道会发送什么事情
jmp eax
ret
;
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke InitCommonControls
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
invoke ExitProcess,0
;########################################################################
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL @dwTid:dword
LOCAL @dwPID:dword
LOCAL @dwOld:dword
LOCAL @dwWriteBytes:dword
LOCAL @hThread:HANDLE
LOCAL @hProcess:HANDLE
LOCAL @context:CONTEXT
LOCAL @pBuffer:dword
LOCAL @hWnd:HWND
LOCAL @hSnapshot:HANDLE
LOCAL @te:THREADENTRY32
LOCAL @mymd:MODULEENTRY32
LOCAL @pfnMsgBox:DWORD
LOCAL @hUser32:DWORD
mov eax,uMsg
.if eax==WM_INITDIALOG
.elseif eax==WM_COMMAND
mov eax,wParam
.if ax == BTN_INJECT
;拿到窗口句柄
invoke FindWindow, offset g_ClassName, NULL
.if eax == NULL
ret
.endif
mov @hWnd, eax
;2.获取进程pid
invoke GetWindowThreadProcessId, @hWnd, addr @dwPID
mov @dwTid,eax ;这个api返回值就是创建窗口的线程ID也就是主线程ID
;3.打开进程
invoke OpenProcess,PROCESS_ALL_ACCESS, FALSE, @dwPID
.if eax == NULL
ret
.endif
mov @hProcess, eax
;遍历模块列表拿到calc的模块基址
;创建进程快照
invoke CreateToolhelp32Snapshot,TH32CS_SNAPMODULE,@dwPID
.if eax == INVALID_HANDLE_VALUE
jmp SAFE_EXIT
.endif
mov @hSnapshot,eax ;保存进程快照
mov @mymd.dwSize,sizeof @mymd
;拿到首个信息结构体
invoke Module32First,@hSnapshot,addr @mymd
.while 1
.if eax != 1 ;判断是否拿到模块
jmp SAFE_EXIT
.endif
invoke StrCmpCA,offset g_User32,addr @mymd.szModule
.if eax == 0
push @mymd.modBaseAddr ;将模块基址存入栈内
.break
.endif
invoke Module32Next,@hSnapshot,addr @mymd ;查找下一个模块
.endw
;修改内存保护属性
invoke VirtualProtect,offset inject_code, 1000h, PAGE_EXECUTE_READWRITE, addr @dwOld
;API地址重定位
invoke GetModuleHandle, offset g_User32
mov @hUser32, eax
invoke GetProcAddress,@hUser32, offset g_MessageBox
mov @pfnMsgBox, eax
mov esi, @hUser32
sub eax, esi ;offset
pop esi ;获取进程模块的基址
add eax, esi ;+base ;遍历模块列表
mov ebx, offset LABEL1
mov dword ptr [ebx+1], eax
;4.打开线程
invoke OpenThread,THREAD_ALL_ACCESS, FALSE, @dwTid
.if eax == NULL
ret
.endif
mov @hThread, eax
;5.挂起线程
invoke SuspendThread, eax
mov @context.ContextFlags,CONTEXT_CONTROL
;6.保存eip
invoke GetThreadContext, @hThread, addr @context
;7.申请内存
invoke VirtualAllocEx,@hProcess, NULL, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE
mov @pBuffer, eax
;9.重定位
mov eax,offset REL
mov ebx,@context.regEip
mov [eax+1],ebx ;重定位之后,运行完注入代码就会跳转到原来的位置继续执行代码,不影响原来的程序运行
;10.写入代码
invoke WriteProcessMemory,@hProcess, @pBuffer, inject_code, start-inject_code, addr @dwWriteBytes
;11.修改eip为申请的缓冲区地址
mov eax, @pBuffer
mov @context.regEip, eax
invoke SetThreadContext, @hThread, addr @context
;12.恢复线程
invoke ResumeThread, @hThread
SAFE_EXIT:
.if @hThread != NULL
invoke CloseHandle, @hThread
.endif
.if @hSnapshot != NULL
invoke CloseHandle, @hSnapshot
.endif
.if @hProcess != NULL
invoke CloseHandle,@hProcess
.endif
.endif
.elseif eax==WM_CLOSE
invoke EndDialog,hWin,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start
利用寄存器注入实现代码加密设计思路
编译后加密
流程:
1初始化函数或者在全局区,反正就是在代码入口前设置好筛选机异常,在筛选机异常回调里,将eip改成代码的位置
2记得第一行留个位置用来编译后好修改成int3的,随便写个mov eax,12h,自己记住多大,然后在处理异常函数里面跳过这个字节执行
3编译好文件
4打开二进制文件将代码段第一条预留的指令改成 int3指令,这样你即使nop调的话,其他的指令就会变成花指令,执行肯定错误
优势:
由于调试器会自动在入口下断点,这样没办法单步,所以实现了反调试