29: TrackBarでチートDLLの移動速度を変える
概要
18回の記事 で、Ctrl-wasd
でその方向にちょっとワープする事で壁抜け移動できるチートを作りました。今回は、ワープする距離をGUIのトラックバーでリアルタイムで変更するという事を目標にやっていきます。
チート対象のゲームは、x86でチートの練習用に作られた、Pwn Adventure 3を使います。
Windows対象で、Visual Studio 2019を使用し
C++/CLI
で記述する。ソースコードはまとめてページの一番下にある。この記事は 25回 からの続きで、18回の内容も使用します。
移動距離を変更するエクスポート関数の追加
外部からチートDLL内のパラメーターを変更する事が今回のメイン目的ですが、 やる事自体はそのパラメーターをグローバルに定義し、それに値をセットするDLLエクスポート関数を作るだけです。18回のdllmain.cppだけ変更します。
ローカル変数として設けていた
boost
が移動距離(速度)なので、これを以下のように変更しましょう。また、これは前の記事の二種類のチートの内、「ゲーム内にInject後ずっと無限ループで居座り続けるDLL(ループ系)」のチートなので、 後からオン・オフを切り替えられるように
Disable
関数も設けておきます。
これで終わりなので忘れずにビルドしておきましょう。
サーバー側にboostコマンドを実装
サーバー側に上記のSetBoost
を使って移動距離(速度)を変更するコマンドを実装します。Disable
を使ったオン/オフに関しては、27回の記事
で追加したサーバー側のコードによって、load cheatfilename
とfree cheatfilename
の cheatfilename
の部分をこの壁抜けチートのファイル名にすれば機能するようになっているので新しくコードを追加する必要はありません。移動距離(速度)を変えるコマンドは
boost 速度の値
にします。まずは、サーバー側のdllmain.cppの最初の方に以下のようにして
SetBoost
関数の関数型を定義しましょう。
そして、以下のようにしてコマンドの内容を実装します。(自分は壁抜けチートのDLLファイル名を
quaternion.dll
にしているのでこうなってます)
これでビルドしましょう。
クライアント側の実装
クライアント側で壁抜けチートをオン/オフするボタンに関しては、27回の記事 の方法と全く同じです。27回のinvincibleボタンとラベルをまんまコピーし、ボタンのNameプロパティだけPlayerTelemoveButton
に変更しましょう。("壁抜け" っていう名前がなんかイマイチだと気づいたので、
telemove
と表記しています)
ボタンのNameプロパティをPlayerTelemoveButton
に変更した後、ボタンをダブルクリックしてハンドラーの内容は以下の用に記述しましょう。
終ったらいよいよトラックバーの追加です。
PlayerWindow.h のデザインビューで「TrackBar」コンポーネントを追加しましょう。
プロパティは以下のようにします。
- デザイン
- Name: PlayerTelemoveTrackBar
- 動作
- AllowDrop: False
- Enabled: False (チート有効化するまではいじれないようにするため)
- LargeChange: 1000 (マウスクリックとかPgUp/PgDnキーでは1000単位で値が変わるようにする)
- Maximum: 20000 (最大20000間隔で動けるようにする)
- Minimum: 0
- SmallChange: 100 (矢印キーでバー動かす時に100単位で動くようにする)
- 配置
- Location: 187, 93
- Size: 237, 45
- 表示
- TickFrequency: 1000 (これがTrackBarの目盛の間隔)
これでUIは整ったので、次はこのトラックバーの値が変った時にチートサーバーに
boost
コマンドを送るようにします。「ボタンが押されたとき」や「トラックバーの値が変った時」のようなイベントは、各コンポーネントの以下の雷マークみたいな所にまとまってます。 TrackBarコンポーネントには
ValueChanged
というイベントが備わっており、そこに PlayerTelemoveTrackBar_ValueChanged
という名前のイベントを入力しましょう。入力後、以下のように PlayerWindow.h にイベントハンドラーのコードが自動で追加されるはずなので、
そこに以下のように記述しましょう。
まとめると、TrackBarの値に変化があるとこの PlayerTelemoveTrackBar_ValueChanged
イベントが発火し、
そのイベントハンドラーである上記のコード内にて、トラックバーの現在の値を読み取り、sc->Send
を使ってソケットを通じて
boost
コマンドをサーバーに送っています。トラックバーの値はintなので文字列に変換する必要があります。
このチートクライアントはC++/CLIというマネージドコードで書かれているので、マネージドコードの文字列型もありますが、 受け取り側であるチートサーバーの方は普通のC++で書かれているので、文字列の方は受け取り側に合わせて
char*
型で送らないといけません。
幸いC++/CLIはアンマネージドコードも混在して書けるので、以下のように #include <string>
を追加しましょう。※ ここらへんの話は 24回の記事で詳しく書いてます。 これで赤線も消えたはずです。
根本的な機能は備わりましたが、このチートをオフにした時にトラックバーを動かせないようにしたいので、 以下のコードを
PlayerTelemoveButton_CheckedChanged
の方に追加しましょう。
connection accepted (-1) の対処
これで出来たはずなのですが、実は一つまだ問題があります。実際にこれで試してみると、DbgView等でデバッグ出力を見るとわかりますが、トラックバーを動かすと大量のデバッグ出力が流れ、
connection accepted (-1)
という出力で埋め尽くされます。このエラー分自体はSocketServer.cppの RecvCmd
関数内で自分で定義してるもので、カッコ内の数字にはソケットのファイルディスクリプタの値が入るはずですが、-1
なのでなんかおかしいです。
原因はよくわかりませんが、対処法は地味で、サーバー側のプログラムにて、SocketServer.cppで CloseSocket
関数の以下の WSACleanup()
を消すと治ります。
実行してみる
では実際にサーバーをインジェクトしてクライアントを起動して試してみましょう。以下のようにTrackBarの値分移動していれば成功です。また、途中で壊れないか等見るためにDbgViewで動作確認する事もオススメします。
ソースコード
壁抜けチートのdllmain.cpp (quaternion.dll)
#include "pch.h"
#include "general-cheats.h"
#include "player.h"
using namespace std;
bool enabled = true;
Player* player;
extern "C" void __declspec(dllexport) Disable() {
enabled = false;
}
float boost;
extern "C" void __declspec(dllexport) SetBoost(float val) {
boost = val;
}
DWORD WINAPI ThreadMain(LPVOID params) {
player = new Player();
boost = 700.0;
DWORD wait = 250;
while (enabled) {
if (isWindowFocused()) {
if (GetAsyncKeyState(VK_CONTROL) & GetAsyncKeyState(0x57)) { // w
Vector3 fv = player->GetCurrentForwardVector();
player->MoveLocation(fv, boost, { 1, 1, 1 });
Sleep(wait);
}
if (GetAsyncKeyState(VK_CONTROL) & GetAsyncKeyState(0x53)) { // s
Vector3 bv = Invert(player->GetCurrentForwardVector());
player->MoveLocation(bv, boost, { 1, 1, 1 });
Sleep(wait);
}
if (GetAsyncKeyState(VK_CONTROL) & GetAsyncKeyState(0x41)) { // a
Vector3 lv = player->GetCurrentLeftVector();
player->MoveLocation(lv, boost, { 1, 1, 1 });
Sleep(wait);
}
if (GetAsyncKeyState(VK_CONTROL) & GetAsyncKeyState(0x44)) { // d
Vector3 rv = Invert(player->GetCurrentLeftVector());
player->MoveLocation(rv, boost, { 1, 1, 1 });
Sleep(wait);
}
if (GetAsyncKeyState(VK_CONTROL) & GetAsyncKeyState(VK_SPACE)) {
Vector3 uv = player->GetCurrentUpVector();
player->MoveLocation(uv, boost, { 0, 1, 0 });
Sleep(wait);
}
}
}
delete player;
FreeLibraryAndExitThread((HMODULE)params, 0);
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
CreateThread(0, 0, ThreadMain, hModule, 0, 0);
}
return TRUE;
}
サーバー側のdllmain.cpp
#include "pch.h"
#include "general-cheats.h"
#include "SocketServer.h"
using namespace std;
SocketServer* server;
typedef void* (Disable)();
typedef void* (Hook)();
typedef void* (UnHook)();
typedef void* (SetBoost)(float val);
void init() {
std::vector<const char*> cheatDlls = {
"demo-nop-damage2"
};
for (const char* cheatDll : cheatDlls) {
char path[MAX_PATH];
snprintf(path, MAX_PATH, "C:\\Users\\<username>\\Desktop\\dlli\\%s\\Debug\\%s.dll", cheatDll, cheatDll);
LoadLibrary(path);
}
}
DWORD WINAPI ThreadMain(LPVOID params) {
server = new SocketServer();
init();
while (1) {
CmdProtocol msg;
server->RecvCmd(&msg);
if (strcmp(msg.cmd, "load") == 0) {
char path[MAX_PATH];
snprintf(path, MAX_PATH, "C:\\Users\\<username>\\Desktop\\dlli\\%s\\Debug\\%s.dll", msg.argument, msg.argument);
LoadLibrary(path);
}
if (strcmp(msg.cmd, "free") == 0) {
HMODULE hDll = GetModuleHandle(msg.argument);
Disable* disable = (Disable*)GetProcAddress(hDll, "Disable");
disable();
CloseHandle(hDll);
}
if (strcmp(msg.cmd, "hook") == 0) {
HMODULE hDll = GetModuleHandle(msg.argument);
Hook* hook = (Hook*)GetProcAddress(hDll, "Hook");
hook();
CloseHandle(hDll);
}
if (strcmp(msg.cmd, "unhook") == 0) {
HMODULE hDll = GetModuleHandle(msg.argument);
UnHook* unhook = (UnHook*)GetProcAddress(hDll, "UnHook");
unhook();
CloseHandle(hDll);
}
if (strcmp(msg.cmd, "boost") == 0) {
HMODULE hDll = GetModuleHandle("quaternion.dll");
SetBoost* setboost = (SetBoost*)GetProcAddress(hDll, "SetBoost");
setboost(atof(msg.argument));
}
server->CloseSocket();
}
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
CreateThread(0, 0, ThreadMain, hModule, 0, 0);
}
return TRUE;
}
※ SocketServer.cppのCloseSocket
関数のWSACleanup()
を消すのを忘れずに
クライアント側で追加したハンドラーとヘッダー
#include <string>
private: System::Void PlayerTelemoveButton_CheckedChanged(System::Object^ sender, System::EventArgs^ e) {
SocketClient* sc = new SocketClient();
if (PlayerTelemoveButton->Checked) {
PlayerTelemoveButton->Text = "Enabled";
sc->Send("load", "quaternion");
PlayerTelemoveTrackBar->Enabled = true;
}
else {
PlayerTelemoveButton->Text = "Disabled";
sc->Send("free", "quaternion");
PlayerTelemoveTrackBar->Enabled = false;
}
}
private: System::Void PlayerTelemoveTrackBar_ValueChanged(System::Object^ sender, System::EventArgs^ e) {
SocketClient* sc = new SocketClient();
std::string s = std::to_string(PlayerTelemoveTrackBar->Value);
sc->Send("boost", (char*)s.c_str());
}