消すかもと書きましたが、よく考えると良い題材だなと思うのでその補足
アセンブラの話になりますが実行ファイルへのパッチのあて方が題材です。
CoLが該当します。
db 04 ⇒ db 08
なのでサイズが変わりません。この場合は何も問題はありません。
減少するパターンも考えてみます。
よくあるのが条件ジャンプで
CoL でも traditional chinese の部分
cmp ecx, 10
ja childoflight.1699588
これは if (ecx > 0x10) ならばジャンプするということなんですが、これをジャンプしないようにパッチをあてる場合が減少するパターンです。(命令を削除するから)
ジャンプしないようにするのは ja 命令を削除すると良いので、ここを何もしない命令(何も変化しない命令) NOP に置き換えます。つまり
cmp ecx, 10
nop
nop
とします。なぜ nop が2個必要なのか?
これは元の ja childoflight.1699588 をアセンブルすると 77 6B という2バイトの機械語になっているので
その2バイトを NOP(1バイトの90)で置き換えるには2個必要になる。
77 6B ⇒ 90 90
とする必要があります。
減少する場合も NOP で埋めれば良いだけなので問題はありません。
2. 増加する場合
問題になるのが増加する(命令を追加する)場合で F◇4 が該当します。
00007FF705CBF944: or [rdx],4000
00007FF705CBF94A: mov [rsp+30],ecx
00007FF705CBF94E: lea rdx,[rsp+30]
これを
00007FF705CBF944: or [rdx],4000
mov ecx, 2 ; << 命令を追加したい
00007FF705CBF94A: mov [rsp+30],ecx
00007FF705CBF94E: lea rdx,[rsp+30]
命令を追加したいのですが追加するスペースがありません。無理矢理
or [rdx],4000 の後に書いたとすると 00007FF705CBF94A 以降にある命令を全部後ろにずらして追加する形になるので(ジャンプ等のアドレスも変更になる)現実的ではありません
なので
空いている場所あるいは新しく確保した場所に追加の命令を書き、そちらにジャンプして追加分の命令が終わったら元の流れに戻るようにリターンするということをします。
ジャンプにも命令を書く必要があるのでまず命令1個つぶしてジャンプに置き換えます
or [rdx],4000
jmp NewMemory
lea rdx,[rsp+30]
NewMemory: ; 空いている場所もしくは確保した場所
mov ecx, 2 ; << 追加したい命令
mov [rsp+30],ecx ; << jmp でつぶされた元の命令
jmp (lea rdx,[rsp+30])のアドレス ; 元の流れにリターン
イメージは上記なんですが
現実には jmp の命令サイズと元の命令サイズが違いjmp の方が多いので命令2個をつぶす必要があります
したがって
or [rdx],4000
jmp NewMemory ; ここから
nop ;
nop ;
nop ;
nop ; ここまでが2個の命令と同じサイズになる
mov ecx,1
NewMemory:
mov ecx, 2 ; << 追加したい命令
mov [rsp+30],ecx ; << jmp でつぶされた1個目の命令
lea rdx,[rsp+30] ; << jmp でつぶされた2個目の命令
jmp (mov ecx,1)のアドレス ; 元の流れにリターン
とすると良いです。
CheatEngine スクリプトの
alloc(MyCode,$1000,trackNo)
の部分はメモリを確保している部分になります。
他の部分は上記で説明したことをやっているだけです。
アセンブラの話になりますが実行ファイルへのパッチのあて方が題材です。
- パッチサイズが変わらない。あるいは減少する。(CoL)
- パッチサイズが増加する。(F◇4)
CoLが該当します。
db 04 ⇒ db 08
なのでサイズが変わりません。この場合は何も問題はありません。
減少するパターンも考えてみます。
よくあるのが条件ジャンプで
CoL でも traditional chinese の部分
cmp ecx, 10
ja childoflight.1699588
これは if (ecx > 0x10) ならばジャンプするということなんですが、これをジャンプしないようにパッチをあてる場合が減少するパターンです。(命令を削除するから)
ジャンプしないようにするのは ja 命令を削除すると良いので、ここを何もしない命令(何も変化しない命令) NOP に置き換えます。つまり
cmp ecx, 10
nop
nop
とします。なぜ nop が2個必要なのか?
これは元の ja childoflight.1699588 をアセンブルすると 77 6B という2バイトの機械語になっているので
その2バイトを NOP(1バイトの90)で置き換えるには2個必要になる。
77 6B ⇒ 90 90
とする必要があります。
減少する場合も NOP で埋めれば良いだけなので問題はありません。
2. 増加する場合
問題になるのが増加する(命令を追加する)場合で F◇4 が該当します。
00007FF705CBF944: or [rdx],4000
00007FF705CBF94A: mov [rsp+30],ecx
00007FF705CBF94E: lea rdx,[rsp+30]
これを
00007FF705CBF944: or [rdx],4000
mov ecx, 2 ; << 命令を追加したい
00007FF705CBF94A: mov [rsp+30],ecx
00007FF705CBF94E: lea rdx,[rsp+30]
命令を追加したいのですが追加するスペースがありません。無理矢理
or [rdx],4000 の後に書いたとすると 00007FF705CBF94A 以降にある命令を全部後ろにずらして追加する形になるので(ジャンプ等のアドレスも変更になる)現実的ではありません
なので
空いている場所あるいは新しく確保した場所に追加の命令を書き、そちらにジャンプして追加分の命令が終わったら元の流れに戻るようにリターンするということをします。
ジャンプにも命令を書く必要があるのでまず命令1個つぶしてジャンプに置き換えます
or [rdx],4000
jmp NewMemory
lea rdx,[rsp+30]
NewMemory: ; 空いている場所もしくは確保した場所
mov ecx, 2 ; << 追加したい命令
mov [rsp+30],ecx ; << jmp でつぶされた元の命令
jmp (lea rdx,[rsp+30])のアドレス ; 元の流れにリターン
イメージは上記なんですが
現実には jmp の命令サイズと元の命令サイズが違いjmp の方が多いので命令2個をつぶす必要があります
したがって
or [rdx],4000
jmp NewMemory ; ここから
nop ;
nop ;
nop ;
nop ; ここまでが2個の命令と同じサイズになる
mov ecx,1
NewMemory:
mov ecx, 2 ; << 追加したい命令
mov [rsp+30],ecx ; << jmp でつぶされた1個目の命令
lea rdx,[rsp+30] ; << jmp でつぶされた2個目の命令
jmp (mov ecx,1)のアドレス ; 元の流れにリターン
とすると良いです。
CheatEngine スクリプトの
alloc(MyCode,$1000,trackNo)
の部分はメモリを確保している部分になります。
他の部分は上記で説明したことをやっているだけです。