Участник
-
Автор темы
- #1
данный гайд основан на dylib'е и реверсе от джаваскриптовых хрефов(
как реверсить джаваскриптовый хреф? берем с ссылки название нужной функции без класса(Game.GetGameTime превращается в GetGameTime), ищем хреф, должно найти несколько(на примере GetGameTime это GetScriptDesc<C_DOTAGameRules>::GetGameTime() или как то так называется, потом GetScriptDesc<ScriptBinding_PR>::GetGameTime() и GetScriptDesc<ScriptBinding_SF>::GetGameTime()). вам нужен GetScriptDesc<ScriptBinding_PR>::GetGameTime(). ну кароче на всякий случай тыкайте на каждый хреф и смотрите. ищите инструкцию типа lea r8, sub_blablabla. чекайте код этой sub_blablabla и если там есть какието очевидные зацепки(ну например если вы в дебагере сидите, чекайте всякие mov/lea адреса и если допустим вы там увидите mov rcx, [xxx] и потом этот xxx откроете в реклассе и увидите что судя по RTTI это C_DOTAGameRules/любой другой связаный по смыслу с функцией класс, то это то что вам надо), хрефы там или что-то еще. также можете брейкпоинт поставить на эти sub_blablabla и повызывать эти функции из джаваскрипта в демке(как это делать описано тута https://yougame.biz/threads/133895/ , но это долго и нудно на самом деле все ставить), что хорошенько ускорит ваш прогресс.
в общем нужные нам джаваскриптовые хрефы сегодня это CreatePanel , GetContextPanel , DeleteAsync и BLoadLayoutFromString.(особо не буду рассказывать как реверсить каждый из них, попробуйте сами, не сможете напишете помогу)
хреф "CreatePanel"(их две штуки) в panorama.dll:
нужная нам инструкция это lea r8, [xxx], переходим по этому xxx
видим тут еще один хреф(выделен синеньким), который убеждает нас что мы перешли туда куда надо
листаем ниже и видим mov qword:[xxx], чекаем что это за xxx такой(пкм->follow in dump->value), потом вводим этот же адрес(ctrl+g->ctrl+c) в реклассе и видим что это ничто иное как
panorama::CUIEngineSource2 : panorama::CUIEngine : panorama::IUIEngine : IToolsResourceListener : IRenderDeviceEventListener
дальше видим очевидный вызов виртуальной функции(выделен синеньким), после него test al,al, то есть эта функция возвращает булеану. что же за булеана? чекаем ниже и видим хреф "CreatePanelWithProperties second param (parent) must be a panel object". тут все понятно, функция чекает валидность второго параметра, она нам не нужна идем дальше видим похожие хрефы которые точно так же проверяют валидности параметров, идем дальше и находим хреф CreatePanelWithProperties - failed to create blablabla. такие хрефы(failed/error и т.д.) обычно идут уже после создания. то есть сначала попытка создать что-либо а потом если не получилось выдаем ошибку. так что смотрим что идет над этим хрефом
а идет там вызов panorama::CUIEngineSource2->функция на 0xf0. то есть вот сама наша функция которую мы так долго ждали. переходим на нее(берем из mov адрес panoram::CUIEngineSource2, берем вмт и прибавляем 0xf0 и переходим), ставим бп чекаем параметры с которыми она вызывается
параметры там такие(чтобы понять что за параметры вспоминаем первый хреф про type, parent, id, properties и все сразу становится на свои места)
rcx - this
rdx - указатель на type
r8 - айди панели(id)
r9 - указатель на panorama::CUIPanel, родитель создаваемой панели.(parent)
все понятно кроме указателя на type, смотрим на скрин видим там lea rdx,[rbp+590] и до этого mov word [rbp+590], ax и еще выше movzx edx, word:[rax] и еще выше вызов функции. тут тоже все понятно, вызывается функция и то что она возвращает это и есть type. засовываем этот type в локальную переменную(в стек) и передаем указатель на эту переменную в функцию создания панели. теперь посмотрим какие параметры у той функции которая возвращает тип. точно так же переходим ставим бп и видим что они такие:
rcx - указатель на word куда будет записан результат
rdx - строка которая из джаваскрипта с вызовом передается, судя по вызову $.CreatePanel( 'Panel', Game.GetMainHUD(), 'MyPanel' ), то нам нужна строка "Panel"
вот код этой функции. как видите(выделено синеньким) результат записывается в word указатель на который передан в rcx.
ну и вот кароче имеем в итоге
WORD PanelType;
GetPanelTypeF(&PanelType, "Panel");//можно инициализировать 1 раз за запуск чита, тип панели всегда одинаковый, вызывать каждый раз не надо
Pointer<CPanel2D> Panel2D = Panorama2->VirtualCall<30>(&PanelType, id, DOTAHud);//та самая +0xf0 .чекаем RTTI и видим что она возвращает panorama::CPanel2D. тыкаем туда сюда в реклассе/дебагере и описываем классы CUIPanel и CPanel2D:
точно так же реверсим остальные функции с хрефов.
GetMainHUD(то есть получение панельки куда мы будем пиндюрить свои панельки) сводится к FindPanelById("Hud"). код этой функции(который получен не помню как ):
DeleteAsync это
CPanel2D::DeleteAsyncPanel = GetAbsoluteAddress(Panel2D->VirtualMethod(42) + 0x735, 3, 7);
BLoadLayoutFromString это
void CUIPanel::LoadLayoutFromString(cc layout) {
VirtualCall<14>(layout, 0, 0);
}
стиль панельки реверсим от хрефа "CUIPanel::BSetProperty". он приведет вас к одноименной функции где вы ищете хреф "style" там чуть ниже будет mov word:[xxx],dx(оффсет 0x3f7) и этот xxx содержит индекс стиля. потом просто вызываете эту же самую функцию где передаете в rdx указатель на этот индекс стиля. то есть xxx можете просто передавать. эта функция имеет 307 индекс в вмт CUIPanel, что можно узнать поставив бп в самое начало данной функции и посмотреть стек перейти туда и посмотреть инструкцию выше(это будет call qword:[register+998]). применять это можно к любой функции которая вызвана через call(еще функции могут вызываться через jmp тогда в стеке не будет адреса который выполнил этот jmp), потому что инструкция call раскрывается в две инструкции: push RIP+sizeof(call); jmp FUNCTION; то есть перед вызовом в стек засовывается адрес возврата, то есть текущий адрес(RIP) + размер call, то есть следующая по счету инструкция. и когда вы ставите бп в начало функции вы можете просто заглянуть в стек и увидите кто вызвал эту функцию(исключение составляет вызов функции не через call а через jmp)
то есть,
void SetStyle(cc value) {
VirtualCall<307>(&StyleOffset, value, Panorama2->VirtualMethodsTable);//r9 не обязателен я туда просто так вмтху передаю, можете этого не делать
}
(это то что вы должны были получить в результате реверса, если у вас не получилось и вы не понимаете как это достать то пишите я разъясню)
все остальные функции берутся из dylib'ов - находите функцию в dylibе, вычисляете ее индекс, потом заходите в дебагер, находите этот же класс ставите брейкпоинты на функции рядом с этим индексом(ну допустим в dylibе индекс x, а я ставлю бпшки на x-2 -> x+2, потом расширяю радиус если надо), выполняете действие(ну допустим если я хукаю OnLeftMouseDown то я нажимаю левую кнопку мыши), где сработало та функция и есть искомая. берете ее индекс и воаля.
итак, алгоритм отрисовки на панораме у нас такой:
1. создаем панель с айди xxx(если что, может быть много панелек с одним и тем же айди, это разрешено)
2. загружаем в панель наш xml из строки
3. обрабатываем взаимодействие с панелькой(клики шлики наведение и т.д.)
4. по надобности(после клика например) изменяем свойства наших уже созданных элементов(изменяем текст надписи и т.д.)
сори не стал уже глубоко объяснять лень все подряд скринить и описывать. а так что-то конкретное могу объяснить пишите, будем вместе дополнять гайд по мере надобности.
код:(выпилил два неймспейса меню и абилитиесп, доделывайте сами)
как выглядит:
по поводу влияния данной фигни на фпс много сказать не могу. играя против ботов в люксембурге(ну то есть как паб кароче) поставив игру на паузу когда все 5 врагов были засвечены замерив фпс за 2 минуты с читом а потом без через cl_showfps1, cl_resetfps, cl_printfps у меня было -2фпс с читом. но это на паузе. а по факту должно быть больше -фпсов. не замерял потому что смысла нет так как в доте фпс нестабильный
если нашли баги пишите блаблабла.
кстати забыл сказать, в панораме есть такое понятие как масштаб, формула масштаба это round(высота_экрана/1080). то есть размеры и координаты из вашего xml умножаются на этот масштаб и отрисовываются уже в полученных размерах. то есть если у меня ширина панельки 50 пикселей в xml, то на моем 1366x768 она становится 50*768/1080, а это 36 пикселей экрана
Пожалуйста, авторизуйтесь для просмотра ссылки.
).как реверсить джаваскриптовый хреф? берем с ссылки название нужной функции без класса(Game.GetGameTime превращается в GetGameTime), ищем хреф, должно найти несколько(на примере GetGameTime это GetScriptDesc<C_DOTAGameRules>::GetGameTime() или как то так называется, потом GetScriptDesc<ScriptBinding_PR>::GetGameTime() и GetScriptDesc<ScriptBinding_SF>::GetGameTime()). вам нужен GetScriptDesc<ScriptBinding_PR>::GetGameTime(). ну кароче на всякий случай тыкайте на каждый хреф и смотрите. ищите инструкцию типа lea r8, sub_blablabla. чекайте код этой sub_blablabla и если там есть какието очевидные зацепки(ну например если вы в дебагере сидите, чекайте всякие mov/lea адреса и если допустим вы там увидите mov rcx, [xxx] и потом этот xxx откроете в реклассе и увидите что судя по RTTI это C_DOTAGameRules/любой другой связаный по смыслу с функцией класс, то это то что вам надо), хрефы там или что-то еще. также можете брейкпоинт поставить на эти sub_blablabla и повызывать эти функции из джаваскрипта в демке(как это делать описано тута https://yougame.biz/threads/133895/ , но это долго и нудно на самом деле все ставить), что хорошенько ускорит ваш прогресс.
в общем нужные нам джаваскриптовые хрефы сегодня это CreatePanel , GetContextPanel , DeleteAsync и BLoadLayoutFromString.(особо не буду рассказывать как реверсить каждый из них, попробуйте сами, не сможете напишете помогу)
хреф "CreatePanel"(их две штуки) в panorama.dll:
нужная нам инструкция это lea r8, [xxx], переходим по этому xxx
видим тут еще один хреф(выделен синеньким), который убеждает нас что мы перешли туда куда надо
листаем ниже и видим mov qword:[xxx], чекаем что это за xxx такой(пкм->follow in dump->value), потом вводим этот же адрес(ctrl+g->ctrl+c) в реклассе и видим что это ничто иное как
panorama::CUIEngineSource2 : panorama::CUIEngine : panorama::IUIEngine : IToolsResourceListener : IRenderDeviceEventListener
дальше видим очевидный вызов виртуальной функции(выделен синеньким), после него test al,al, то есть эта функция возвращает булеану. что же за булеана? чекаем ниже и видим хреф "CreatePanelWithProperties second param (parent) must be a panel object". тут все понятно, функция чекает валидность второго параметра, она нам не нужна идем дальше видим похожие хрефы которые точно так же проверяют валидности параметров, идем дальше и находим хреф CreatePanelWithProperties - failed to create blablabla. такие хрефы(failed/error и т.д.) обычно идут уже после создания. то есть сначала попытка создать что-либо а потом если не получилось выдаем ошибку. так что смотрим что идет над этим хрефом
а идет там вызов panorama::CUIEngineSource2->функция на 0xf0. то есть вот сама наша функция которую мы так долго ждали. переходим на нее(берем из mov адрес panoram::CUIEngineSource2, берем вмт и прибавляем 0xf0 и переходим), ставим бп чекаем параметры с которыми она вызывается
параметры там такие(чтобы понять что за параметры вспоминаем первый хреф про type, parent, id, properties и все сразу становится на свои места)
rcx - this
rdx - указатель на type
r8 - айди панели(id)
r9 - указатель на panorama::CUIPanel, родитель создаваемой панели.(parent)
все понятно кроме указателя на type, смотрим на скрин видим там lea rdx,[rbp+590] и до этого mov word [rbp+590], ax и еще выше movzx edx, word:[rax] и еще выше вызов функции. тут тоже все понятно, вызывается функция и то что она возвращает это и есть type. засовываем этот type в локальную переменную(в стек) и передаем указатель на эту переменную в функцию создания панели. теперь посмотрим какие параметры у той функции которая возвращает тип. точно так же переходим ставим бп и видим что они такие:
rcx - указатель на word куда будет записан результат
rdx - строка которая из джаваскрипта с вызовом передается, судя по вызову $.CreatePanel( 'Panel', Game.GetMainHUD(), 'MyPanel' ), то нам нужна строка "Panel"
вот код этой функции. как видите(выделено синеньким) результат записывается в word указатель на который передан в rcx.
ну и вот кароче имеем в итоге
WORD PanelType;
GetPanelTypeF(&PanelType, "Panel");//можно инициализировать 1 раз за запуск чита, тип панели всегда одинаковый, вызывать каждый раз не надо
Pointer<CPanel2D> Panel2D = Panorama2->VirtualCall<30>(&PanelType, id, DOTAHud);//та самая +0xf0 .чекаем RTTI и видим что она возвращает panorama::CPanel2D. тыкаем туда сюда в реклассе/дебагере и описываем классы CUIPanel и CPanel2D:
C++:
class CUIPanel{
public:
u64 vmt;
CPanel2D* _CPanel2D;
const char* id;
CUIPanel* parent;
u64 TopLevelWindow;
int numofchildren;
int junk;
CUIPanel** Children;
CUIPanel* GetChild(int child) {
if (child == -1) return this;
if (child >= numofchildren) return 0;
return Children[child];
}
template<typename... args> CUIPanel* GetChild(int child, args... pack) {
if (child == -1) return this;
if (child >= numofchildren) return 0;
return Children[child]->GetChild(pack...);
}
};
class CPanel2D{
public:
u64 vmt;
CUIPanel* CUIPanel;
GetMainHUD(то есть получение панельки куда мы будем пиндюрить свои панельки) сводится к FindPanelById("Hud"). код этой функции(который получен не помню как ):
C++:
CUIPanel* FindPanelById(cc id) {
// struct PanelListItem {
// u64 junk[2];
// CUIPanel* panel1;
// u64 Junk[3];
// CUIPanel* panel2;
//};
//Pointer<PanelListItem> PanelList() {
// return Member(0x140);
// }
//stringsmatch(x,y) это !strcmp(x,y)
for (auto Item = Panorama2->PanelList();; Item += 64) {
if (StringsMatch(Item->panel1->id, id)) {
return Item->panel1;
}
if (StringsMatch(Item->panel2->id, id)) {
return Item->panel2;
}
}
}
CPanel2D::DeleteAsyncPanel = GetAbsoluteAddress(Panel2D->VirtualMethod(42) + 0x735, 3, 7);
BLoadLayoutFromString это
void CUIPanel::LoadLayoutFromString(cc layout) {
VirtualCall<14>(layout, 0, 0);
}
стиль панельки реверсим от хрефа "CUIPanel::BSetProperty". он приведет вас к одноименной функции где вы ищете хреф "style" там чуть ниже будет mov word:[xxx],dx(оффсет 0x3f7) и этот xxx содержит индекс стиля. потом просто вызываете эту же самую функцию где передаете в rdx указатель на этот индекс стиля. то есть xxx можете просто передавать. эта функция имеет 307 индекс в вмт CUIPanel, что можно узнать поставив бп в самое начало данной функции и посмотреть стек перейти туда и посмотреть инструкцию выше(это будет call qword:[register+998]). применять это можно к любой функции которая вызвана через call(еще функции могут вызываться через jmp тогда в стеке не будет адреса который выполнил этот jmp), потому что инструкция call раскрывается в две инструкции: push RIP+sizeof(call); jmp FUNCTION; то есть перед вызовом в стек засовывается адрес возврата, то есть текущий адрес(RIP) + размер call, то есть следующая по счету инструкция. и когда вы ставите бп в начало функции вы можете просто заглянуть в стек и увидите кто вызвал эту функцию(исключение составляет вызов функции не через call а через jmp)
то есть,
void SetStyle(cc value) {
VirtualCall<307>(&StyleOffset, value, Panorama2->VirtualMethodsTable);//r9 не обязателен я туда просто так вмтху передаю, можете этого не делать
}
(это то что вы должны были получить в результате реверса, если у вас не получилось и вы не понимаете как это достать то пишите я разъясню)
все остальные функции берутся из dylib'ов - находите функцию в dylibе, вычисляете ее индекс, потом заходите в дебагер, находите этот же класс ставите брейкпоинты на функции рядом с этим индексом(ну допустим в dylibе индекс x, а я ставлю бпшки на x-2 -> x+2, потом расширяю радиус если надо), выполняете действие(ну допустим если я хукаю OnLeftMouseDown то я нажимаю левую кнопку мыши), где сработало та функция и есть искомая. берете ее индекс и воаля.
итак, алгоритм отрисовки на панораме у нас такой:
1. создаем панель с айди xxx(если что, может быть много панелек с одним и тем же айди, это разрешено)
2. загружаем в панель наш xml из строки
3. обрабатываем взаимодействие с панелькой(клики шлики наведение и т.д.)
4. по надобности(после клика например) изменяем свойства наших уже созданных элементов(изменяем текст надписи и т.д.)
сори не стал уже глубоко объяснять лень все подряд скринить и описывать. а так что-то конкретное могу объяснить пишите, будем вместе дополнять гайд по мере надобности.
код:(выпилил два неймспейса меню и абилитиесп, доделывайте сами)
Пожалуйста, авторизуйтесь для просмотра ссылки.
вбе тут нет сами добавите из прошлой части.как выглядит:
по поводу влияния данной фигни на фпс много сказать не могу. играя против ботов в люксембурге(ну то есть как паб кароче) поставив игру на паузу когда все 5 врагов были засвечены замерив фпс за 2 минуты с читом а потом без через cl_showfps1, cl_resetfps, cl_printfps у меня было -2фпс с читом. но это на паузе. а по факту должно быть больше -фпсов. не замерял потому что смысла нет так как в доте фпс нестабильный
если нашли баги пишите блаблабла.
кстати забыл сказать, в панораме есть такое понятие как масштаб, формула масштаба это round(высота_экрана/1080). то есть размеры и координаты из вашего xml умножаются на этот масштаб и отрисовываются уже в полученных размерах. то есть если у меня ширина панельки 50 пикселей в xml, то на моем 1366x768 она становится 50*768/1080, а это 36 пикселей экрана
Последнее редактирование: