ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   Общие вопросы программирования (https://forum.antichat.xyz/forumdisplay.php?f=206)
-   -   Удаленный вызов игровой функции (https://forum.antichat.xyz/showthread.php?t=1505175)

UnknownPerson 07.03.2024 00:07

Привет! Не буду тянуть кота за хвост и перейду сразу к сути

В samp.dll имеется функция, которая создает клиентские команды (адрес функции — 0x69000). Выглядит функция так:

AddCommand:





Код:

void
__thiscall CInput
::
AddCommand
(
CInput
*
this
,
const
char
*
szName
,
void
(
__cdecl
*
handler
)
(
const
char
*
)
)



Соответственно функция принимает название команды и хандлер для обработки этой команды, который принимает введенные игроком параметры и исполняет какой-либо код

Я вызываю эту функцию удаленно через CreateRemoteThread и прочие приблуды, функция вызывается и выполняется, команда создается, с этим никаких проблем нет, но есть проблема с хандлером, который нужно засунуть в качестве второго аргумента функции

Прошерстив интернет я выяснил, что засунуть хандлер в принципе возможно, но как это сделать и возможно ли это вообще в сампе — непонятно

В качестве хандела я пытаюсь засунуть шеллкод:

shellcode:





Код:

byte
[
]
asmBytes
=
new
byte
[
]
{
0x55
,
// push ebp
0x89
,
0xE5
,
// mov ebp, esp
0x89
,
0xEC
,
// mov esp, ebp
0x5D
,
// pop ebp
0xC3
// ret
}
;



Затем я выделяю память для этого кода и записываю его в процесс, а также получаю адрес выделенной области, который передаю как хандлер:

Запись в процесс:





Код:

memoryAddress
=
VirtualAllocEx
(
handle
,
IntPtr
.
Zero
,
asmBytes
.
Length
,
0x1000
,
0x40
)
;
WriteMemoryBytes
(
memoryAddress
,
asmBytes
)
;



Код успешно регистрирует клиентскую команду и при использовании ее в игре краша нет, но и хандлер ничего не выполняет, а служит обычной "заглушкой"

Мне нужно, чтобы хандлер вызывал функцию AddEntry (0x67460) в случае, если игрок не ввел ни одного параметра в команде, и функцию SendCommand (0x69190) (отправляет сразу команду в чат), если пользователь ввел какие-то параметры в команде

Функция AddEntry:





Код:

void
__thiscall CInput
::
AddCommand
(
CInput
*
this
,
const
char
*
szName
,
void
(
__cdecl
*
handler
)
(
const
char
*
)
)



Я пробовал вызвать AddEntry следующим образом:

C#:





Код:

byte
[
]
asmBytes
=
new
byte
[
]
{
0x55
,
// push ebp
0x89
,
0xE5
,
// mov ebp, esp
0x89
,
0xEC
,
// mov esp, ebp
0x68
,
0x00
,
0x00
,
0x00
,
0x00
,
// push CChatAddress (сюда вписывается samp.dll + 0x26E8C8)
0x6A
,
0x08
,
// push 0x8
0x68
,
0x74
,
0x65
,
0x78
,
0x74
,
// push "text"
0x68
,
0x00
,
0x00
,
0x00
,
0x00
,
// push 0x0 (empty string for szPrefix)
0x68
,
0xFF
,
0xFF
,
0xFF
,
0xFF
,
// push 0xFFFFFF (textColor)
0x68
,
0xFF
,
0xFF
,
0xFF
,
0xFF
,
// push 0xFFFFFF (prefixColor)
0xB9
,
0x00
,
0x00
,
0x00
,
0x00
,
// mov ecx, AddEntryFunctionAddress (сюда вписывается samp.dll + 0x67460)
0xFF
,
0xD1
,
// call ecx
0x5D
,
// pop ebp
0xC3
// ret
}
;



Но игра вызывает краш после ввода команды и попытки выполнить этот код. Хочу также подметить, что в ассемблере я не силен от слова совсем

Из всего вышенаписанного вытекает вопрос: реально ли вообще реализовать вызов функции AddCommand подобным образом и если да, то мне нужен пример того, как это можно правильно сделать с краткими объяснениями (ну или если есть желание, то с подробными, было бы вообще круто)

Все вышепоказанное я делал на C#, но примеры на C++ (если таковые имеются) также приму

Заранее спасибо :)

RedHolms 24.03.2024 03:12

на счёт C# не знаю вообще, но на C++ ты просто буквально передаешь функцию:

C++:





Код:

void
handler
(
const
char
*
arg
)
{
/// ....
}
void
reg
(
)
{
CInput
::
AddCommand
(
"mycmf"
,
handler
)
;
}



Я бы советовал поискать в интернете, возможно ли передать C#-пную функцию как аргумент C-шной (100% функция должна быть статической)

И еще, не балуйся с потоками, так как самп не thread-safe

вайега52 25.03.2024 20:57

Цитата:

Сообщение от RedHolms

на счёт C# не знаю вообще, но на C++ ты просто буквально передаешь функцию:

если я правильно понимаю, автор темы делает экстернал чит, в таком случае, если я я верно думаю, необходимо загружать байты (в данном случае функцию) внутрь виртуальной памяти процесса

Vintik 02.04.2024 23:19

Да, привет!

Ты всё понял верно, шаги у тебя правильные.

На самом деле, нужно понять на каком моменте что-то ломается.

Цитата:

Сообщение от UnknownPerson

Код успешно регистрирует клиентскую команду и при использовании ее в игре краша нет, но и хандлер ничего не выполняет, а служит обычной "заглушкой"

Шаг первый. Нужно проверить, работает ли твоя заглушка. В консоль твоей программы выпиши адрес области, которая выделяется VirtualAllocEx (эта же функция и возвращает этот адрес). После инъекции, уже при работе программы, открываешь Cheat Engine (CE), дальше Memory Viewer, дальше Ctrl + G и вставляй этот адрес. У тебя должна открыться твоя же функция (те байты, которые ты написал). Выбираешь любой из них (любую строку) и нажимаешь F5 (либо ПКМ и «Break and trace instructions»). А теперь попробуй ввести в чат команду, которую ты зарегистрировал. CE должен будет остановить твою игру, а в меню Memory Viewer появится активная кнопка «Продолжить» (как при паузе). Если так, то это оно! Значит игра проходит через твою заглушку.

Цитата:

Сообщение от UnknownPerson

Но игра вызывает краш после ввода команды и попытки выполнить этот код. Хочу также подметить, что в ассемблере я не силен от слова совсем

Шаг второй. Я думаю, что в ASM коде мало кто силён. Если шаг первый успешно выполнен, то единственная задача — правильно написать функцию, не сломав при этом значения регистров и программный стек. Прототип твоей функции следующий:

C++:





Код:

void
__cdecl
handler
(
const
char
*
)
;



И никто не мешает тебе в отдельной программе (не основной!) написать на C++ ту функцию, которая тебе нужна:

C++:





Код:

// создаёшь прототипы своих функций (я не знаю какие аргументы там)
typedef
void
(
__thiscall
*
AddEntry
)
(
int
,
int
,
const
char
*
,
int
,
int
,
int
)
;
int
SampDll
;
// получаешь адрес модуля samp.dll
void
__cdecl
handler
(
const
char
*
arg
)
{
if
(
strlen
(
arg
)
==
0
)
reinterpret_cast

(
SampDll
+
0x67460
)
(
SampDll
+
0x26E8C8
,
8
,
"text"
,
0x0
,
0xFFFFFF
,
0xFFFFFF
)
;
.
.
.
}



А дальше компилируешь это всё дело в x32. Можешь в коде где-то в
Код:

main()
написать
[CODE]
std::cout Only Bytes») и вставить в массив. Всю неприятную работу по созданию ASM кода выполнит компилятор. Если изначальные прототипы функций (не только твоей, но и тех, что ты вызываешь внутри) верны, то краша быть не должно.

Цитата:

Сообщение от Спойлер

Даже при краше пишет адрес краша («Exception at address ...»), так ты можешь посмотреть, где и что конкретно вызывает краш.

upd.

Чтобы получить адрес модуля
Код:

samp.dll
воспользуйся этой функцией (это можно делать как внутри твоего вызова каждый раз, так и один раз при запуске):

C++:





Код:

DWORD
GetProcId
(
const
char
*
procname
)
{
PROCESSENTRY32 pe
;
HANDLE hSnap
;
pe
.
dwSize
=
sizeof
(
PROCESSENTRY32
)
;
hSnap
=
CreateToolhelp32Snapshot
(
TH32CS_SNAPPROCESS
,
NULL
)
;
if
(
Process32First
(
hSnap
,
&
pe
)
)
{
do
{
if
(
strcmp
(
pe
.
szExeFile
,
procname
)
==
0
)
break
;
}
while
(
Process32Next
(
hSnap
,
&
pe
)
)
;
}
return
pe
.
th32ProcessID
;
}
HMODULE
GetModuleHandleExtern
(
const
char
*
szModuleName
,
DWORD dwProcessId
)
{
if
(
!
szModuleName
||
!
dwProcessId
)
{
return
NULL
;
}
HANDLE hSnap
=
CreateToolhelp32Snapshot
(
TH32CS_SNAPMODULE
,
dwProcessId
)
;
if
(
hSnap
==
INVALID_HANDLE_VALUE
)
{
return
NULL
;
}
MODULEENTRY32 me
;
me
.
dwSize
=
sizeof
(
MODULEENTRY32
)
;
if
(
Module32First
(
hSnap
,
&
me
)
)
{
while
(
Module32Next
(
hSnap
,
&
me
)
)
{
if
(
!
strcmp
(
me
.
szModule
,
szModuleName
)
)
{
CloseHandle
(
hSnap
)
;
return
me
.
hModule
;
}
}
}
CloseHandle
(
hSnap
)
;
return
NULL
;
}



Использование:





Код:

HMODULE SampDll
=
GetModuleHandleExtern
(
"samp.dll"
,
GetProcId
(
"gta_sa.exe"
)
)
;
// HMODULE — это обычный указатель, который занимает 4 байта в x32. Поэтому можешь использовать C-cast: (unsigned int)SampDll



Будут еще вопросы — пиши.

UnknownPerson 05.04.2024 15:55

Огромное спасибо за помощь, подсказка с плюсами и CE мне очень помогла, правда, я сделал немого иначе — написал хандлер с asm кодом для вызова определенных функций, скомпилировал все в dll, инжектнул в игру и получил тело всей функции уже в виде шеллкода, ну и соответственно на шарпе я выполнил все те действия, которые описывал выше, в конечном итоге это сработало

Еще раз огромное спасибо


Время: 16:14