Гайд Продолжение. вбе(партикли)

Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
итак в этом туторе мы будем делать вбе(рисуем под ногами героя кольцо(ну либо любой другой партикль на ваш выбор) когда он засвечен врагами(будь то варды или инвизный герой рядом или еще что)).
состоит из двух частей: получение информации о том, видит ли нас враг и создании партиклей.
начнем с первой части. дота не хранит информацию о том, видит ли в конкретный момент времени враг какого-либо юнита, она лишь совершает какие-то действия при изменении данного параметра, то есть, если враг УВИДЕЛ сущность, то что-то там произошло, если враг ПЕРЕСТАЛ ВИДЕТЬ сущность, что-то еще произошло. то есть мы не можем просто взять какой-нибудь нетвар и из этого сделать вывод видит ли кого-то враг(исключение локал игрок, у него в сущности хранится эта инфа). но мы можем хукнуть коллбек, который вызывается при изменении нужного нам нетвара. коллбек называется OnTeamVisibilityChanged, находим с хрефа.

(на скрине подписано REGISTERNETVARCHANGECALLBACK но это не он, на самом деле он ниже в конце функции там где call [r10+b8], r10+b8 это и есть registernetworkchangecallback чето там)
тут два варианта. из скрина очевидно что загружается указатель на функцию и куда-то суется. мы можем либо пойти реверсить и смотреть че куда там эта фигня суется и потом просто заменить там указатель и назначить свой коллбек(как в вмт хуке), либо можем просто забайтпатчить(что дешевле по времени). я выбрал второй вариант потому что нифига не нашел и желания особо там рыться не было.
давайте определим "байтпатч".
байтпатч это когда мы просто заменяем байтики в памяти, которые отвечают за инструкции данной функции.
хук это когда мы заставляем какую-то функцию(или любой другой объект) передать управление в наши руки.
совмещаем два понятия и получаем "байтпатч хук":
заменяем байтики в функции так, чтобы получились инструкции, передающие управление нам в руки(например jmp, call и т.д.)
остановимся на jmp.
я сижу на х64, поэтому адреса 8 байт. моя длл находится в куче(адрес 0000019D88413500 например)(мануал мап инжект), поэтому чтобы допрыгнуть оттуда из PE(адрес 00007FFB2EA0C4EB например), нужно 8 байт(разница между двумя адресами слишком огромная и в 4 байта не влезет).
следовательно используем две инструкции: mov rax, [absolute 8 byte address]; jmp rax, которые в байтах будут 48 a1 <8 bytes absolute address> ff e0, то есть 12 байт.
теперь давайте разберемся, где же тут лучше хукнуть.


так как мы своим хуком засираем rax, то нужно бы хукнуть перед тем местом, где оригинальная функция сама что-то пишет в rax. такое место присутствует на оффсете +0x29. поэтому нам нужно хукнуть перед этой инструкцией. идеальное место находится на оффсете 0x1b, там лежат 4 инструкции которые в сумме как раз дают 12 байт(даже если бы оно было не так, ничего страшного, мы бы просто нопнули(заменили на 0x90) лишний шлак. тут просто так сложились обстоятельства что никаких лишних действий нам выполнять не надо). наша цель: заменить эти 12 байт на mov rax, [blablabla]; jmp rax, в нашем [blablabla] вызывать MyHook, выполнить те оригинальные 4 инструкции которые мы перезаписали(естественно предварительно их скопировав из оригинала перед байтпатчем) и выполнить прыжок обратно на оригинал после нашего хука(оригинал+0x1b+12).
то есть так оно должно выглядеть после нашей магии:


и наша обертка куда мы прыгаем из оригинала

сначала делаем обертку(то куда мы прыгаем из оригинала), которая состоит из вызова нашей собственной функции которая делает грязные дела + оригинальных инструкций которые затерли самим прыжком + трамплина(прыжок обратно в оригинал):
тут много вариантов.мы либо просто вызываем VirtualAlloc с rwx, либо ваще что душе угодно. я решил сделать говнофункцию не несующую смысловую нагрузку, снять с странички где она находится протект(поставить rwx), и пробайтпатчить ее: сделать там вызов MyHook, выполнить там инструкции с 12 оригинальных байт, выполнить там прыжок обратно на оригинал.
C++:
u64 MyShitFunction() {
    u64 a = __rdtsc();
    u64 b = __readgsqword(0);
    u64 c = __readgsqword(1);
    u64 d = __readgsqword(2);
    u64 e = __readgsqword(2);
    u64 f = __readgsqword(3);
    return a + b + c + d + e + f;
    //пишем все что угодно главное чтобы она размером была как минимум 5(call MyHook) + 12+-(оригинальные байты которые мы перезаписали, в нашем случае 12 байт но могло быть больше) + 12(прыжок обратно на оригинал) байт
}

...
    //MyShitF это адрес MyShitFunction, TVCHook это адрес нашего MyHook
        // тут вместо 0x1b используется оффсет 0x1a потому что сига начинается на 1 байт позже чем со скринов
    //то есть кароче OnTVC это адрес функции + 1 конкретно в моем случае потому что я так захотел(не помню почему:D)
    MEMORY_BASIC_INFORMATION mbi;
        VirtualQuery(MyShitFunction, &mbi, sizeof(mbi));
        VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
        *(char*)(MyShitF) = 0xe8;
        //call <4 byte rel>
        *(int*)(MyShitF + 1) = TVCHook - MyShitF - 5;
        //нам нужно прыгнуть с x1 на x2, то есть:
        //x1+offset = x2
        //offset = x2 - x1
        //и из этого еще вычитаем размер инструкции - так как у нас call(e8 <4 byte rel>) то это 5 байт
        MemCopy(MyShitF + 5, OnTVC + 0x1a, 12);
        //копируем оригинал
        *(short*)(MyShitF + 5 + 12) = 0xa148;
        //mov rax, [8 byte abs]
        TVC_BACK_JUMP = OnTVC + 0x1a + 12;
        //то есть нам нужно прыгнуть на оригинал+оффсет на котором мы перезаписали + размер нашей перезаписи(12 байт размер хука)
        *(void**)(MyShitF + 5 + 12 + 2) = &TVC_BACK_JUMP;
        //у нас инструкция mov rax, [8 byte abs], то есть происходит дереференс, то есть нам нужно запихать сюда именно адрес указателя где будет хранится адрес куда нужно прыгнуть, а не сам этот адрес куда нужно прыгнуть
        *(short*)(MyShitF + 5 + 12 + 2 + 8) = 0xe0ff;
        //jmp rax
        VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &mbi.Protect);
        //все пробайтпатчили rwx нам не нужен больше
...
    //теперь сама функция в доте
     MEMORY_BASIC_INFORMATION mbi;
        VirtualQuery(OnTVC, &mbi, sizeof(mbi));
        VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
        TVC_HOOK_JUMP = MyShitFunction;
        *(short*)(OnTVC + 0x1a) = 0xa148;
        //mov rax, []
        *(void**)(OnTVC + 0x1a + 2) = &TVC_HOOK_JUMP;
        //именно указатель на наш трамплин
        *(short*)(OnTVC + 0x1a + 2 + 8) = 0xe0ff;
        //jmp rax
        VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, &mbi.Protect);
дальше сама функция MyHook:
C++:
u64 TVC_HOOK(u64 rcx, u64 rdx, u64 r8, u64 r9) {
    g_r10 = getr10();//g_r10 это глобальная 8 байт переменная
    g_r11 = getr11();
    if (Heroes) {
        for (Hero& hero : *Heroes) {
            if (hero.entity == rcx) {
                hero.VisibleToEnemy = (*(int*)r8 == 14 || *(int*)r8 == 30);
                break;
            }
        }
    }
    setr1011(g_r10, g_r11);//регеню r10 и r11
    setFC(rcx, rdx, r8, r9);//регеню rcx,rdx,r8,r9 потому что __fastcall. сам код функции просто ret и она ничего не делает. нас интересует лишь тот факт что перед вызовом компилятор засунет наши аргументы куда надо.
    return 0;
}
то есть мы берем rcx, находим сущность которая имеет такой адрес в нашем менеджере, смотрим на *(int*)r8 и в зависимости от значения записываем в нашу сущность видит ли ее враг или нет
помимо этого нам нужно пристально следить за тем,чтобы супер умный компилятор не зарал регистры которые он не собирается восстанавливать, такие как r10 и r11 в моем случае. у вас может быть по-другому, смотрите сами. если видите в вашей собранной функции(откройте вашу длл в x64dbg и найдите функцию по символу) инструкции типа mov r10/mov r11/mov <любой другой регистр который по окончанию функции не восстанавливается(push/pop)> то сочувствую. подрубаем asm и ручками сохраняем данные регистры. заходим в визуалку нажимаем пкм по проекту -> build dependencies ->build customizations -> галочку ставим .masm, создаем Source File с названием <название>.asm пишем там наш шлак типа:
Код:
.CODE
     setFC PROC
     ret
     setFC ENDP
     getr10 PROC
     mov rax, r10
     ret
     getr10 ENDP
     setr1011 PROC
     mov r10, rcx
     mov r11, rdx
     ret
     setr1011 ENDP
     getr11 PROC
     mov rax, r11
     ret
     getr11 ENDP
     END
потом в коде где-нибудь extern "C" u64 getr11(); extern "C" void setr1011(u64,u64); и т.д.
в начале функции сохраняем загрязняемые регистры, в конце функции восстанавливаем их. вполне возможно что как только вы внесете малейшие изменения в код функции MyFunc компилятор пересоберет с засиранием уже других регистров... готовьтесь морально.
ну в общем подводим итог байтпатч хука:
в оригинале делаем трамплин: mov rax, [trampoline]; jmp rax; еще нужно учитывать что вы таким хуком засираете rax, поэтому мы и хукаем перед тем как функция сама запишет в rax - в тот момент rax ей нафиг не нужен и не зависимо от того засран он или нет он примет новое значение.
в трамплине: вызываем MyHook, выполняем оригинальные инструкции, прыгаем на оригинал
в MyHook: находим сущность по указателю, чекаем новое значение нетвара, пишем в нашем менеджере что сущность видна/ не видна

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

видим тут кучу хрефов, ищем их в client.dll, и видим как и что нам нужно делать. просто копипастим кароче с asm в с++

находим по строке какую-нибудь функцию с ссылки выше(ищем без мени класса, то есть не Particles.CreateParticle, а ищем просто CreateParticle), а потом ищем вокруг всякие указатели, если видим там какието символы из v8(GetCurrentIsolate или че как там эта фигня называется), если видим там какието указатели с RTTI с явно говорящими именами(CDOTA_ParticleManager например), то поздравляю вы нашли функцию. либо статик анализ(напрягаем глазки и всматриваемся), либо ставите бп и подрубаете джаваскриптовый шлак через впк файл(в соседнем разделе тема есть по этому поводу), что впринципе должно ускорить процесс и сделать его приятнее для вашего мозга и для ваших глаз.
выглядит как-то так(цвет радиус настраиваемы. можно ваще любой другой партикль запихать вместо обычного кольца. можно вообще вместо партиклей на экране рисовать какую-нибудь иконку или еще какую-нибудь хрень(на панораме например))

фулл код:
Пожалуйста, авторизуйтесь для просмотра ссылки.

естественно там могут быть какието баги/краши/лики и т.д. мб где-то чето недоглядел(хотя у меня вроде все норм работает), код предназначен для учебных/развлекательных целей а не инжектить в пабе и джебашить. ну и разумеется вак словите если в пабе будете с этим дерьмом гонять не отрубив вак. так что все тесты проводим в демке/лобби на вальвовском сервере с ботами или друзьями.(на скрине стокгольм лобби с ботами, от паба практически ничем в техническом плане не отличается разве что вак не дают)
следующий тутор наверно будет по панораме(ability cooldowns или называйте как хотите, всякие итембары и т.д.)
создаем новый проект в визуалке и потихоньку по одной копипастим системы приводя их в приятный на ваш взгляд вид.
если есть вопросы спрашивайте.
коллбек работает на любую сущность(крип герой блаблабла) у которой есть данный нетвар.
ну и да забыл упомянуть, если сущность только что заспавнилась( например -createhero), то коллбек не сработает на нее при спавне. то есть как только она создалась, вы не сможете сказать, видит ее враг или нет. мб у нее нетвар хранится в нормальном виде(после коллбека нетвар m_iTaggedAsVisibleByTeam заполняется бесполезным мусором), если честно не чекал(главное не на локалхосте тестить, там нетвар всегда хранится в нормальном виде). в теории такие ситуации возможны разве что если вы реконнектнетесь в катку а какая-то из сущностей уже будет просвечена, и вы не сможете определить просвечена она или нет так как коллбек не сработал(на практике не тестил, можете почекать сами, либо коллбек все-таки сработает, либо нетвар еще не превратился в мусор). ну кароче мне лень тестить, заходите в лобби(в каком-нибудь стокгольме) с другом(чтобы сервер не лег после вашего лива) подождите пока союзник-бот просветится, ставьте паузу, ливайте, заходите обратно и чекайте значение нетвара/вызывается ли коллбек и можете отписаться сюда о своих наблюдениях.
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
30 Мар 2020
Сообщения
326
Реакции[?]
24
Поинты[?]
12K
какая разница между байтпатчем и вот реверсом полным? Чето мне кажется что байтпатч палится жостко где-то слышал,Нет?
тут два варианта. из скрина очевидно что загружается указатель на функцию и куда-то суется. мы можем либо пойти реверсить и смотреть че куда там эта фигня суется и потом просто заменить там указатель и назначить свой коллбек(как в вмт хуке), либо можем просто забайтпатчить(что дешевле по времени). я выбрал второй вариант потому что нифига не нашел и желания особо там рыться не было.
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
какая разница между байтпатчем и вот реверсом полным? Чето мне кажется что байтпатч палится жостко где-то слышал,Нет?
а вмт хук как будто не палится:D
все и везде палится если захотеть.
благо вальвы особо не парятся. вак вырубаешь и профит.
реверс полный сложнее чем байтпатч обычно. при реверсе может быть все слишком запутано, можешь не в том направлении искать, можно что-то не заметить, может настроение плохое, может еще что, может тупишь, а вот байтпатч взял и сделал за 5 секунд.
коллбек это то что происходит по окончанию какого-то действия.
NetworkVariableChangeCallback это коллбек который вызывается по изменению нетвара.
в общем коллбек - функция которая вызывается в каком-то контексте, например кликнул на что-нибудь или кнопку нажал или еще что
void callback(int param){
std::cout << param << std::endl;
}
void Iterate(){
for(int i = 0; i < 10; i++){
callback(stuff);//ну типа для каждого элемента массива вызываем callback передавая элемент массива как параметр
}
}
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
а конкретно в случае с нетварами в доте там что-то типо такого:
Netvar::Change(int new_value){
this.netvar_value = new_value;//this для наглядности,чтобы видно было что принадлежит к классу
this.GetChangeCallback().call(this,new_value);
}
то есть гдето в нетваре лежит его значение и коллбек его изменения(ну то есть указатель на функцию кароче), и вместо того чтобы изменять значение напрямую(Netvar::netvar_value = 123456), вызывается эта функция Netvar::Change(123456),а внутри этой функции вызывается этот коллбек по указателю с какими-то параметрами например новое значение нетвара какой сущности принадлежит и т.д., наверняка можно переписать указатель на функцию внутри этого нетвара, как в вмт хуке. то есть он будет вызывать коллбек с указателя который в нем лежит, а там лежит левый указатель на наш коллбек где мы делаем грязные делишки. но этот указатель еще надо найти, и возможно он находится в жопе какой-нибудь(а может и ваще на поверхности бог знает) поэтому я не парился и просто забайтпатчил
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
я сижу на х64, поэтому адреса 8 байт. моя длл находится в куче(адрес 0000019D88413500 например)(мануал мап инжект), поэтому чтобы допрыгнуть оттуда из PE(адрес 00007FFB2EA0C4EB например), нужно 8 байт(разница между двумя адресами слишком огромная и в 4 байта не влезет).
следовательно используем две инструкции: mov rax, [absolute 8 byte address]; jmp rax, которые в байтах будут 48 a1 <8 bytes absolute address> ff e0, то есть 12 байт
если кто не понял, то суть такая:
под PE я имел ввиду "регион куда суются большинство PE(.exe/.dll) файлов".
в этот регион суются не все дллки, но большинство, если их загружать традиционными способами.
в этом регионе находится client.dll, в этом регионе будет находиться и ваша длл если вы ее заинжектите через LoadLibrary(стандартный инжект).
в таком случае вы спокойно можете прыгать с client.dll на вашу длл потому что обе длл находятся в этом регионе, и разница между ними поместится в int
например только что заинжектил стандартным способом дллку, и client.base - my_dll.base у меня 0xd770000. то есть спокойно могу прыгать по относительному адресу.(e9 <4 byte rel>)
но я предпочитаю Manual Map(дллка в таком случае находится в куче а не в регионе PE), поэтому расстояние от client.dll которая находится в PE регионе(например адрес 00007FFB2EA0C4EB) до моей длл которая находится на 0000019D88413500 уже не помещается в 4 байта, поэтому прыгнуть на 4 байта я просто не могу. следовательно используем прыжок на абсолютный(8 байт) адрес.
 
practice makes perfect
Пользователь
Статус
Оффлайн
Регистрация
16 Мар 2019
Сообщения
89
Реакции[?]
68
Поинты[?]
20K
Получилось гуд. Что такое иксрэф
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
Получилось гуд. Что такое иксрэф
eXternal REFerence, в широком смысле это любой call lea или что еще. например когда одна функция вызывает другую. в конкретно данном гайде xref употреблялся как референс на строку, а точнее инструкция lea <register>, [указатель на какую-либо ASCII строку]. ну то есть
const char* my_stuff = "abcdefg";
void My_Stuff(){
printf("%s\n",my_stuff);
//псевдо-asm:
//lea rdx, [my_stuff] - хреф на my_stuff, то есть на строку "abcdefg"
//lea rcx, [temp_stroka] - хреф на "%s\n"
//call printf - хреф на printf
}
в IDA например можно посмотреть эти хрефы, я навел на my_stuff, нажал "показать хрефы" и я увижу в списке функцию My_Stuff и еще функции другие которые как-то используют эту фигню. точно так же на printf наведу и там найду свою функцию и другие.
точно так же и в x64dbg можно посмотреть хрефы, в частности мы ищем хрефы к строке "OnTeamVisibilityChanged"(другими словами, ищем инструкции, которые загружают в регистр указатель на котором находится это сочетание ASCII символов).
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
сига обновилась с оффсетом.
C++:
    u64 ParticlePat = FindPattern("client.dll",
        "48 89 5c 24 ? 57 48 83 ec ? 33 c0 41 8b d8 48 8b fa 41 83 f9 ff 74 ? 48 8b 05 ? ? ? ? 48 85 c0 74 ? 48 63 48 ? 44 8b 90",
        "ParticleManager not found!"
    );
    DPMP = GetAbsoluteAddress(ParticlePat + 0x74, 3, 7);
//было 48 89 5c 24 ? 57 48 83 ec ? 33 c0 41 8b d8 48 8b fa 41 83 f9 ? 74 ? 7c ? 48 8b 0d ? ? ? ? и +0x35
(заходим в старую базу в ида находим там нерабочую сигу смотрим хреф к функции видим там вокруг хрефы CreateParticle и pParticleName находим их в новой базе и ищем там, находим там указатель на нужную нам функцию переходим по нему и делаем там новую сигу и находим новый оффсет - то есть чекаем там каждый mov/lea где есть RTTI с названием CDOTA_ParticleManager(юзаем какой-нибудь reclass или чето типо такого))
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
27 Май 2020
Сообщения
15
Реакции[?]
2
Поинты[?]
0
Все гайды хорошие, спасибо большое, даже не для чайников, а просто как помощь на начальном этапе кто не реверсил игру.
Появилось желание пореверсить доту.

А какую функцию ты хукаешь вот тут ?
Panorama2Hooks->Hook(6, hkRunFrame);
И есть ли функция в client.dll, которую можно хукать как OnUpdate.
ну тоесть функция, которая вызывается не на каждую отрисовку, а на каждое изменение состояния мира после получения данных от сервера.
Короче каждый тик сервера. А не каждый кадр.

Нашел следующие функции
CLoopModeGame::PostDataUpdate ( )
CBaseEntity::OnDataUpdate()
И еще там другие с похожими именами.
Но они слишком часто вызываются, много чаще чем 30 раз в секунду (дефолтный апдейтрейт в доте максимальный 40).

Или мб это нужно в engine.dll смотреть
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
Все гайды хорошие, спасибо большое, даже не для чайников, а просто как помощь на начальном этапе кто не реверсил игру.
Появилось желание пореверсить доту.

А какую функцию ты хукаешь вот тут ?
Panorama2Hooks->Hook(6, hkRunFrame);
И есть ли функция в client.dll, которую можно хукать как OnUpdate.
ну тоесть функция, которая вызывается не на каждую отрисовку, а на каждое изменение состояния мира после получения данных от сервера.
Короче каждый тик сервера. А не каждый кадр.

Нашел следующие функции
CLoopModeGame::PostDataUpdate ( )
CBaseEntity::OnDataUpdate()
И еще там другие с похожими именами.
Но они слишком часто вызываются, много чаще чем 30 раз в секунду (дефолтный апдейтрейт в доте максимальный 40).

Или мб это нужно в engine.dll смотреть
хукаю RunFrame, это типо функция которая каждый кадр вызывается. в этой функции конкретно происходит апдейт интерфейса панорамы. можно хукать любую другую которая вызывается каждый кадр. я просто в dylib зашел ввел слово "frame" и выбрал ту функцию которая понравилась:DD
в теории каждый тик сервера который ты получаешь на клиент это
Пожалуйста, авторизуйтесь для просмотра ссылки.
-> пакет с именем CNETMsg_Tick(не тестил но судя по названию должно быть оно), но смысл? еще есть framestagenotify, там тоже какието postupdate чето там можешь чекнуть там на lwss чето есть по этому поводу.
 
Начинающий
Статус
Оффлайн
Регистрация
27 Май 2020
Сообщения
15
Реакции[?]
2
Поинты[?]
0
хукаю RunFrame, это типо функция которая каждый кадр вызывается. в этой функции конкретно происходит апдейт интерфейса панорамы. можно хукать любую другую которая вызывается каждый кадр. я просто в dylib зашел ввел слово "frame" и выбрал ту функцию которая понравилась:DD
в теории каждый тик сервера который ты получаешь на клиент это
Пожалуйста, авторизуйтесь для просмотра ссылки.
-> пакет с именем CNETMsg_Tick(не тестил но судя по названию должно быть оно), но смысл? еще есть framestagenotify, там тоже какието postupdate чето там можешь чекнуть там на lwss чето есть по этому поводу.
> но смысл ?
Ну типо не хочется класть логику которая основана на Состоянии игры (позиции хп мана кулдауны ) в каждую итерацию отрисовки. Это бесполезно и фпс сильно просадит. Вычисления будут по несколько раз над неизменными данными.
PostRecievedNetMessage это только когда пришел пакет, состояние еще скорее всего не обновилось. Возможно даже десериализации еще не было.
Вот framestagenotify уже лучше подходит. Я тестил ее раньше, она 30 раз ровно за секунду игрового времени вызывается. ЕДинственное она еще и в меню, даже не заходя в матч вызывается. Но пока что наверно лучшая из всех
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
> но смысл ?
Ну типо не хочется класть логику которая основана на Состоянии игры (позиции хп мана кулдауны ) в каждую итерацию отрисовки. Это бесполезно и фпс сильно просадит. Вычисления будут по несколько раз над неизменными данными.
PostRecievedNetMessage это только когда пришел пакет, состояние еще скорее всего не обновилось. Возможно даже десериализации еще не было.
Вот framestagenotify уже лучше подходит. Я тестил ее раньше, она 30 раз ровно за секунду игрового времени вызывается. ЕДинственное она еще и в меню, даже не заходя в матч вызывается. Но пока что наверно лучшая из всех
инструкции за наносекунды исполняются, все твои вычисления хп маны и прочей фигни это сущие пустяки. то же самое и отрисовка 4 картинок абилок.
1.голая дота
60 frames: 86.7 fps, avg time:11.53ms abs_range:16.51ms, min_time:8.96ms, max_time:16.51ms
240 frames: 85.1 fps, avg time:11.75ms, abs_range:7.36ms, min_time:8.96ms, max_time:18.89ms
1000 frames: 78.6 fps, avg time:12.72ms, total frames: 4360

2.абилки на панораме с с++(криво сделал слегка:D )
60 frames: 87.1 fps, avg time:11.48ms abs_range:15.52ms, min_time:9.33ms, max_time:15.52ms
240 frames: 86.3 fps, avg time:11.59ms, abs_range:6.35ms, min_time:9.26ms, max_time:17.82ms
1000 frames: 89.1 fps, avg time:11.23ms, total frames: 3074

3. абилки на панораме на PanoramaScript(типо джаваскриптовый интерфейс от вальвов но панорама там убогая донельзя в плане фпс)
(собственно поэтому PanoramaScript и есть говно)
60 frames: 60.9 fps, avg time:16.42ms abs_range:21.20ms, min_time:13.27ms, max_time:21.20ms
240 frames: 61.8 fps, avg time:16.19ms, abs_range:5.92ms, min_time:13.24ms, max_time:22.35ms
1000 frames: 62.8 fps, avg time:15.93ms, total frames: 2593

как видишь разницы ваще никакой практически нет; весь интерфейс в доте сделан на панораме. всяких картинок на интерфейсе дофига и дота все равно стабильно у всех работает в 60 фпс. от того что ты добавишь на интерфейс еще парочку своих картинок ничего не изменится.
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
27 Май 2020
Сообщения
15
Реакции[?]
2
Поинты[?]
0
инструкции за наносекунды исполняются, все твои вычисления хп маны и прочей фигни это сущие пустяки. то же самое и отрисовка 4 картинок абилок.
1.голая дота
60 frames: 86.7 fps, avg time:11.53ms abs_range:16.51ms, min_time:8.96ms, max_time:16.51ms
240 frames: 85.1 fps, avg time:11.75ms, abs_range:7.36ms, min_time:8.96ms, max_time:18.89ms
1000 frames: 78.6 fps, avg time:12.72ms, total frames: 4360

2.абилки на панораме с с++(криво сделал слегка:D )
60 frames: 87.1 fps, avg time:11.48ms abs_range:15.52ms, min_time:9.33ms, max_time:15.52ms
240 frames: 86.3 fps, avg time:11.59ms, abs_range:6.35ms, min_time:9.26ms, max_time:17.82ms
1000 frames: 89.1 fps, avg time:11.23ms, total frames: 3074

3. абилки на панораме на PanoramaScript(типо джаваскриптовый интерфейс от вальвов но панорама там убогая донельзя в плане фпс)
(собственно поэтому PanoramaScript и есть говно)
60 frames: 60.9 fps, avg time:16.42ms abs_range:21.20ms, min_time:13.27ms, max_time:21.20ms
240 frames: 61.8 fps, avg time:16.19ms, abs_range:5.92ms, min_time:13.24ms, max_time:22.35ms
1000 frames: 62.8 fps, avg time:15.93ms, total frames: 2593

как видишь разницы ваще никакой практически нет; весь интерфейс в доте сделан на панораме. всяких картинок на интерфейсе дофига и дота все равно стабильно у всех работает в 60 фпс. от того что ты добавишь на интерфейс еще парочку своих картинок ничего не изменится.
C панорамой да, я согласен, разницы нет. Я про какие то более серьезные вычисления.
Вот даже рекомендация есть в апи одного из читов.
Пожалуйста, авторизуйтесь для просмотра ссылки.

И даже при такой рекомендации сильные падения фпс у них при минимальном кол-ве скриптов.
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
C панорамой да, я согласен, разницы нет. Я про какие то более серьезные вычисления.
Вот даже рекомендация есть в апи одного из читов.
Пожалуйста, авторизуйтесь для просмотра ссылки.

И даже при такой рекомендации сильные падения фпс у них при минимальном кол-ве скриптов.
ну если ты собрался на джаваскрипте писать то возможно есть смысл. на с++ я не вижу смысла париться о вычислениях, особенно если они не сложные(вся твоя логика в доте это вектора синусы.../арксинусы... и все. ты же не матрицы и факториалы там перемножаешь по 500 штук за кадр)
 
Начинающий
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
42
Реакции[?]
3
Поинты[?]
0
ну если ты собрался на джаваскрипте писать то возможно есть смысл. на с++ я не вижу смысла париться о вычислениях, особенно если они не сложные(вся твоя логика в доте это вектора синусы.../арксинусы... и все. ты же не матрицы и факториалы там перемножаешь по 500 штук за кадр)
можешь плиз гайд по дебаггеру сделать, желательно на этом же гайде, как ты отыскиваешь нужные тебе офсеты функции, допустим ты знаешь что в классе c_dota_hero_blabla есть эта функция вбе, как ее поймать, если в чит энжине можно с этим классом манипулировать, смотреть на количество функции, то в дбг64 я вообще ничего не вижу, даже не знаю за что ухватиться. как вызвать функцию вбе, если ты знаешь только, что она в данном классе, и реакцию инструкций видишь в нужное время. не рассматиривать же в ручную все поинтеры.
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
можешь плиз гайд по дебаггеру сделать, желательно на этом же гайде, как ты отыскиваешь нужные тебе офсеты функции, допустим ты знаешь что в классе c_dota_hero_blabla есть эта функция вбе, как ее поймать, если в чит энжине можно с этим классом манипулировать, смотреть на количество функции, то в дбг64 я вообще ничего не вижу, даже не знаю за что ухватиться. как вызвать функцию вбе, если ты знаешь только, что она в данном классе, и реакцию инструкций видишь в нужное время. не рассматиривать же в ручную все поинтеры.
ок без проблем скоро будет
 
Начинающий
Статус
Оффлайн
Регистрация
12 Сен 2020
Сообщения
42
Реакции[?]
3
Поинты[?]
0
а то что это адрес указателя на функцию вбе (в начале гайда) это догадка или как это определить. почему мы не взяли самый первый адрес с названием функции onteamvisibilitychange? и еще 48 a1 <8 bytes absolute address> ff e0, откуда эти коды в байтах, это произвольно или стандарт какой-то для замены функции.
 
Последнее редактирование:
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
а то что это адрес указателя на функцию вбе (в начале гайда) это догадка или как это определить. почему мы не взяли самый первый адрес с названием функции onteamvisibilitychange? и еще 48 a1 <8 bytes absolute address> ff e0, откуда эти коды в байтах, это произвольно или стандарт какой-то для замены функции.
1)
адрес функции вбе это впринципе да, догадка построенная на во-первых визуальном подтверждении(я на скрине мышкой навел на адрес как видишь по этому адресу находятся байты инструкций а не просто какая-то билиберда) + поставил брейкпоинт на эту функцию(функция которая находится по третьему указателю) и посмотрел что действительно вызывается когда изменяется видимость(ну пошел под вышку вражескую, эта вышка тебя увидела - функция вызвалась) + символ в дилибе есть RegisterNetvarChangeCallback или как его там. и он принимает указатель на функцию параметром. так что среди параметров должен быть указатель на функцию. первый указатель - на строку, второй тоже на строку(там сбоку не видно но там строка вроде "C_DOTA_BaseNPC"), третий - как раз на байты инструкций, а это и есть функция.
2)
это две инструкции
mov rax, qword ptr: [абсолютный 8 байт адрес]//48 a1 <8 bytes>
jmp rax//ff e0
но закодированные в байтах.
mov обычно бывает на 4 байта, то есть относительный адрес(к текущему нужно прибавить относительный адрес чтобы получить абсолютный адрес), но мне его не хватило поэтому юзаю mov на 8 байт абсолютный, конкретно данные байты 48 a1 <8 bytes> и ff e0 нашел гдето в гугле. если ты через LoadLibrary инжектишь то тебе mov на 4 байта хватит(читай выше в этом треде ответ про "PE регион"), я же мануал мапом инжекчу, то есть дллка моя сидит в куче, поэтому от кучи до PE региона в 4 байта не допрыгнуть и поэтому я юзаю 8 байт. чтобы получить байты на 4 байтовый mov rax гугли либо ищи где-нибудь в самой доте, mov встречается на каждом шагу
 
Сверху Снизу