首先来修改个简单的,打砖块的游戏,DXBALL2
先运行游戏,用游戏修改器(我用的是GameExpert)找到保存生命的地址43B1BC
接着用W32DASM反汇编主程序DXBALL2.EXE,搜索43B1BC,找到以下代码段

:00405645 A1BCB14300              mov eax, dword ptr [0043B1BC]
:0040564A 8B0D24B24300            mov ecx, dword ptr [0043B224]
:00405650 48                      dec eax
:00405651 6A00                    push 00000000
:00405653 A3BCB14300              mov dword ptr [0043B1BC], eax
:00405658 890D108E5200            mov dword ptr [00528E10], ecx
:0040565E C705A484520001000000    mov dword ptr [005284A4], 00000001
:00405668 B9A0CB4F00              mov ecx, 004FCBA0
:0040566D E84E350100              call 00418BC0

把00405650 48    dec eax这句nop掉,生命值就不减了。

至于游戏里的加强能力,没有是0,有是1,也很容易找到。

编写修改器如下,VB6,XP1通过。程序写得很烂,还望高手指教。

Public combot As Long
Public sd As Long


Private Sub start_Click()
Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄
Dim pid As Long ' 储存进程标识符( Process Id )


hwnd = FindWindow(vbNullString, "DX-Ball 2  (Press P to pause, and Alt-Tab to switch apps)")
If (hwnd = 0) Then
MsgBox "找不到游戏进程!", vbOKOnly, "出错!"
Exit Sub
End If

GetWindowThreadProcessId hwnd, pid


pHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, 0, pid)


If (pHandle = 0) Then
MsgBox "发生错误!"
Exit Sub
Else: btnPasteName.Caption = "修改开始!": Command1.Enabled = True: Command2.Enabled = True

End If
EnableDebugPriv

End Sub

Private Sub Command1_Click()
sd = (sd + 1) Mod 2 '开关自动锁定
If sd = 1 Then
    ReadTimer.Enabled = True: Command1.Caption = "已经自动锁定"
Else
    ReadTimer.Enabled = False: Command1.Caption = "已经取消锁定"
End If
End Sub

Private Sub Command2_Click()
Call ReadTimer_Timer
End Sub

Private Sub Form_Load()
combot = 1
sd = 0
End Sub

Private Sub Form_Unload(Cancel As Integer)
CloseHandle pHandle
End Sub

Private Sub ReadTimer_Timer() '修改代码
If CD0.Value = True Then combot = 0
If CD1.Value = True Then combot = 1
If CD2.Value = True Then combot = 2
If CD3.Value = True Then combot = 3
If CD4.Value = True Then combot = 4
WriteProcessMemory pHandle, &H4FCBD0, combot, 1, ByVal 0& '长度
WriteProcessMemory pHandle, &H52C660, ZD.Value, 1, ByVal 0& '子弹
WriteProcessMemory pHandle, &H5284B8, HQ.Value, 1, ByVal 0& '火球
WriteProcessMemory pHandle, &H52AEE0, CT.Value, 1, ByVal 0& '穿透
WriteProcessMemory pHandle, &H5284B4, XY.Value, 1, ByVal 0& '吸引
End Sub


------------------------
接来来是另一个打砖块的游戏,幻想游戏里的星际弹球之失落的世界
先用上面的方法找到保存生命的地址D1ED1C。
但是你如果反汇编主程序的话,照上面那种搜索方法,会毫无结果。
再进一次游戏,会发现保存生命的地址又变成D383B4,每一次都会变,所以这次要请出OD了

用游戏修改器找到地址后,不要关闭游戏,直接用OD的附加,附加后(假设这次的地址是D383B4),下硬件断点hw D383B4,死掉一条命后,中断

0046510A  |.  8B48 18       MOV ECX,DWORD PTR DS:[EAX+18]
0046510D  |.  83C0 18       ADD EAX,18
00465110  |.  41            INC ECX
00465111  |.  8908          MOV DWORD PTR DS:[EAX],ECX
00465113  |.  FF4F 44       DEC DWORD PTR DS:[EDI+44]      很明显这就是生命数减1的语句
00465116  |.  8B0D 5CB16400 MOV ECX,DWORD PTR DS:[64B15C]  中断在这里
0046511C  |.  E8 4F3F0700   CALL Rebound_.004D9070



0046510A  |.  8B48 18       MOV ECX,DWORD PTR DS:[EAX+18]
0046510D  |.  83C0 18       ADD EAX,18
00465110  |.  41            INC ECX
00465111  |.  8908          MOV DWORD PTR DS:[EAX],ECX
00465113      90            NOP
00465114      90            NOP
00465115      90            NOP
00465116  |.  8B0D 5CB16400 MOV ECX,DWORD PTR DS:[64B15C]
0046511C  |.  E8 4F3F0700   CALL Rebound_.004D9070


把它NOP掉,如上,进游戏一看,生命已经不再减了

顺带说一下,调试全屏游戏时,如果中断后OD显示不出来,请把OD的总在最前打开就行,或者用D3DWINDOW把游戏窗口化。大多数游戏地址都不是直接引用和固定的,所以还是得靠调试器来进行才是。

------------------------

接上次的文章,监狱1.0
http://bbs.pediy.com/showthread.php?threadid=19655 

XF00000000X
F0000000000
00000000000
00000000000
………
F0000000000
X000000000X

在棋盘上置三个防守棋子(F),如图示,这时中断,来到内存地址

00971FA0  00 03 00 00 00 00 00 00 00 03 00 03 00 00 00 00  .............
00971FB0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00971FC0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00971FD0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00971FE0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00971FF0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00972000  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00972010  00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00  ...............
00972020  03 00 00 00 14 00 00 00 06 00 00 00 00 00 00 00  .............

971FA0和971FA9都有03,证明棋子在内存中是以第一列开始从上到下的顺序储存的。971FAA是00,971FAB是03,说明左下角的X,虽然不能放置棋子,但是内存空间中它照样占有位置,照理四个角落的棋子都应该如此。
这里采用的是直接写入内存的方式来修改,初步设想在窗口上画它121个棋子(四个角落的隐藏掉就行),通过点击使棋子依次变为灰色(无棋子),红色(红方棋子),蓝色(蓝方棋子),白色(防守棋子),并把相应数据写入游戏内存。

当我在VB的窗口上扔上121个的Shape控件数组并把它们一一拉到相应位置并写了一点代码后,点击下去却全点没有反应。认真的一试,原来Shape控件没有任何事件,连click都没有#-_-。不想再重来,于是就决定以点击时鼠标的座标为依据判断点击的是哪个位置。

写修改器如下,VB6,XP1通过。程序写得很烂,还望高手指教。

Dim hang, lie, index2 As Long
Private Sub Command1_Click()
Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄
Dim pid As Long ' 储存进程标识符( Process Id )


hwnd = FindWindow(vbNullString, "监狱(Quod)")
If (hwnd = 0) Then
MsgBox "找不到游戏进程!", vbOKOnly, "出错!"
Exit Sub
End If

GetWindowThreadProcessId hwnd, pid


pHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, 0, pid)


If (pHandle = 0) Then
MsgBox "发生错误!"
Exit Sub
Else: Command1.Caption = "修改开始!"

End If
EnableDebugPriv

End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim i As Byte, j As Byte
Dim readad As Long, readad2 As Long
If (pHandle = 0) Then
MsgBox "找不到游戏进程!", vbOKOnly, "出错!"
Exit Sub
End If
For i = 0 To 120
    readad = &H971FA0 + i
    ReadProcessMemory pHandle, readad, j, 2, ByVal 0&
    Select Case j
        Case 0
            readad = &H8000000F '根据内存数据改变棋子颜色
        Case 1
            readad = &HFF&
        Case 2
            readad = &HFF0000
        Case 3
            readad = &HFFFFFF
    End Select
    Shape1(i).FillColor = readad
Next i
Select Case X '这两个case判断鼠标点击的是哪一个棋子
    Case 120 To 615
        lie = 1
    Case 720 To 1215
        lie = 2
    Case 1320 To 1815
        lie = 3
    Case 1920 To 2415
        lie = 4
    Case 2520 To 3015
        lie = 5
    Case 3120 To 3615
        lie = 6
    Case 3720 To 4215
        lie = 7
    Case 4320 To 4815
        lie = 8
    Case 4920 To 5415
        lie = 9
    Case 5520 To 6015
        lie = 10
    Case 6120 To 6615
        lie = 11
    Case Else
        lie = 0
End Select
Select Case Y
    Case 120 To 615
        hang = 1
    Case 720 To 1215
        hang = 2
    Case 1320 To 1815
        hang = 3
    Case 1920 To 2415
        hang = 4
    Case 2520 To 3015
        hang = 5
    Case 3120 To 3615
        hang = 6
    Case 3720 To 4215
        hang = 7
    Case 4320 To 4815
        hang = 8
    Case 4920 To 5415
        hang = 9
    Case 5520 To 6015
        hang = 10
    Case 6120 To 6615
        hang = 11
    Case Else
        hang = 0
End Select
If (lie <> 0 And hang <> 0) Then '点击后改变棋子颜色并写入游戏内存
    index2 = ((lie - 1) * 11 + hang - 1)
    readad2 = &H971FA0 + index2
    If Shape1(index2).FillColor = &H8000000F Then
        Shape1(index2).FillColor = &HFF&: WriteProcessMemory pHandle, readad2, 1, 1, ByVal 0&
    ElseIf Shape1(index2).FillColor = &HFF& Then
        Shape1(index2).FillColor = &HFF0000: WriteProcessMemory pHandle, readad2, 2, 1, ByVal 0&
    ElseIf Shape1(index2).FillColor = &HFF0000 Then
        Shape1(index2).FillColor = &HFFFFFF: WriteProcessMemory pHandle, readad2, 3, 1, ByVal 0&
    ElseIf Shape1(index2).FillColor = &HFFFFFF Then
        Shape1(index2).FillColor = &H8000000F: WriteProcessMemory pHandle, readad2, 0, 1, ByVal 0&
    End If
End If
End Sub


写完之后测试了一下,修改器显示的是正常,但是游戏界面却没有显示出棋子,在修改器里面改了之后虽然游戏里没有显示棋子,但照样显示“某方胜利”。
由此可知游戏在点击后才依据点击的位置来画棋子,并不是每次刷新。而且判断胜利也是用的上面的内存数据来判断。
根据上次分析的结果下断,来到此处。


00462464  |> \8D1416        LEA EDX,DWORD PTR DS:[ESI+EDX]
00462467      889C02 480300>MOV BYTE PTR DS:[EDX+EAX+348],BL         ;  这行里BL存的是棋子类型,用MOV写进内存
0046246E      8B4D F8       MOV ECX,DWORD PTR SS:[EBP-8]
00462471  |.  8B55 FC       MOV EDX,DWORD PTR SS:[EBP-4]
00462474  |.  8BC6          MOV EAX,ESI
00462476      E8 5DF4FFFF   CALL QUOD.004618D8                       ;  NOP掉这一行,结果发现棋子不见了,由此跟进这个CALL里
0046247B  |.  8BC6          MOV EAX,ESI
0046247D  |.  E8 1AF0FFFF   CALL QUOD.0046149C
00462482  |.  8BC6          MOV EAX,ESI
00462484  |.  E8 8FEBFFFF   CALL QUOD.00461018
00462489  |.  80BE 9A420000>CMP BYTE PTR DS:[ESI+429A],0             ;  是不是没有棋子
00462490  |.  75 0C         JNZ SHORT QUOD.0046249E
00462492  |.  80FB 03       CMP BL,3                                 ;  或者是防守棋子,不是的话,下面的CALL改变下棋方
00462495  |.  74 07         JE SHORT QUOD.0046249E
00462497  |.  8BC6          MOV EAX,ESI
00462499  |.  E8 FEEEFFFF   CALL QUOD.0046139C                       ;  改变下棋方
0046249E  |>  5E            POP ESI
0046249F  |.  5B            POP EBX
004624A0  |.  59            POP ECX
004624A1  |.  59            POP ECX
004624A2  |.  5D            POP EBP
004624A3  \.  C2 0C00       RETN 0C

跟进,前面省略一部分代码

00461951  |.  2C 01         SUB AL,1                                 ;  Switch (cases 0..3)
00461953  |.  72 0C         JB SHORT QUOD.00461961
00461955  |.  74 19         JE SHORT QUOD.00461970
00461957  |.  FEC8          DEC AL
00461959  |.  74 24         JE SHORT QUOD.0046197F
0046195B  |.  FEC8          DEC AL
0046195D  |.  74 2F         JE SHORT QUOD.0046198E
0046195F  |.  EB 3A         JMP SHORT QUOD.0046199B
00461961  |>  BA C0C0C000   MOV EDX,0C0C0C0                          ;  Case 0 of switch 00461951
00461966  |.  8B43 14       MOV EAX,DWORD PTR DS:[EBX+14]
00461969  |.  E8 32F9FBFF   CALL QUOD.004212A0
0046196E  |.  EB 2B         JMP SHORT QUOD.0046199B
00461970  |>  BA FF000000   MOV EDX,0FF                              ;  Case 1 of switch 00461951
00461975  |.  8B43 14       MOV EAX,DWORD PTR DS:[EBX+14]
00461978  |.  E8 23F9FBFF   CALL QUOD.004212A0
0046197D  |.  EB 1C         JMP SHORT QUOD.0046199B
0046197F  |>  BA 0000FF00   MOV EDX,0FF0000                          ;  Case 2 of switch 00461951
00461984  |.  8B43 14       MOV EAX,DWORD PTR DS:[EBX+14]
00461987  |.  E8 14F9FBFF   CALL QUOD.004212A0
0046198C  |.  EB 0D         JMP SHORT QUOD.0046199B
0046198E  |>  BA FFFFFF00   MOV EDX,0FFFFFF                          ;  Case 3 of switch 00461951
00461993  |.  8B43 14       MOV EAX,DWORD PTR DS:[EBX+14]
00461996      E8 05F9FBFF   CALL QUOD.004212A0                       
0046199B  |>  33D2          XOR EDX,EDX                              ;  Default case of switch 00461951

OD注释得真是详细,很明显看出,上面的Case根据内存中的数据判断棋子类型,并把相关颜色信息赋值

继续往下走,用NOP法判断之后来到下面的代码

00421644  /$  55            PUSH EBP
00421645  |.  8BEC          MOV EBP,ESP
00421647  |.  53            PUSH EBX
00421648  |.  56            PUSH ESI
00421649  |.  57            PUSH EDI
0042164A  |.  8BF9          MOV EDI,ECX
0042164C  |.  8BF2          MOV ESI,EDX
0042164E  |.  8BD8          MOV EBX,EAX
00421650  |.  8BC3          MOV EAX,EBX
00421652  |.  8B10          MOV EDX,DWORD PTR DS:[EAX]
00421654  |.  FF52 10       CALL DWORD PTR DS:[EDX+10]
00421657  |.  8A15 88164200 MOV DL,BYTE PTR DS:[421688]
0042165D  |.  8BC3          MOV EAX,EBX
0042165F  |.  E8 58050000   CALL QUOD.00421BBC
00421664  |.  8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]
00421667  |.  50            PUSH EAX                                 ; /Bottom
00421668  |.  8B45 0C       MOV EAX,DWORD PTR SS:[EBP+C]             ; |
0042166B  |.  50            PUSH EAX                                 ; |Right
0042166C  |.  57            PUSH EDI                                 ; |Top
0042166D  |.  56            PUSH ESI                                 ; |Left
0042166E  |.  8B43 04       MOV EAX,DWORD PTR DS:[EBX+4]             ; |
00421671  |.  50            PUSH EAX                                 ; |hDC
00421672  |.  E8 6D55FEFF   CALL <JMP.&GDI32.Ellipse>                ; \Ellipse
00421677  |.  8BC3          MOV EAX,EBX
00421679  |.  8B10          MOV EDX,DWORD PTR DS:[EAX]
0042167B  |.  FF52 0C       CALL DWORD PTR DS:[EDX+C]
0042167E  |.  5F            POP EDI
0042167F  |.  5E            POP ESI
00421680  |.  5B            POP EBX
00421681  |.  5D            POP EBP
00421682  \.  C2 0800       RETN 8


查了一下Ellipse函数(网上找到的是VB的,不过API应该都一样)

VB声明 
Declare Function Ellipse Lib "gdi32" Alias "Ellipse" (ByVal hdc As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long 
说明 
描绘一个椭圆,由指定的矩形围绕。椭圆用当前选择的画笔描绘,并用当前选择的刷子填充 
返回值 
Long,非零表示成功,零表示失败。会设置GetLastError 
参数表 
参数 类型及说明 
hdc Long,设备场景的句柄 
X1,Y1 Long,约束矩形采用逻辑坐标的左上角位置 
X2,Y2 Long,约束矩形采用逻辑坐标的右下角位置 

把上面Right,Top,Left等参数修改再运行



可见点击后,游戏先判断棋子类型,并把它写入内存,再在界面上用Ellipse函数画一个圆并填充,和我修改器用的改变控件颜色是不同方法。至于用修改器改完,要让游戏界面显示出棋子,目前对我来说显得比较困难,暂时搁下了。