PDA

Просмотр полной версии : Вызов lua калбека через sol внутри обработчика хука без обработки C++ исключений


Tema05
13.06.2025, 17:56
Вот упрощённый вариант частей моей dll-ки. Я ставлю хук на функцию игры и из lua устанавливаю калбек, который будет вызываться из оригинального C++ калбека. Всё отлично работает, но есть проблема с обработкой ошибок.

C++:






// установка lua калбека для вызова из хука
void
set_callback
(
sol
::
this_state ts
,
sol
::
unsafe_function func
)
{
gHandler
=
func
;
}
// вызывается при подключении библиотеки из lua через require
sol
::
table
open
(
sol
::
this_state ts
)
{
sol
::
state_view
lua
(
ts
)
;
installHook
(
)
;
module
.
set_function
(
"set_callback"
,
&
set_cb
)
;
return
module
;
}
extern
"C"
__declspec
(
dllexport
)
int
luaopen_lib
(
lua_State
*
L
)
{
return
(
sol
::
c_call

)
(
L
)
;
}
sol
::
unsafe_function gHandler
;
// тут будет хранится функция lua калбека
kthook
::
kthook_simple

mHookedFunc
;
// объект хука
// функция установки C++ хука
void
installHook
(
)
{
mHookedFunc
.
set_dest
(
0x123456
)
;
// адрес функции в игре на которую нужно поставить хук
mHookedFunc
.
set_cb
(
handleHookedFunc
)
;
// калбек, который будет срабатывать при вызове функции
mHookedFunc
.
install
(
)
// устанавливает хук
}
// калбек захуканной функции
bool
handleHookedFunc
(
const
kthook
::
kthook_simple

&
hook
,
void
*
ptr
)
{
gHandler
(
)
;
// вызов lua калбека. Если он пройдёт с ошибкой управление текущему потоку не вернётся и код ниже не выполнится
return
hook
.
call_trampoline
(
ptr
)
;
// трамплин на оригинальную функцию
}




Lua:






local
lib
=
require
'lib'
lib
.
set_callback
(
function
(
)
error
(
"LUA ERROR"
)
-- условно какая-то ошибка.
end
)




Если внутри lua калбека происходит какая-то ошибка (у меня искуственно при помощи

error("CRASH")

) то lua скрипт закономерно должен крашнуться. Где-то внутри вызова

gHandler()

выполнится lua_error (через sol) и ошибка дойдёт до lua. Но, проблема в том что в этом случаи управление потоку не будет возвращено и весь код после вызова неудачно выполненной lua функции

gHandler()

не отработает. Соответственно в представленном варианте

return hook.call_trampoline(ptr);

не выполнится, а это нарушает хука и игра разумеется умирает.

Я могу заменить sol::unsafe_function на sol::protected_function, тогда sol обернёт вызов lua функции в pcall, который отловит ошибку в lua калбеке и трамплин выполнится. Но, в этом случаи lua скрипт не крашнется. Ошибка была отловлена и "проглочена" при помощи pcall, в C++ мы её дальше никак не обрабатываем. А это мне не подходит, я хочу получать краш lua скрипта при ошибках в lua калбеке.

Если добавить проброс пойманной ошибки через throw это ни к чему хорошему не приведёт. Оригинальный C++ калбек хука handleHookedFunc выполняется непосредственно в том же потоке что и захуканная функция, т.е. в потоке игры, а это значит что исключение пробрасывается внутрь игры, это если не брать в расчёт что трамплин, который идёт позже также не вызывается. Игра также умирает.

C++:






bool
handleHookedFunc
(
const
kthook
::
kthook_simple

&
hook
,
void
*
ptr
)
{
sol
::
protected_function_result result
=
gHandler
(
)
;
// вызов lua калбека. Если он пройдёт с ошибкой управление текущему потоку не вернётся и код ниже не выполнится
if
(
!
result
.
valid
(
)
)
{
sol
::
error err
=
result
;
throw
err
;
}
return
hook
.
call_trampoline
(
ptr
)
;
// трамплин на оригинальную функцию
}




Теперь смотрим на moonloader. Там через addEventHandler можно установить калбека на всякие события, например "onReceivePacket" (событие отправки пакета данный на сервер). Эти события по сути также хуки каких-то функций/методов в игре. И внутри этих lua калбеков ошибки обрабатываются так как я хочу. Они крашат lua скрипт, при этом это никак не мешает работе игры, несмотря на то что вызов lua калбека идёт из хука.

Lua:






addEventHandler
(
'onReceivePacket'
,
function
(
id
,
bs
)
error
(
'LUA ERROR'
)
end
)




Как мне реализовать такую же обработку ошибок как в moonloader? Чтобы ошибки внутри lua калбека крашили скрипт, а не игнорировались, но не ломали хук из которого они вызываются?

вайега52
13.06.2025, 18:08
Вот упрощённый вариант частей моей dll-ки. Я ставлю хук на функцию игры и из lua устанавливаю калбек, который будет вызываться из оригинального C++ калбека. Всё отлично работает, но есть проблема с обработкой ошибок.

C++:






// установка lua калбека для вызова из хука
void
set_callback
(
sol
::
this_state ts
,
sol
::
unsafe_function func
)
{
gHandler
=
func
;
}
// вызывается при подключении библиотеки из lua через require
sol
::
table
open
(
sol
::
this_state ts
)
{
sol
::
state_view
lua
(
ts
)
;
installHook
(
)
;
module
.
set_function
(
"set_callback"
,
&
set_cb
)
;
return
module
;
}
extern
"C"
__declspec
(
dllexport
)
int
luaopen_lib
(
lua_State
*
L
)
{
return
(
sol
::
c_call

)
(
L
)
;
}
sol
::
unsafe_function gHandler
;
// тут будет хранится функция lua калбека
kthook
::
kthook_simple

mHookedFunc
;
// объект хука
// функция установки C++ хука
void
installHook
(
)
{
mHookedFunc
.
set_dest
(
0x123456
)
;
// адрес функции в игре на которую нужно поставить хук
mHookedFunc
.
set_cb
(
handleHookedFunc
)
;
// калбек, который будет срабатывать при вызове функции
mHookedFunc
.
install
(
)
// устанавливает хук
}
// калбек захуканной функции
bool
handleHookedFunc
(
const
kthook
::
kthook_simple

&
hook
,
void
*
ptr
)
{
gHandler
(
)
;
// вызов lua калбека. Если он пройдёт с ошибкой управление текущему потоку не вернётся и код ниже не выполнится
return
hook
.
call_trampoline
(
ptr
)
;
// трамплин на оригинальную функцию
}




Lua:






local
lib
=
require
'lib'
lib
.
set_callback
(
function
(
)
error
(
"LUA ERROR"
)
-- условно какая-то ошибка.
end
)




Если внутри lua калбека происходит какая-то ошибка (у меня искуственно при помощи

error("CRASH")

) то lua скрипт закономерно должен крашнуться. Где-то внутри вызова

gHandler()

выполнится lua_error (через sol) и ошибка дойдёт до lua. Но, проблема в том что в этом случаи управление потоку не будет возвращено и весь код после вызова неудачно выполненной lua функции

gHandler()

не отработает. Соответственно в представленном варианте

return hook.call_trampoline(ptr);

не выполнится, а это нарушает хука и игра разумеется умирает.

Я могу заменить sol::unsafe_function на sol::protected_function, тогда sol обернёт вызов lua функции в pcall, который отловит ошибку в lua калбеке и трамплин выполнится. Но, в этом случаи lua скрипт не крашнется. Ошибка была отловлена и "проглочена" при помощи pcall, в C++ мы её дальше никак не обрабатываем. А это мне не подходит, я хочу получать краш lua скрипта при ошибках в lua калбеке.

Если добавить проброс пойманной ошибки через throw это ни к чему хорошему не приведёт. Оригинальный C++ калбек хука handleHookedFunc выполняется непосредственно в том же потоке что и захуканная функция, т.е. в потоке игры, а это значит что исключение пробрасывается внутрь игры, это если не брать в расчёт что трамплин, который идёт позже также не вызывается. Игра также умирает.

C++:






bool
handleHookedFunc
(
const
kthook
::
kthook_simple

&
hook
,
void
*
ptr
)
{
sol
::
protected_function_result result
=
gHandler
(
)
;
// вызов lua калбека. Если он пройдёт с ошибкой управление текущему потоку не вернётся и код ниже не выполнится
if
(
!
result
.
valid
(
)
)
{
sol
::
error err
=
result
;
throw
err
;
}
return
hook
.
call_trampoline
(
ptr
)
;
// трамплин на оригинальную функцию
}




Теперь смотрим на moonloader. Там через addEventHandler можно установить калбека на всякие события, например "onReceivePacket" (событие отправки пакета данный на сервер). Эти события по сути также хуки каких-то функций/методов в игре. И внутри этих lua калбеков ошибки обрабатываются так как я хочу. Они крашат lua скрипт, при этом это никак не мешает работе игры, несмотря на то что вызов lua калбека идёт из хука.

Lua:






addEventHandler
(
'onReceivePacket'
,
function
(
id
,
bs
)
error
(
'LUA ERROR'
)
end
)




Как мне реализовать такую же обработку ошибок как в moonloader? Чтобы ошибки внутри lua калбека крашили скрипт, а не игнорировались, но не ломали хук из которого они вызываются?


Я подозреваю, что обертка sol'а над луа функциями в случае ошибки кидает исключение, которое можешь попробовать поймать внутри C++ каллбека на хук

вайега52
13.06.2025, 18:30
кстати, наверное тебе лучше использовать std::function, т.к. он более безопасный, но при этом, насколько помню, не оборачивает в pcall

Tema05
13.06.2025, 19:10
Я подозреваю, что обертка sol'а над луа функциями в случае ошибки кидает исключение, которое можешь попробовать поймать внутри C++ каллбека на хук


Оно не кидает исключение. При ошибке с sol::unsafe_function там идёт longjmp из-за lua_error и управление уйдёт, даже если в try catch завернуть, а при sol::protected_function исключение не передаётся, только статус и текст ошибки если есть.



кстати, наверное тебе лучше использовать std::function, т.к. он более безопасный, но при этом, насколько помню, не оборачивает в pcall


std::function (видимо ты имел ввиду sol::function) это и есть sol::unsafe_function или sol::protected_function в зависимости от наличия

#define SOL_NO_EXCEPTIONS 1


https://forum.antichat.xyz/attachments/28614074/

Я когда дебаггером пытался разобраться как оно в moonloader работает заметил, что иногда скрипт может крашнуться одновременно из-за нескольких ошибок. Что по идеи невозможно из-за однопоточного устройства lua или происходит из-за гонки потоков. Причём это не одно и то же событие и разные. Значит moonloader как-то в отдельных потоках или корутиных выполняет все эти события. Это скорее всего и позволяет обрабатывать ошибки на месте без потери управления потоком

https://forum.antichat.xyz/attachments/28614074/