目標:在掃雷中注入一個messagebox彈窗;
方法:開啟一個程式(掃雷的程式),申請記憶體,寫入messagebox;
另外啟動一個執行緒,讓整個程式碼跑起來
專案建立
注入程式碼
.586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.data
g_szWinmine db "掃雷", 0
g_szKernel db "Kernel32", 0
g_szLoadLibrary db "LoadLibraryA", 0
g_szGetProcAddress db "GetProcAddress", 0
g_hKer dd 0
g_dwPid dd 0
g_hProc dd 0
g_pAddr dd 0
g_dwBytesWrited dd 0
g_dwOldProc dd 0
g_szMsg db "你被注入了", 0
g_szTitle db "不要擔心,重啟就行", 0
.code
MSGBOX:
int 3
invoke MessageBox,NULL,offset g_szMsg,offset g_szTitle, MB_OK
start:
;開啟程式
invoke FindWindow,NULL,offset g_szWinmine ;視窗控制程式碼
invoke GetWindowThreadProcessId,eax,offset g_dwPid ; 程式id
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,g_dwPid ;開啟程式
mov g_hProc,eax
;申請記憶體
invoke VirtualAllocEx,g_hProc,NULL,1000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
mov g_pAddr,eax
;生成機器碼寫入到申請的記憶體,跨程式寫記憶體
invoke WriteProcessMemory, g_hProc, g_pAddr, offset MSGBOX, offset start - offset MSGBOX, offset g_dwBytesWrited
; 建立執行緒,執行程式碼
invoke CreateRemoteThread,g_hProc,NULL,0,g_pAddr,NULL,NULL,NULL
invoke ExitProcess,eax
end start
掃雷的可執行檔案放進去,先執行了掃雷,在執行程式,導致直接掃雷崩潰,,,,,,,,
除錯分析過程記錄
在chongdingwei.exe中記憶體申請成功,在02770000處;
在winmine.exe中找到這個位置檢視,4個push和1個call;
在chongdingwei.exe再檢視
檢視chongdingwei.exe,找到二進位制資料,4個push(00結尾)和1個call;
在winmine.exe的02770000處下斷點,當chongdingwei.exe啟動執行緒時,在這個位置斷下來
這裡重新開始跑了,申請的地址變了,不過不影響分析,繼續進行,新申請的地址 02b60000
當chongdingwei.exe啟動執行緒
在winmine.exe的02770000處這個位置斷下來;
繼續單步走一下就崩潰了; 這裡是怎麼看出來崩掉的 ?????????
檢視日誌: 檢視 - 記錄
這裡日誌沒有報錯資訊
用x64dbg開啟調一下,這裡的日誌資訊比較詳細;
winmine.exe用x64dbg開啟,先讓程式跑起來;
在radasm生成程式碼的前面加個int 3,當斷點用,到int3的時候偵錯程式就會停下來的,就能知道下斷點的位置;
radasm中 構建-執行,主執行緒呼叫,winmine.exe在x64dbg斷下來
f8單步,直接崩潰;
下個斷點,(將斷點int 3,改為Nop, 繼續走)
走到call的位置就掛掉了
4個push,中間兩個是兩個字串: 右鍵-記憶體視窗中轉到-這個地址 ;發現並不是傳入的字串
字串沒有複製過來,而且地址不對,地址在原程式中寫成固定值了;
這個字串地址是原程式中的地址,並沒有複製到winmine.exe中
開啟chongdingwei.exe,看一下字串的位置
檢視字串: 右鍵 - 資料視窗跟隨 - 立即數
分析完成,開始處理問題;
問題處理
字串問題處理: 先把字串帶到winmine.exe執行緒:
修改用這種方式
先讓winmine.exe用x64dbg開啟,程式跑起來;
雙擊chongdingwei.exe
程式斷下來
下斷點,檢視字串是否帶進來了
字串從02ac0003開始的
可以看到字串代進來了;
但是push的地址不對,push的地址應該是在jmp的位置;
因為這個地址是寫的注入程式碼時記憶體的地址,複製到主執行緒後,機器碼沒有變化,這個地址還是 原來的地址。
地址需要動態算出來
地址問題處理:發現主執行緒地址與注入程式碼地址差是一樣的(偏移值相同)。 動態算出來地址
當程式碼跑起來的時候,需要算出來在哪個地址;
用相同指令位置的值來相減就可以;
偏移值: 獲取當前的EIP ,減去之前的offset地址值
獲取當前指令在新的記憶體中地址:
call 會把下一條指令的地址壓棧,標號正好在pop指令上,所以就把pop ebx指令地址壓棧,然後再執行pop ebx,此時ebx就是這條指令的地址(eip的值);
獲取偏移:
新地址 - 原先的地址
invoke不能用了,否則地址仍然是固定的,但地址要實時取,所以用push四個引數。
先讓winmine.exe用x64dbg開啟,程式跑起來;
雙擊chongdingwei.exe
程式斷下來,可以看到各個差值是正確的
但是這個call的地址值為空,這裡無法呼叫messagebox
messagebox地址也不能寫死,否則不能做到通用;
看一下chongdingwei.exe的call怎麼呼叫messagebox的
call 跑到jmp的位置,jmp跳到messagebox函式地址的,是一個偏移
同一臺電腦上,不同程式中的dll地址相同,包括kernal32,user32,那麼LoadLib,GetProcAddr,地址也是一樣的; 模組地址一樣,那麼匯出函式地址一樣
假設不同程式中地址不一樣,獲取到LoadLibary,GetProcAddress地址,就獲取任何api的地址
未使用宏的時候,程式碼儲存一下
.586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
spush macro x
mov eax, offset x
add eax,ebx
push eax
endm
scall macro x
mov eax, offset x
add eax,ebx
call dword ptr[eax]
endm
smov macro x,reg
mov eax, offset x
add eax,ebx
mov dword ptr [eax],reg
endm
.data
g_szWinmine db "掃雷", 0
g_szKernel db "Kernel32", 0
g_szLoadLibrary db "LoadLibraryA", 0
g_szGetProcAddress db "GetProcAddress", 0
g_hKer dd 0
g_dwPid dd 0
g_hProc dd 0
g_pAddr dd 0
g_dwBytesWrited dd 0
g_dwOldProc dd 0
.code
MSGBOX:
int 3
jmp COMEONBABY
g_szMsg db "你被注入了", 0
g_szTitle db "不要擔心,重啟就行", 0
g_szUser32 db "user32",0 ;拿到user32基址
g_szMsgBox db "MessageBoxA",0
g_pfnLoadLib dd 0
g_pfnGetProcAddr dd 0
g_pfnMessageBox dd 0
g_hUser32 dd 0
COMEONBABY:
call NEXT
NEXT:
pop ebx ; 獲取新記憶體地址
sub ebx, offset NEXT ;獲取偏移
mov edx, offset g_pfnLoadLib
add edx,ebx ;LoadLibary地址
mov eax, offset g_szUser32
add eax, ebx
push eax
call dword ptr [edx]; 呼叫LoadLibary,載入User32模組
mov edx, offset g_hUser32
add edx,ebx
mov dword ptr [edx],eax
mov edx, offset g_pfnGetProcAddr
add edx,ebx ;GetProcAddress地址
mov eax, offset g_szMsgBox
add eax,ebx
push eax
call dword ptr[eax] ;呼叫GetProcAddress,獲取MessageBox地址
mov edx, offset g_pfnMessageBox
add edx, ebx
mov dword ptr [edx], eax
push MB_OK
mov eax, offset g_szTitle
add eax,ebx ;當前地址+偏移地址
push eax
mov eax, offset g_szMsg
add eax,ebx
push eax
push NULL
mov edx, offset g_pfnMessageBox
add edx, ebx
call dword ptr [edx] ; 呼叫MessageBox
ret 4 ; 執行之後從執行緒函式中返回,執行緒函式一個引數,引數平棧
;invoke MessageBox,NULL,offset g_szMsg,offset g_szTitle, MB_OK
start:
;開啟程式
invoke FindWindow,NULL,offset g_szWinmine ;視窗控制程式碼
invoke GetWindowThreadProcessId,eax,offset g_dwPid ; 程式id
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,g_dwPid ;開啟程式
mov g_hProc,eax
;申請記憶體
invoke VirtualAllocEx,g_hProc,NULL,1000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
mov g_pAddr,eax
;生成機器碼寫入到申請的記憶體,跨程式寫記憶體
invoke WriteProcessMemory, g_hProc, g_pAddr, offset MSGBOX, offset start - offset MSGBOX, offset g_dwBytesWrited
; 建立執行緒,執行程式碼
invoke CreateRemoteThread,g_hProc,NULL,0,g_pAddr,NULL,NULL,NULL
invoke ExitProcess,eax
end start
使用宏程式碼記錄
.586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
spush macro x
mov eax, offset x
add eax,ebx
push eax
endm
scall macro x
mov eax, offset x
add eax,ebx
call dword ptr[eax]
endm
smov macro x,reg
mov eax, offset x
add eax,ebx
mov dword ptr [eax],reg
endm
.data
g_szWinmine db "掃雷", 0
g_szKernel db "Kernel32", 0
g_szLoadLibrary db "LoadLibraryA", 0
g_szGetProcAddress db "GetProcAddress", 0
g_hKer dd 0
g_dwPid dd 0
g_hProc dd 0
g_pAddr dd 0
g_dwBytesWrited dd 0
g_dwOldProc dd 0
.code
MSGBOX:
int 3
jmp COMEONBABY
g_szMsg db "你被注入了", 0
g_szTitle db "不要擔心,重啟就行", 0
g_szUser32 db "user32",0 ;拿到user32基址
g_szMsgBox db "MessageBoxA",0
g_pfnLoadLib dd 0
g_pfnGetProcAddr dd 0
g_pfnMessageBox dd 0
g_hUser32 dd 0
COMEONBABY:
call NEXT
NEXT:
pop ebx ; 獲取新記憶體地址
sub ebx, offset NEXT ;獲取偏移
;載入user32.dll
spush offset g_szUser32
scall g_pfnLoadLib
mov edx,eax
smov g_hUser32,edx
;獲取MessageBox地址
spush offset g_szMsgBox
scall g_pfnGetProcAddr
mov edx,eax
smov g_pfnMessageBox,edx
;呼叫MessageBox
push MB_OK
spush offset g_szTitle
spush offset g_szMsg
push NULL
scall offset g_pfnMessageBox
ret 4 ; 執行之後從執行緒函式中返回,執行緒函式一個引數,引數平棧
;invoke MessageBox,NULL,offset g_szMsg,offset g_szTitle, MB_OK
start:
;開啟程式
invoke FindWindow,NULL,offset g_szWinmine ;視窗控制程式碼
invoke GetWindowThreadProcessId,eax,offset g_dwPid ; 程式id
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,g_dwPid ;開啟程式
mov g_hProc,eax
;寫入loadLibrary和GetProcAddress地址
invoke GetModuleHandle,offset g_szKernel
mov g_hKer,eax
invoke GetProcAddress,g_hKer,offset g_szLoadLibrary
mov g_pfnLoadLib,eax
invoke GetProcAddress,g_hKer,offset g_szGetProcAddress
mov g_pfnGetProcAddr,eax
;申請記憶體
invoke VirtualAllocEx,g_hProc,NULL,1000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
mov g_pAddr,eax
;生成機器碼寫入到申請的記憶體,跨程式寫記憶體
invoke WriteProcessMemory, g_hProc, g_pAddr, offset MSGBOX, offset start - offset MSGBOX, offset g_dwBytesWrited
; 建立執行緒,執行程式碼
invoke CreateRemoteThread,g_hProc,NULL,0,g_pAddr,NULL,NULL,NULL
invoke ExitProcess,eax
end start
地址寫資料的時候報C00005,401034程式碼區,地址寫到程式碼區就完蛋
程式碼需要改一下屬性
主程式也報錯異常了;
發現
引數不夠的表現
程式碼修改,messageBox增加引數
spush offset g_szMsgBox
spushval g_hUser32
scall g_pfnGetProcAddr
注入成功
完整程式碼記錄
.586
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
spush macro x
mov eax, offset x
add eax, ebx
push eax
endm
spushval macro x
mov eax, offset x
add eax, ebx
push dword ptr[eax]
endm
scall macro x
mov eax, offset x
add eax, ebx
call dword ptr[eax]
endm
smov macro x, reg
mov eax, offset x
add eax, ebx
mov dword ptr [eax], reg
endm
.data
g_szWinmine db "掃雷", 0
g_szKernel db "Kernel32", 0
g_szLoadLibrary db "LoadLibraryA", 0
g_szGetProcAddress db "GetProcAddress", 0
g_hKer dd 0
g_dwPid dd 0
g_hProc dd 0
g_pAddr dd 0
g_dwBytesWrited dd 0
g_dwOldProc dd 0
.code
MSGBOX:
jmp EXECODE
g_szMsg db "你被注入了", 0
g_szTitle db "不要擔心,重啟就行", 0
g_szUser32 db "user32", 0
g_szMsgBox db "MessageBoxA", 0
g_pfnLoadLib dd 0 ;三個函式地址,在主函式賦值了。
g_pfnGetProcAddr dd 0
g_pfnMessageBox dd 0
g_hUser32 dd 0 ;控制程式碼
EXECODE:
call NEXT
NEXT:
pop ebx
sub ebx,offset NEXT ;獲取偏移
;----------------------------------------messagebox函式在user32.dll裡-------------------
;載入user32.dll
spush offset g_szUser32
scall g_pfnLoadLib
mov edx, eax
smov g_hUser32, edx
;獲取MessageBox地址
spush offset g_szMsgBox
spushval g_hUser32
scall g_pfnGetProcAddr
mov edx, eax
smov g_pfnMessageBox, edx
;----------------------------------------呼叫messagebox函式-------------------
push MB_OK
spush offset g_szMsg
spush offset g_szTitle
push NULL
scall offset g_pfnMessageBox
ret 4 ;執行緒過程函式有一個引數
;invoke MessageBox, NULL, offset g_szMsg, offset g_szTitle, MB_OK
;---------------------------------------------------------------------------------
start:
invoke FindWindow,NULL,offset g_szWinmine ;視窗控制程式碼,返回到了eax
invoke GetWindowThreadProcessId,eax,offset g_dwPid ;程式id
invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,g_dwPid ;開啟程式
mov g_hProc,eax ;程式控制程式碼
;改程式碼段屬性
invoke VirtualProtect, offset MSGBOX, offset start - offset MSGBOX, PAGE_EXECUTE_READWRITE, offset g_dwOldProc
;寫入LoadLibaray和GetProcAddress地址;LoadLibaray載入user32.dll,GetProcAddress得到messagebox地址。
invoke GetModuleHandle, offset g_szKernel
mov g_hKer, eax
invoke GetProcAddress,g_hKer,offset g_szLoadLibrary
mov g_pfnLoadLib, eax
invoke GetProcAddress, g_hKer, offset g_szGetProcAddress
mov g_pfnGetProcAddr, eax
;申請記憶體
invoke VirtualAllocEx,g_hProc,NULL,1000h,MEM_COMMIT,PAGE_EXECUTE_READWRITE
mov g_pAddr,eax
invoke VirtualProtect, offset MSGBOX, offset start - offset MSGBOX, g_dwOldProc, offset g_dwOldProc
;寫入機器碼
invoke WriteProcessMemory,g_hProc,g_pAddr,offset MSGBOX, offset start - offset MSGBOX, offset g_dwBytesWrited
;建立執行緒,執行程式碼
invoke CreateRemoteThread, g_hProc, NULL,0, g_pAddr, NULL, NULL, NULL
invoke ExitProcess,eax
end start