目标:在扫雷中注入一个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
文章来源: 博客园
- 还没有人评论,欢迎说说您的想法!