记得看win32asm教程的时候,里面提到win32只使用stdcall约定,即参数由右向左压入堆栈,恢复堆栈的工作交给被调用者。但同时还 提到有一个函数例外,wsprintf,因为它的参数个数是不确定的。原来一直没有用过这个函数,就没注意了。今天用了一下,发现也不用自己恢复堆栈啊, 只要简单的
invoke wsprintf, OFFSET szBuf, OFFSET szFmt, …
就OK了, 并不用在后面加上add esp, xx,为什么呢?把程序反汇编了一下,恍然大悟!原来是编译器帮我搞定了,自动的加上了add esp, xx一句。如果自己再写add esp, xx就是画蛇添足了。
由于红警2中保存金钱的位置每次载入都不一样,所以不能简单的 WriteProcessMemory 就行了。不过办法当然还是有的。查到一次的地址,然后在地址处下个断点,动态跟踪一下就会发现,代码中有一处 push eax 是用来给出保存金钱的地址的。在该处下断点,每次 eax 的值加 2E8h 刚好是保存金钱的地址。嘿嘿,那只要在这里做一些手脚不就搞定了?在进程的空间中找一处空闲的地方,例如地址A和其不远出的B,然后把push eax 的地方改成 jmp B 在B那里写上 mov A, eax 这样就把这个值保存在了一个固定的地方,然后再在B后面写上被破坏的几条指令,并最后jmp回原来的地址。OK了,现在我们只要去A处取地址,加上 2E8h 就能得到金钱的地址了,就又能改钱了,哈哈!至于改别的,我没有试,大概方法都差不多。
下面是代码,只适用于winxp,并且是 针对尤里的复仇的。每次按下alt+1加50000的money。
.386
.model flat, stdcall
option casemap: none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\shell32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\shell32.lib
WinMain proto
WORD,
WORD,
WORD,
WORD
WM_SHELLNOTIFY equ WM_USER + 5
IDI_TRAY equ 0
YuriIcon equ 10000
HotKeyID equ 0ABC0h
.const
AppMutex db ‘FixYuriMutex’, 0
AlreadyRun db ‘FixYuri is already running!’, 0
AppName db ‘FixYuri’, 0
ClassName db ‘FixYuriClass’, 0
TargetTitle db ‘Yuri’’s Revenge’, 0
HintText db ‘press Alt+1 add 50000$’, 0
YuriNotFound db ‘Yuri’’s Revenge is not running!’, 0
Addr1 dd 004A2593h ; push eax 处的地址,别的机器上可能不同
Addr2 dd 00B78F10h ; 保存代码的位置
Addr3 dd 00B78F00h ; 保存地址的位置
Data1 db 0E9h, 078h, 069h, 06Dh, 000h, 090h ; jmp 00B78F10h 的机器代码
Data2 db 0A3h, 000h, 08Fh, 0B7h, 000h, 050h, 0FFh, 051h, 018h, 033h, 0D2h, 0E9h, 079h, 096h, 092h, 0FFh ; mov [00B78F10h], eax; push eax; call dword ptr ds:[ecx+18]; xor edx, edx; jmp 004A2599h 的机器代码
.data
pid dd 0
hd dd 0
.data?
inst HINSTANCE ?
cmd LPSTR ?
note NOTIFYICONDATA <?>
ico dd ?
tmp dd ?
Addr4 dd ?
dwFlag dd ?
.code
start:
invoke CreateMutex, NULL, FALSE, addr AppMutex
invoke GetLastError
.IF eax == ERROR_ALREADY_EXISTS
invoke MessageBox, NULL, addr AlreadyRun, addr AppName, MB_OK or MB_ICONWARNING
invoke ExitProcess, 0
.ENDIF
invoke GetModuleHandle, NULL
mov inst, eax
invoke GetCommandLine
mov cmd, eax
invoke WinMain, inst, NULL, cmd, SW_MINIMIZE
invoke ExitProcess, eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW + 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
invoke LoadIcon, hInst, YuriIcon
mov wc.hIcon, eax
mov wc.hIconSm, eax
mov ico, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL,\
addr ClassName,\
addr AppName,\
WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_SIZEBOX,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
200,\
50,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc: HDC
LOCAL ps: PAINTSTRUCT
LOCAL rect: RECT
.IF uMsg == WM_CREATE
invoke RegisterHotKey, hWnd, HotKeyID, MOD_ALT, VK_1
.ELSEIF uMsg == WM_PAINT
invoke BeginPaint, hWnd, addr ps
mov hdc, eax
invoke GetClientRect, hWnd, addr rect
invoke DrawText, hdc, addr HintText, -1, addr rect,\
DT_CENTER or DT_VCENTER or DT_SINGLELINE
.ELSEIF uMsg == WM_SIZE
.IF wParam == SIZE_MINIMIZED
mov note.cbSize, SIZEOF NOTIFYICONDATA
push hWnd
pop note.hwnd
mov note.uID, IDI_TRAY
mov note.uFlags, NIF_ICON or NIF_MESSAGE or NIF_TIP
mov note.uCallbackMessage, WM_SHELLNOTIFY
push ico
pop note.hIcon
invoke lstrcpy, addr note.szTip, addr AppName
invoke ShowWindow, hWnd, SW_HIDE
invoke Shell_NotifyIcon, NIM_ADD, addr note
.ENDIF
.ELSEIF uMsg == WM_SHELLNOTIFY
.IF wParam == IDI_TRAY
.IF lParam == WM_LBUTTONDOWN
invoke Shell_NotifyIcon, NIM_DELETE, addr note
invoke ShowWindow, hWnd, SW_RESTORE
invoke SetForegroundWindow, hWnd
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_HOTKEY
.IF wParam == HotKeyID
.IF hd == 0
invoke FindWindow, 0, addr TargetTitle
.IF eax != 0
invoke GetWindowThreadProcessId, eax, addr pid
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, pid
mov hd, eax
.ENDIF
.IF hd != 0
invoke WriteProcessMemory, hd, Addr1, addr Data1, SIZEOF Data1, NULL
invoke WriteProcessMemory, hd, Addr2, addr Data2, SIZEOF Data2, NULL
invoke Sleep, 1000
.ELSE
invoke SetForegroundWindow, hWnd
invoke MessageBox, hWnd, addr YuriNotFound, addr AppName, MB_OK or MB_ICONWARNING
.ENDIF
.ELSE
invoke ReadProcessMemory, hd, Addr3, addr tmp, SIZEOF tmp, NULL
.IF eax
push tmp
pop Addr4
add Addr4, 02e8h
invoke ReadProcessMemory, hd, Addr4, addr tmp, SIZEOF tmp, NULL
add tmp, 50000
invoke WriteProcessMemory, hd, Addr4, addr tmp, SIZEOF tmp, NULL
.ELSE
mov hd, 0
.ENDIF
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_DESTROY
invoke UnregisterHotKey, hWnd, HotKeyID
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
前段时间写的,感觉比较完善了。能锁定金钱,电量和用电量,并能增加建造速度。并可以工作在win98和winxp两种平台上。
.386
.model flat, stdcall
option casemap: none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\shell32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\shell32.lib
WinMain proto
WORD,
WORD,
WORD,
WORD
KeyProc proto
WORD,
WORD,
WORD
GetOsVersion proto
WM_SHELLNOTIFY equ WM_USER + 5
IDI_TRAY equ 0
RaIcon equ 10000
TimerID equ 1
ButtonID1 equ 1
ButtonID2 equ 2
ButtonID3 equ 3
ButtonID4 equ 4
HotKeyID1 equ 0ABC1h
HotKeyID2 equ 0ABC2h
HotKeyID3 equ 0ABC3h
HotKeyID4 equ 0ABC4h
.const
AppMutex db ‘racheatmutex’, 0
AlreadyRun db ‘RA cheater is already running!’, 0
AppName db ‘RA cheater’, 0
ClassName db ‘racheaterclass’, 0
TargetTitle db ‘Red Alert’, 0
ButtonClass db ‘Button’, 0
Button1 db ‘lock money(Alt+1)’, 0
Button2 db ‘lock used power(Alt+2)’, 0
Button3 db ‘lock power(Alt+3)’, 0
Button4 db ’speed up(Alt+4)’, 0
Value1 dd 30000
Value2 dd 0
Value3 dd 500
Value4 dd 0F0h
.data
flag1 db 0
flag2 db 0
flag3 db 0
pid dd 0
hd dd 0
.data?
inst HINSTANCE ?
cmd LPSTR ?
note NOTIFYICONDATA <?>
ico dd ?
OsVer dd ?
hHook dd ?
Addr1 dd ?
Addr2 dd ?
Addr3 dd ?
Addr4 dd ?
hwnd HWND ?
.code
start:
invoke CreateMutex, NULL, FALSE, addr AppMutex
invoke GetLastError
.IF eax == ERROR_ALREADY_EXISTS
invoke MessageBox, NULL, addr AlreadyRun, addr AppName, MB_OK or MB_ICONWARNING
invoke ExitProcess, 0
.ENDIF
invoke GetOsVersion ; 判断系统类型
mov OsVer, eax
.IF OsVer == VER_PLATFORM_WIN32_NT ; 根据系统类型赋不同的地址,也许在你的机器上和我的不一样,FPE搜一下就知道了。
mov Addr1, 0A431D17h
mov Addr2, 0A431D67h
mov Addr3, 0A431D63h
mov Addr4, 0A431D53h
.ELSE
mov Addr1, 01C31D17h
mov Addr2, 01C31D67h
mov Addr3, 01C31D63h
mov Addr4, 0A431D53h
.ENDIF
invoke GetModuleHandle, NULL
mov inst, eax
invoke GetCommandLine
mov cmd, eax
invoke WinMain, inst, NULL, cmd, SW_MINIMIZE
invoke ExitProcess, eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
invoke LoadIcon, hInst, RaIcon
mov wc.hIcon, eax
mov wc.hIconSm, eax
mov ico, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx, NULL,\
addr ClassName,\
addr AppName,\
WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_SIZEBOX,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
200,\
225,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd, eax
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.ENDW
mov eax, msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg == WM_CREATE
invoke SetTimer, hWnd, TimerID, 500, NULL
.IF OsVer == VER_PLATFORM_WIN32_NT
invoke RegisterHotKey, hWnd, HotKeyID1, MOD_ALT, VK_1
invoke RegisterHotKey, hWnd, HotKeyID2, MOD_ALT, VK_2
invoke RegisterHotKey, hWnd, HotKeyID3, MOD_ALT, VK_3
invoke RegisterHotKey, hWnd, HotKeyID4, MOD_ALT, VK_4
.ELSE ; 由于win98下注册的快捷键进游戏后就失效了,只好用钩子实现快捷键。
invoke SetWindowsHookEx, WH_JOURNALRECORD, addr KeyProc, inst, NULL
mov hHook, eax
.ENDIF
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button1,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 0, 195, 50,\
hWnd, ButtonID1, inst, NULL
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button2,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 50, 195, 50,\
hWnd, ButtonID2, inst, NULL
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button3,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 100, 195, 50,\
hWnd, ButtonID3, inst, NULL
invoke CreateWindowEx, NULL, addr ButtonClass, addr Button4,\
BS_PUSHBUTTON or WS_VISIBLE or WS_CHILD, 0, 150, 195, 50,\
hWnd, ButtonID4, inst, NULL
.ELSEIF uMsg == WM_SIZE
.IF wParam == SIZE_MINIMIZED
mov note.cbSize, SIZEOF NOTIFYICONDATA
push hWnd
pop note.hwnd
mov note.uID, IDI_TRAY
mov note.uFlags, NIF_ICON or NIF_MESSAGE or NIF_TIP
mov note.uCallbackMessage, WM_SHELLNOTIFY
push ico
pop note.hIcon
invoke lstrcpy, addr note.szTip, addr AppName
invoke ShowWindow, hWnd, SW_HIDE
invoke Shell_NotifyIcon, NIM_ADD, addr note
.ENDIF
.ELSEIF uMsg == WM_SHELLNOTIFY
.IF wParam == IDI_TRAY
.IF lParam == WM_LBUTTONDOWN
invoke Shell_NotifyIcon, NIM_DELETE, addr note
invoke ShowWindow, hWnd, SW_RESTORE
invoke SetForegroundWindow, hWnd
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_TIMER
.IF hd == 0
invoke FindWindow, 0, addr TargetTitle
.IF eax != 0
invoke GetWindowThreadProcessId, eax, addr pid
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, pid
mov hd, eax
.ENDIF
.ENDIF
.IF flag1 == 1
invoke WriteProcessMemory, hd, Addr1, addr Value1, SIZEOF Value1, NULL
.IF eax == 0
mov hd, 0
.ENDIF
.ENDIF
.IF flag2 == 1
invoke WriteProcessMemory, hd, Addr2, addr Value2, SIZEOF Value2, NULL
.IF !eax
mov hd, 0
.ENDIF
.ENDIF
.IF flag3 == 1
invoke WriteProcessMemory, hd, Addr3, addr Value3, SIZEOF Value3, NULL
.IF !eax
mov hd, 0
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_COMMAND
mov eax, wParam
.IF ax == ButtonID1
xor flag1, 1
.ELSEIF ax == ButtonID2
xor flag2, 1
.ELSEIF ax == ButtonID3
xor flag3, 1
.ELSEIF ax == ButtonID4
.IF hd
mov edx, Addr4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_HOTKEY
.IF wParam == HotKeyID1
xor flag1, 1
.ELSEIF wParam == HotKeyID2
xor flag2, 1
.ELSEIF wParam == HotKeyID3
xor flag3, 1
.ELSEIF
.IF hd
mov edx, Addr4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
add edx, 4
invoke WriteProcessMemory, hd, edx, addr Value4, SIZEOF Value4, NULL
.ENDIF
.ENDIF
.ELSEIF uMsg == WM_SYSKEYDOWN
.IF lParam == VK_1
xor flag1, 1
.ELSEIF lParam == VK_2
xor flag2, 1
.ELSEIF lParam == VK_3
xor flag3, 1
.ENDIF
.ELSEIF uMsg == WM_DESTROY
.IF OsVer == VER_PLATFORM_WIN32_NT
invoke UnregisterHotKey, hWnd, HotKeyID1
invoke UnregisterHotKey, hWnd, HotKeyID2
invoke UnregisterHotKey, hWnd, HotKeyID3
.ELSE
invoke UnhookWindowsHookEx, hHook
.ENDIF
invoke PostQuitMessage, NULL
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
KeyProc proc nCode: DWORD, wParam: WPARAM, lParam: LPARAM
.IF nCode == HC_ACTION
mov edx, lParam
assume edx: PTR EVENTMSG
.IF [edx].message == WM_SYSKEYDOWN
mov eax, [edx].paramL
.IF al == VK_1
invoke PostMessage, hwnd, WM_SYSKEYDOWN, NULL, VK_1
.ELSEIF al == VK_2
invoke PostMessage, hwnd, WM_SYSKEYDOWN, NULL, VK_2
.ELSEIF al == VK_3
invoke PostMessage, hwnd, WM_SYSKEYDOWN, NULL, VK_3
.ENDIF
.ENDIF
.ENDIF
invoke CallNextHookEx, hHook, nCode, wParam, lParam
ret
KeyProc endp
GetOsVersion proc
LOCAL ovi:OSVERSIONINFO
mov ovi.dwOSVersionInfoSize, SIZEOF OSVERSIONINFO
invoke GetVersionEx, addr ovi
mov eax, ovi.dwPlatformId
ret
GetOsVersion endp
end start
由 于红警98中金钱等信息的地址在每次载入时都是固定的,所以这个程序可以得逞,要是换红警2,就得用另一种方法了。