Undetected Dota 2 External Patcher

Пользователь
Статус
Оффлайн
Регистрация
8 Апр 2022
Сообщения
673
Реакции[?]
106
Поинты[?]
69K
если кому интересно - патч на показ ролей в рейтинге
сига - "48 8D 4D ? 41 8B 84 24" + 0x4(просто некст инструкция)

ориг - "41 8B 84 24 74 01 00 00"
патч - "B8 01 00 00 00 90 90 90"
пока лень добавлять в софт просто чето копался и нашел эт
 
Начинающий
Статус
Оффлайн
Регистрация
11 Фев 2024
Сообщения
7
Реакции[?]
0
Поинты[?]
0
если кому интересно - патч на показ ролей в рейтинге
сига - "48 8D 4D ? 41 8B 84 24" + 0x4(просто некст инструкция)

ориг - "41 8B 84 24 74 01 00 00"
патч - "B8 01 00 00 00 90 90 90"
пока лень добавлять в софт просто чето копался и нашел эт
I find the following with CE and "48 8D 4D ? 41 8B 84 24" + 0x4. Am I wrong, cause it shows "41 8B 84 24 80" ?
 

Вложения

Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
Начинающий
Статус
Оффлайн
Регистрация
11 Фев 2024
Сообщения
7
Реакции[?]
0
Поинты[?]
0
you're searching in the wrong place. the result you found is within D3DCOMPILER_47.dll, it should be within client.dll(the original poster forgot to specify that)
Got it, thank you for helping. :BlessRNG:

Edit: Doesn't do anything though. At least nothing is visible during draft for me. The AOB is definitely the correct one though.
 
Последнее редактирование:
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
можно спектатор стат панель(и итем панель) включить в игре
у врагов правда не показывает голду и крипстат
cs.PNG
gold.PNG
invv.PNG
Код:
client.dll - sha1: eb6e94fee71285b2aef15a5a14f640bb413a8e9e timestamp: 1719937884(16:31:24 02 Jul 2024)
spectator game stats show(mostly useless, only shows data for teammates(enemy data isn't sent to the client, it would appear), but necessary for CDOTA_Hud_SpectatorItems thing since the button to show items is on the spectator stats panel)
(can't update while local team is DOTA_TEAM_NONE(0)(player still loading) or will crash)
    (client.dll: xref "CDOTA_Hud_SpectatorGameStats::EventHudUpdate")
        0F 84 ?? ?? ?? ?? 8B CD E8 ?? ?? ?? ?? 83 F8 01(@ $+62)  client.dll
            $-16A50          | 48:895C24 08             | mov qword ptr ss:[rsp+8],rbx
            $-16A4B          | 57                       | push rdi
            $-16A4A          | 48:83EC 30               | sub rsp,30
            $-16A46          | 48:8BF9                  | mov rdi,rcx
            $-16A43          | 0F297424 20              | movaps xmmword ptr ss:[rsp+20],xmm6
            $-16A3E          | 48:8D0D 170C8101         | lea rcx,qword ptr ds:[7FF8382291E0]                                    | 00007FF8382291E0:"CDOTA_Hud_SpectatorGameStats::EventHudUpdate"
            $-16A37          | 48:8D15 50612502         | lea rdx,qword ptr ds:[7FF838C6E720]
            $-16A30          | FF15 52557000            | call qword ptr ds:[<&public: static void (__cdecl * __cdecl VProfScope
            $-16A2A          | 48:8BD8                  | mov rbx,rax
            $-16A27          | FF15 994E7000            | call qword ptr ds:[<&Plat_FloatTime>]
            $-16A21          | 0F57F6                   | xorps xmm6,xmm6
            $-16A1E          | F2:0F5AF0                | cvtsd2ss xmm6,xmm0
            $-16A1A          | F3:0F1047 50             | movss xmm0,dword ptr ds:[rdi+50]
            $-16A15          | F3:0F5805 91347500       | addss xmm0,dword ptr ds:[7FF83716BA84]
            $-16A0D          | 0F2FF0                   | comiss xmm6,xmm0
            $-16A0A          | 76 15                    | jbe client.7FF836A1860D
            $-16A08          | 48:8BCF                  | mov rcx,rdi
            $-16A05          | E8 D0930000              | call client.7FF836A219D0
            $-16A00          | 48:8BCF                  | mov rcx,rdi
            $-169FD          | E8 F8690100              | call client.7FF836A2F000                                               <---- the actual update impl
            $-169F8          | F3:0F1177 50             | movss dword ptr ds:[rdi+50],xmm6
            $-169F3          | FFD3                     | call rbx
            $-169F1          | 48:8B5C24 40             | mov rbx,qword ptr ss:[rsp+40]
            $-169EC          | 32C0                     | xor al,al
            $-169EA          | 0F287424 20              | movaps xmm6,xmmword ptr ss:[rsp+20]
            $-169E5          | 48:83C4 30               | add rsp,30
            $-169E1          | 5F                       | pop rdi
            $-169E0          | C3                       | ret

                actual update impl:
                $ ==>            | 40:53                    | push rbx
                $+2              | 55                       | push rbp
                $+3              | 56                       | push rsi
                $+4              | 57                       | push rdi
                $+5              | 41:55                    | push r13
                $+7              | 48:83EC 50               | sub rsp,50
                $+B              | 4C:8BE9                  | mov r13,rcx
                $+E              | E8 0DD921FF              | call client.7FF835C4C920
                $+13             | 33ED                     | xor ebp,ebp
                $+15             | 48:8D3D C4F52302         | lea rdi,qword ptr ds:[7FF838C6E5E0]
                $+1C             | 8BDD                     | mov ebx,ebp
                $+1E             | 8BF0                     | mov esi,eax
                $+20             | 83FB 0D                  | cmp ebx,D
                $+23             | 73 2B                    | jae client.7FF836A2F050
                $+25             | 48:8B17                  | mov rdx,qword ptr ds:[rdi]
                $+28             | 48:8D8C24 80000000       | lea rcx,qword ptr ss:[rsp+80]
                $+30             | E8 DBCE4000              | call client.7FF836E3BF10
                $+35             | 49:8B4D 08               | mov rcx,qword ptr ds:[r13+8]
                $+39             | 3BF3                     | cmp esi,ebx
                $+3B             | 0FB79424 80000000        | movzx edx,word ptr ss:[rsp+80]
                $+43             | 41:0F94C0                | sete r8b
                $+47             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
                $+4A             | FF90 C8040000            | call qword ptr ds:[rax+4C8]
                $+50             | FFC3                     | inc ebx
                $+52             | 48:83C7 08               | add rdi,8
                $+56             | 83FB 0C                  | cmp ebx,C
                $+59             | 7C C5                    | jl client.7FF836A2F020
                $+5B             | 48:392D D6EB6402         | cmp qword ptr ds:[7FF83907DC38],rbp
                $+62             | 0F84 80000000            | je client.7FF836A2F0E8
                $+68             | 8BCD                     | mov ecx,ebp
                $+6A             | E8 41F6EFFE              | call client.7FF83592E6B0          <---- get local team
                $+6F             | 83F8 01                  | cmp eax,1                         <---- check if spectator(1)
                $+72             | 75 74                    | jne client.7FF836A2F0E8           <---- if jump taken, hide spectator stats
                $+74             | E8 57DD21FF              | call client.7FF835C4CDD0
                $+79             | 48:85C0                  | test rax,rax
                $+7C             | 74 27                    | je client.7FF836A2F0A5
                $+7E             | E8 4DDD21FF              | call client.7FF835C4CDD0
                $+83             | 48:85C0                  | test rax,rax
                $+86             | 74 1D                    | je client.7FF836A2F0A5
                $+88             | E8 43DD21FF              | call client.7FF835C4CDD0


                    replacement:
                    $+6A             | E8 41F6EFFE              | call client.7FF83592E6B0     <---- get local team
                    $+6F             | 84 C0                    | test al,al                   <---- check DOTA_TEAM_NONE(0)
                    $+71             | 90                       | nop
                    $+72             | 74 74                    | je client.7FF836A2F0E8       <---- jump if DOTA_TEAM_NONE(0)


client.dll - sha1: eb6e94fee71285b2aef15a5a14f640bb413a8e9e timestamp: 1719937884(16:31:24 02 Jul 2024)
spectator game items show
(can't update while local team is DOTA_TEAM_NONE(0)(player still loading) or will crash)
    (client.dll: xref "CDOTA_Hud_SpectatorGameStats::EventHudUpdate")
        E8 ?? ?? ?? ?? 41 ?? ?? ?? ?? ?? 83 F8 01(@ $+2E)  client.dll
            $ ==>            | 48:894C24 08             | mov qword ptr ss:[rsp+8],rcx
            $+5              | 53                       | push rbx
            $+6              | 41:54                    | push r12
            $+8              | 41:56                    | push r14
            $+A              | 48:83EC 70               | sub rsp,70
            $+E              | 4C:8BE1                  | mov r12,rcx
            $+11             | 48:8D15 905C2402         | lea rdx,qword ptr ds:[7FF838C6E788]
            $+18             | 48:8D0D 31138001         | lea rcx,qword ptr ds:[7FF838229E30]                                    | 00007FF838229E30:"CDOTA_Hud_SpectatorItems::Update"
            $+1F             | FF15 23506F00            | call qword ptr ds:[<&public: static void (__cdecl * __cdecl VProfScope
            $+25             | 45:33F6                  | xor r14d,r14d
            $+28             | 48:8BD8                  | mov rbx,rax
            $+2B             | 41:8BCE                  | mov ecx,r14d
            $+2E             | E8 9D5BF0FE              | call client.7FF83592E6B0                 <---- get local team
            $+33             | 41:0FB65424 28           | movzx edx,byte ptr ds:[r12+28]
            $+39             | 83F8 01                  | cmp eax,1                                <---- check if spectator(1)
            $+3C             | 74 1E                    | je client.7FF836A28B3C                   <---- if jump taken, update spectator items panel
            $+3E             | 84D2                     | test dl,dl
            $+40             | 0F84 D3030000            | je client.7FF836A28EF9
            $+46             | 49:8BCC                  | mov rcx,r12
            $+49             | E8 222BFFFF              | call client.7FF836A1B650
            $+4E             | FFD3                     | call rbx
            $+50             | B0 01                    | mov al,1
            $+52             | 48:83C4 70               | add rsp,70
            $+56             | 41:5E                    | pop r14
            $+58             | 41:5C                    | pop r12
            $+5A             | 5B                       | pop rbx
            $+5B             | C3                       | ret


                replacement:
                $+2E             | E8 9D5BF0FE              | call client.7FF83592E6B0            <---- get local team
                $+33             | 41:0FB65424 28           | movzx edx,byte ptr ds:[r12+28]
                $+39             | 84 C0                    | test al,al                          <---- check DOTA_TEAM_NONE(0)
                $+3B             | 90                       | nop
                $+3C             | 75 1E                    | jne client.7FF836A28B3C             <---- jump if not DOTA_TEAM_NONE(0)
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
патч на показ стоимостей всех путей в краунфолле если кому нужен(чтобы знать наперед какие токены надо/не надо фармить)(эти стоимости индивидуальные для аккаунта, прилетают с сервера; не меняются)
1720769462017.png
Код:
crownfall show required tokens for all paths
    (client.dll: xref "PathTokensOwned" somewhat after xref "PathActive")
        45 33 F6 40 84 FF 75 ?? 49(@ $ ==>)  client.dll
            $-18E            | E8 25CFCBFF              | call client.7FFB6BE13700
            $-189            | 84C0                     | test al,al
            $-187            | 40:0FB6FF                | movzx edi,dil
            $-183            | 0F44FB                   | cmove edi,ebx
            $-180            | 49:8B5D 10               | mov rbx,qword ptr ds:[r13+10]
            $-17C            | 48:8D15 29540D02         | lea rdx,qword ptr ds:[7FFB6E22BC18]                | 00007FFB6E22BC18:"Completing"
            $-175            | 48:8D8D 40010000         | lea rcx,qword ptr ss:[rbp+140]
            $-16E            | E8 956AE700              | call client.7FFB6CFCD290
            $-169            | 48:8B4B 08               | mov rcx,qword ptr ds:[rbx+8]
            $-165            | 45:33C0                  | xor r8d,r8d
            $-162            | 0FB795 40010000          | movzx edx,word ptr ss:[rbp+140]
            $-15B            | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $-158            | FF90 C8040000            | call qword ptr ds:[rax+4C8]
            $-152            | 49:8B5D 10               | mov rbx,qword ptr ds:[r13+10]
            $-14E            | 48:8D15 535F0D02         | lea rdx,qword ptr ds:[7FFB6E22C770]                | 00007FFB6E22C770:"PathActive"
            $-147            | 48:8D8D 40010000         | lea rcx,qword ptr ss:[rbp+140]
            $-140            | E8 676AE700              | call client.7FFB6CFCD290
            $-13B            | 48:8B4B 08               | mov rcx,qword ptr ds:[rbx+8]
            $-137            | 45:0FB6C6                | movzx r8d,r14b
            $-133            | 0FB795 40010000          | movzx edx,word ptr ss:[rbp+140]
            $-12C            | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $-129            | FF90 C8040000            | call qword ptr ds:[rax+4C8]
            $-123            | 49:8B4D 10               | mov rcx,qword ptr ds:[r13+10]
            $-11F            | 83FE 02                  | cmp esi,2
            $-11C            | 74 0C                    | je client.7FFB6C156856
            $-11A            | 41:807C24 5F 00          | cmp byte ptr ds:[r12+5F],0
            $-114            | 75 04                    | jne client.7FFB6C156856
            $-112            | 32D2                     | xor dl,dl
            ...
            $ ==>            | 45:33F6                  | xor r14d,r14d
            $+3              | 40:84FF                  | test dil,dil
            $+6              | 75 5B                    | jne client.7FFB6C1569C7                            <---- if jump taken, show tokens for this path
            $+8              | 49:8B45 18               | mov rax,qword ptr ds:[r13+18]
            $+C              | 48:8B48 08               | mov rcx,qword ptr ds:[rax+8]
            $+10             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $+13             | FF90 B0010000            | call qword ptr ds:[rax+1B0]
            $+19             | 49:8B45 18               | mov rax,qword ptr ds:[r13+18]
            $+1D             | 41:0FB65424 5F           | movzx edx,byte ptr ds:[r12+5F]
            $+23             | 48:8B48 08               | mov rcx,qword ptr ds:[rax+8]
            $+27             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $+2A             | FF90 18060000            | call qword ptr ds:[rax+618]
            $+30             | 49:8B5D 10               | mov rbx,qword ptr ds:[r13+10]
            $+34             | 48:8D15 E15D0D02         | lea rdx,qword ptr ds:[7FFB6E22C780]                | 00007FFB6E22C780:"PathTokensOwned"
            $+3B             | 48:8D8D 40010000         | lea rcx,qword ptr ss:[rbp+140]
            $+42             | E8 E568E700              | call client.7FFB6CFCD290
            $+47             | 48:8B4B 08               | mov rcx,qword ptr ds:[rbx+8]
            $+4B             | 45:33C0                  | xor r8d,r8d
            $+4E             | 0FB795 40010000          | movzx edx,word ptr ss:[rbp+140]
            $+55             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $+58             | FF90 C8040000            | call qword ptr ds:[rax+4C8]
            $+5E             | E9 29050000              | jmp client.7FFB6C156EF0
            $+63             | 4D:85F6                  | test r14,r14
            $+66             | 0F84 20050000            | je client.7FFB6C156EF0

                            
                replacement:
                $ ==>            | 45:33F6                  | xor r14d,r14d
                $+3              | 40:84FF                  | test dil,dil
                $+6              | EB 5B                    | jmp client.7FFB6C1569C7           <---- always jump
 
Начинающий
Статус
Оффлайн
Регистрация
1 Янв 2019
Сообщения
13
Реакции[?]
1
Поинты[?]
2K
патч на показ стоимостей всех путей в краунфолле если кому нужен(чтобы знать наперед какие токены надо/не надо фармить)(эти стоимости индивидуальные для аккаунта, прилетают с сервера; не меняются)
Посмотреть вложение 280986
Код:
crownfall show required tokens for all paths
    (client.dll: xref "PathTokensOwned" somewhat after xref "PathActive")
        45 33 F6 40 84 FF 75 ?? 49(@ $ ==>)  client.dll
            $-18E            | E8 25CFCBFF              | call client.7FFB6BE13700
            $-189            | 84C0                     | test al,al
            $-187            | 40:0FB6FF                | movzx edi,dil
            $-183            | 0F44FB                   | cmove edi,ebx
            $-180            | 49:8B5D 10               | mov rbx,qword ptr ds:[r13+10]
            $-17C            | 48:8D15 29540D02         | lea rdx,qword ptr ds:[7FFB6E22BC18]                | 00007FFB6E22BC18:"Completing"
            $-175            | 48:8D8D 40010000         | lea rcx,qword ptr ss:[rbp+140]
            $-16E            | E8 956AE700              | call client.7FFB6CFCD290
            $-169            | 48:8B4B 08               | mov rcx,qword ptr ds:[rbx+8]
            $-165            | 45:33C0                  | xor r8d,r8d
            $-162            | 0FB795 40010000          | movzx edx,word ptr ss:[rbp+140]
            $-15B            | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $-158            | FF90 C8040000            | call qword ptr ds:[rax+4C8]
            $-152            | 49:8B5D 10               | mov rbx,qword ptr ds:[r13+10]
            $-14E            | 48:8D15 535F0D02         | lea rdx,qword ptr ds:[7FFB6E22C770]                | 00007FFB6E22C770:"PathActive"
            $-147            | 48:8D8D 40010000         | lea rcx,qword ptr ss:[rbp+140]
            $-140            | E8 676AE700              | call client.7FFB6CFCD290
            $-13B            | 48:8B4B 08               | mov rcx,qword ptr ds:[rbx+8]
            $-137            | 45:0FB6C6                | movzx r8d,r14b
            $-133            | 0FB795 40010000          | movzx edx,word ptr ss:[rbp+140]
            $-12C            | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $-129            | FF90 C8040000            | call qword ptr ds:[rax+4C8]
            $-123            | 49:8B4D 10               | mov rcx,qword ptr ds:[r13+10]
            $-11F            | 83FE 02                  | cmp esi,2
            $-11C            | 74 0C                    | je client.7FFB6C156856
            $-11A            | 41:807C24 5F 00          | cmp byte ptr ds:[r12+5F],0
            $-114            | 75 04                    | jne client.7FFB6C156856
            $-112            | 32D2                     | xor dl,dl
            ...
            $ ==>            | 45:33F6                  | xor r14d,r14d
            $+3              | 40:84FF                  | test dil,dil
            $+6              | 75 5B                    | jne client.7FFB6C1569C7                            <---- if jump taken, show tokens for this path
            $+8              | 49:8B45 18               | mov rax,qword ptr ds:[r13+18]
            $+C              | 48:8B48 08               | mov rcx,qword ptr ds:[rax+8]
            $+10             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $+13             | FF90 B0010000            | call qword ptr ds:[rax+1B0]
            $+19             | 49:8B45 18               | mov rax,qword ptr ds:[r13+18]
            $+1D             | 41:0FB65424 5F           | movzx edx,byte ptr ds:[r12+5F]
            $+23             | 48:8B48 08               | mov rcx,qword ptr ds:[rax+8]
            $+27             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $+2A             | FF90 18060000            | call qword ptr ds:[rax+618]
            $+30             | 49:8B5D 10               | mov rbx,qword ptr ds:[r13+10]
            $+34             | 48:8D15 E15D0D02         | lea rdx,qword ptr ds:[7FFB6E22C780]                | 00007FFB6E22C780:"PathTokensOwned"
            $+3B             | 48:8D8D 40010000         | lea rcx,qword ptr ss:[rbp+140]
            $+42             | E8 E568E700              | call client.7FFB6CFCD290
            $+47             | 48:8B4B 08               | mov rcx,qword ptr ds:[rbx+8]
            $+4B             | 45:33C0                  | xor r8d,r8d
            $+4E             | 0FB795 40010000          | movzx edx,word ptr ss:[rbp+140]
            $+55             | 48:8B01                  | mov rax,qword ptr ds:[rcx]
            $+58             | FF90 C8040000            | call qword ptr ds:[rax+4C8]
            $+5E             | E9 29050000              | jmp client.7FFB6C156EF0
            $+63             | 4D:85F6                  | test r14,r14
            $+66             | 0F84 20050000            | je client.7FFB6C156EF0

                          
                replacement:
                $ ==>            | 45:33F6                  | xor r14d,r14d
                $+3              | 40:84FF                  | test dil,dil
                $+6              | EB 5B                    | jmp client.7FFB6C1569C7           <---- always jump
а ты можешь обновить патчер, а то автор немного занят?
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
а ты можешь обновить патчер, а то автор немного занят?
возьми визуалку и свой напиши.
Код:
client.dll - sha1: 2f17fb6b520ed76bea98c33cf7c2a99f8d944600 timestamp: 1717201500(00:25:00 01 Jun 2024)
camera distance(from modifying cvar dota_camera_distance and observing in CE what changes as a result(fl_CameraDistance does))
    EB ?? E8 ?? ?? ?? ?? F3 0F 10 ?? 48 8D 0D(@ $ ==>)  client.dll
        $ ==>            | EB 05                    | jmp client.7FF9B2E01788           | EB ?? E8 ?? ?? ?? ?? F3 0F 10 ?? 48 8D 0D
        $+2              | E8 C8A2FCFF              | call client.7FF9B2DCBA50          <------- call GetCameraDistance
        $+7              | F3:0F1038                | movss xmm7,dword ptr ds:[rax]
        $+B              | 48:8D0D 1D664B03         | lea rcx,qword ptr ds:[7FF9B62B7DB0]

            GetCameraDistance:
            $ ==>            | 48:83EC 28               | sub rsp,28
            $+4              | 6548:8B0425 58000000     | mov rax,qword ptr gs:[58]
            $+D              | 8B0D 3DCF7403            | mov ecx,dword ptr ds:[7FF96E9C6770]
            $+13             | BA 38000000              | mov edx,38
            $+18             | 48:8B0CC8                | mov rcx,qword ptr ds:[rax+rcx*8]
            $+1C             | 8B040A                   | mov eax,dword ptr ds:[rdx+rcx]
            $+1F             | 3905 BB8F6003            | cmp dword ptr ds:[7FF96E882800],eax
            $+25             | 7F 0C                    | jg client.7FF96B279853
            $+27             | 48:8D05 9A8F6003         | lea rax,qword ptr ds:[7FF9B62BBC28]    <-------- fl_CameraDistance
            $+2E             | 48:83C4 28               | add rsp,28
            $+32             | C3                       | ret

particles.dll - sha1: e221ecea54a3520357c2c8f7cf46e1db2d2656f9 timestamp: 1717025579(23:32:59 29 May 2024)
particle fow: (particles.dll: xref "Error in child list of particle system %s [%p], parent: %p\n", target fn CParticleCollection::SetRenderingEnabled)
    80 A6 ?? ?? ?? ?? ?? 41 0F B6 ?? 4C 8B 76 ?? C0 E0 07(@ $ ==>)  particles.dll
        $ ==>            | 80A6 4C0A0000 7F         | and byte ptr ds:[rsi+A4C],7F          | 80 A6 ?? ?? ?? ?? ?? 41 0F B6 ?? 4C 8B 76 ?? C0 E0 07
        $+7              | 41:0FB6C4                | movzx eax,r12b
        $+B              | 4C:8B76 20               | mov r14,qword ptr ds:[rsi+20]
        $+F              | C0E0 07                  | shl al,7                              <------- al(yyyy yyyX) shifted by 7 bits, result(X000 0000) = 0(0000 0000) or 0x80(1000 0000)
        $+12             | 0886 4C0A0000            | or byte ptr ds:[rsi+A4C],al           <------- adds above result to particle flags

            replacement:
            $+F              | 808E 4C0A0000 80         | or byte ptr ds:[rsi+A4C],80           <------- always add 0x80 to flags
            $+16             | 90                       | nop
            $+17             | 90                       | nop

engine2.dll - sha1: 8ac410b63a431da7751ff2c1fc053f17ecb7311c timestamp: 1717025664(23:34:24 29 May 2024)
disable fog(HWBP from cvar fog_enable's value)
    48 8B 40 ?? 80 38 ?? 74 ?? B0 ?? EB(@ +$D0C)    engine2.dll
        $ ==>            | 48:895424 10             | mov qword ptr ss:[rsp+10],rdx
        $+5              | 55                       | push rbp
        $+6              | 53                       | push rbx
        $+7              | 56                       | push rsi
        $+8              | 57                       | push rdi
        $+9              | 41:54                    | push r12
        $+B              | 41:55                    | push r13
        ...
        $+CF4            | 48:8D0D FD233D00         | lea rcx,qword ptr ds:[7FF9D6D7F2E8]
        $+CFB            | E8 F0741800              | call engine2.7FF9D6B343E0
        $+D00            | 48:85C0                  | test rax,rax
        $+D03            | 75 0B                    | jne engine2.7FF9D69ACF00
        $+D05            | 48:8B05 F4233D00         | mov rax,qword ptr ds:[7FF9D6D7F2F0]
        $+D0C            | 48:8B40 08               | mov rax,qword ptr ds:[rax+8]              | 48 8B 40 ?? 80 38 ?? 74 ?? B0 ?? EB
        $+D10            | 8038 00                  | cmp byte ptr ds:[rax],0
        $+D13            | 74 04                    | je engine2.7FF9D69ACF09                   <------- if jump taken, fog disabled
        $+D15            | B0 01                    | mov al,1
        $+D17            | EB 02                    | jmp engine2.7FF9D69ACF0B
        $+D19            | 32C0                     | xor al,al
        $+D1B            | 8845 FC                  | mov byte ptr ss:[rbp-4],al
            replacement:
            $+D10            | 8038 00                  | cmp byte ptr ds:[rax],0
            $+D13            | EB 04                    | jmp engine2.7FF9D69ACF09              <------- always jump(disable fog)
            $+D15            | B0 01                    | mov al,1
            $+D17            | EB 02                    | jmp engine2.7FF9D69ACF0B
            $+D19            | 32C0                     | xor al,al

client.dll - sha1: 2f17fb6b520ed76bea98c33cf7c2a99f8d944600 timestamp: 1717201500(00:25:00 01 Jun 2024)
new player pick all heroes(client.dll: xref "new_player_enable")
(actually just forcibly allows picking all heroes, breaks single draft)
    44 39 ?? 0F 84 ?? ?? ?? ?? 48 FF ?? 48 83 C0 ?? 48 3B ?? 7C(@ $+2A0)    client.dll
        $ ==>            | 48:895C24 08             | mov qword ptr ss:[rsp+8],rbx
        $+5              | 48:896C24 10             | mov qword ptr ss:[rsp+10],rbp
        $+A              | 48:897424 18             | mov qword ptr ss:[rsp+18],rsi
        $+F              | 48:897C24 20             | mov qword ptr ss:[rsp+20],rdi
        ...
        $+2A0            | 44:3908                  | cmp dword ptr ds:[rax],r9d            | 44 39 ?? 0F 84 ?? ?? ?? ?? 48 FF ?? 48 83 C0 ?? 48 3B ?? 7C
        $+2A3            | 0F84 93FEFFFF            | je client.7FF9B293C65C
        $+2A9            | 48:FFC1                  | inc rcx
        $+2AC            | 48:83C0 04               | add rax,4
        $+2B0            | 48:3BCA                  | cmp rcx,rdx
        $+2B3            | 7C EB                    | jl client.7FF9B293C7C0
        $+2B5            | B0 01                    | mov al,1                                   <-------- 1 = can't pick
        $+2B7            | E9 7AFFFFFF              | jmp client.7FF9B293C756(epilogue)
        
            replacement:
            $+2B5            | 32C0                     | xor al,al                                  <-------- 0 = can pick
            $+2B7            | E9 7AFFFFFF              | jmp client.7FF9B293C756(epilogue)
(+ то что я выше скидывал)
либо на крайняк это говно юзай(на свой страх и риск естественно(.text/.rdata модификации детектятся))
C++:
//shitcode
//c++23
#ifdef _MSC_VER
#pragma warning(disable: \
        4710  /* 'function' : function not inlined */ \
        4711  /* function 'name' selected for automatic inline expansion */ \
        4820  /* 'type': 'number' bytes padding added after type 'type' */ \
        5246  /* 'member': the initialization of a subobject should be wrapped in braces */ \
        5045  /* Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified */ \
    )
#endif

#ifdef __clang__
#pragma clang diagnostic ignored "-Wc++98-compat"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wpre-c++17-compat"
#pragma clang diagnostic ignored "-Wpre-c++20-compat-pedantic"
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wctad-maybe-unsupported"
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#endif

#include <iostream>
#include <vector>
#include <string>
#include <span>
#include <array>
#include <optional>
#include <expected>
#include <Windows.h>
#include <Psapi.h>
#include <TlHelp32.h>

bool fuzzy_memcmp
(
    const std::uint8_t* lhs,
    const std::uint8_t* rhs,
    std::size_t size,
    const std::uint8_t* masks
) noexcept
{
    constexpr auto wildcard = static_cast<std::uint8_t>('?');
    const auto end = lhs + size;
    for (; lhs < end; ++lhs, ++rhs, ++masks)
    {
        if (*masks != wildcard && *lhs != *rhs)
            return false;
    }
    return true;
}

const std::uint8_t* sigscan_naive(const std::uint8_t* base, std::size_t input_size, const uint8_t* pattern,
    std::size_t pattern_size, const std::uint8_t* masks) noexcept
{
    if (
        pattern_size
        && (input_size >= pattern_size)
        && base
        && pattern
        && masks
        )
    {
        const auto alignmentCount = (input_size - pattern_size) + 1;
        const auto end = base + alignmentCount;
        for (auto current = base; current < end; ++current)
        {
            if (fuzzy_memcmp(current, pattern, pattern_size, masks))
                return current;
        }
    }

    return nullptr;
}

std::vector<std::string_view> strsplit(std::string_view input, std::string_view token)
{
    std::vector<std::string_view> result{};
    const auto token_size = token.size();
    std::size_t pos{};
    while ((pos = input.find(token)) != std::string_view::npos)
    {
        result.push_back(input.substr(0, pos));
        input = input.substr(pos + token_size);
    }
    result.push_back(input);
    return result;
}

constexpr std::uint8_t get_hex_char(std::uint8_t x)
{
    if (x >= '0' && x <= '9')
        return static_cast<std::uint8_t>(x - '0');
    else if (x >= 'A' && x <= 'F')
        return static_cast<std::uint8_t>(x - 'A' + 0xA);
    else if (x >= 'a' && x <= 'f')
        return static_cast<std::uint8_t>(x - 'a' + 0xA);
    return (std::numeric_limits<std::uint8_t>::max)();
}

constexpr std::uint8_t get_hex_chars(std::uint8_t char1, std::uint8_t char2)
{
    return static_cast<std::uint8_t>((get_hex_char(char1) << 4) | get_hex_char(char2));
}

std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>
preprocess_patern_fuzzy(std::string_view textsig)
{
    std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>> result{};
    if (!textsig.empty())
    {
        result.first.reserve(textsig.size());
        result.second.reserve(textsig.size());
        for (const auto& chunk : strsplit(textsig, " "))
        {
            if (chunk.size() == 2)
            {
                if (chunk.front() == '?')
                {
                    result.first.push_back('?');
                    result.second.push_back('?');
                }
                else
                {
                    const auto high = get_hex_char(static_cast<std::uint8_t>(chunk.front()));
                    const auto low = get_hex_char(static_cast<std::uint8_t>(chunk.back()));
                    if (high == (std::numeric_limits<std::uint8_t>::max)()
                        || low == (std::numeric_limits<std::uint8_t>::max)())
                        return {};
                    result.first.push_back(static_cast<std::uint8_t>((high << 4) | low));
                    result.second.push_back('x');
                }
            }
            else
                return {};
        }
    }
    return result;
}

std::span<std::uint8_t> GetModuleExternal(HANDLE hProcess, std::string_view moduleName) noexcept
{
    std::array<HMODULE, 1024> hMods{};
    DWORD out_size;
    if (K32EnumProcessModules(hProcess, hMods.data(), sizeof(hMods), &out_size))
    {
        for (const auto& mod : std::span{ hMods.data(), out_size / sizeof(HMODULE) })
        {
            std::array<char, MAX_PATH> szModName;
            if (K32GetModuleBaseNameA(hProcess, mod, szModName.data(),
                static_cast<DWORD>(szModName.size())))
            {
                if (std::string_view{ szModName.data() } == moduleName)
                {
                    MODULEINFO out_modinfo;
                    if (K32GetModuleInformation(hProcess, mod, &out_modinfo, sizeof(MODULEINFO)))
                    {
                        return
                        {
                            static_cast<std::uint8_t*>(out_modinfo.lpBaseOfDll),
                            out_modinfo.SizeOfImage
                        };
                    }
                }
            }
        }
    }

    return {};
}

std::vector<std::uint8_t> ReadModule(HANDLE hProcess, std::span<std::uint8_t> mod)
{
    std::vector<std::uint8_t> result{};
    result.resize(mod.size());
    SIZE_T out_size;
    if (ReadProcessMemory(hProcess, mod.data(), result.data(), mod.size(), &out_size)
        && out_size == mod.size())
    {
        return result;
    }
    return {};
}

constexpr auto sig_not_found_ = (std::numeric_limits<std::ptrdiff_t>::max)();
std::ptrdiff_t ExternalSigscanGetRVA
(
    const std::uint8_t* base,
    std::size_t input_size,
    std::string_view textsig
)
{
    const auto& [bytes, masks] = preprocess_patern_fuzzy(textsig);
    if (!bytes.empty() && bytes.size() == masks.size())
    {
        const auto result =
            sigscan_naive(base, input_size, bytes.data(), bytes.size(), masks.data());
        if (result)
        {
            return result - base;
        }
    }
    return sig_not_found_;
}

std::uint8_t* ExternalSigscan
(
    HANDLE hProcess,
    std::string_view moduleName,
    std::string_view textsig,
    std::string_view signame
)
{
    const auto extmod = GetModuleExternal(hProcess, moduleName);
    if (extmod.empty())
        throw std::runtime_error{ std::format("module {} not found!", moduleName) };
    const auto& intmod = ReadModule(hProcess, extmod);
    if (intmod.size() != extmod.size())
        throw std::runtime_error{ std::format("can't map module {}!", moduleName) };

    if (const auto rva = ExternalSigscanGetRVA(intmod.data(), intmod.size(), textsig);
        rva != sig_not_found_)
    {
        return extmod.data() + rva;
    }
    std::string errmsg{ signame };
    errmsg += " not found!";
    throw std::runtime_error{ errmsg };
}


template<HANDLE invalid_value>
struct handle_raii
{
    HANDLE handle = invalid_value;
    bool IsValid() const noexcept
    {
        return handle != invalid_value;
    }
    explicit handle_raii(HANDLE handle_) : handle{ handle_ } {}
    handle_raii(const handle_raii&) = delete;
    handle_raii& operator=(const handle_raii&) = delete;
    handle_raii(handle_raii&& other) noexcept
        : handle{ other.handle }
    {
        other.handle = invalid_value;
    }
    handle_raii& operator=(handle_raii&& other) noexcept
    {
        std::destroy_at(this);
        std::construct_at(this, std::move(other));
        return *this;
    }
    ~handle_raii()
    {
        if (IsValid())
            CloseHandle(handle);
    }
    operator HANDLE() const noexcept
    {
        return handle;
    }
};

constexpr DWORD invalid_pid_ = (std::numeric_limits<DWORD>::max)();
DWORD GetProcessID(const std::string_view& processName) noexcept
{
    using TProcHandle = handle_raii<(std::numeric_limits<HANDLE>::max)()>;
    if (const TProcHandle hSnap{ CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL) };
        hSnap.IsValid())
    {
#pragma push_macro("PROCESSENTRY32")
#pragma push_macro("Process32First")
#pragma push_macro("Process32Next")
#undef PROCESSENTRY32
#undef Process32First
#undef Process32Next

        PROCESSENTRY32 entry{ .dwSize = sizeof(PROCESSENTRY32) };

        for (auto result = Process32First(hSnap, &entry); result; result = Process32Next(hSnap, &entry))
            if (std::string_view{ reinterpret_cast<const char*>(entry.szExeFile) } == processName)
                return entry.th32ProcessID;

#pragma pop_macro("Process32Next")
#pragma pop_macro("Process32First")
#pragma pop_macro("PROCESSENTRY32")
    }
    return invalid_pid_;
}

template<class T>
std::expected<T, DWORD> ReadMemory(HANDLE process, const void* ptr) noexcept
{
    if (process != nullptr)
    {
        constexpr auto expected_size = sizeof(T);
        T obj{};
        SIZE_T out_size{};
        if (!ReadProcessMemory(process, ptr, &obj, expected_size, &out_size))
            return std::unexpected{ GetLastError() };

        if (out_size != expected_size)
            return std::unexpected{ ERROR_MORE_DATA };

        return { obj };
    }
    return std::unexpected{ ERROR_INVALID_HANDLE };
}

void WriteMemory(HANDLE process, void* ptr, std::span<const std::uint8_t> input)
{
    if (process == nullptr)
        throw std::runtime_error{ "WriteMemory: invalid process handle" };

    SIZE_T out_size{};
    if (!WriteProcessMemory(process, ptr, input.data(), input.size(), &out_size))
        throw std::runtime_error{ std::format("WriteMemory: error code {}!", GetLastError()) };

    if (out_size != input.size())
        throw std::runtime_error{ "WriteMemory: couldn't write everything!" };
}

template<std::endian endian, class T>
constexpr std::array<std::uint8_t, sizeof(T)> serialize_word(T word) noexcept
{
    std::array<std::uint8_t, sizeof(T)> result{};
    for (std::size_t i = 0; i < sizeof(T); ++i)
    {
        const auto shift_ = CHAR_BIT * ((sizeof(T) - 1) - i);
        const auto byte_ = static_cast<std::uint8_t>((word >> shift_) & 0xFF);
        if constexpr (endian == std::endian::big)
        {
            result[i] = byte_;
        }
        else
        {
            result[(sizeof(T) - 1) - i] = byte_;
        }
    }
    return result;
}

/*  WriteProcessMemory already unprotects executable sections(undocumented)
    though maybe not on all windows versions */
class UnProtect
{
    DWORD old{};
    HANDLE process_{};
    MEMORY_BASIC_INFORMATION mbi{};
public:
    UnProtect(HANDLE process, const void* addr) : process_{ process }
    {
        if (VirtualQueryEx(process, addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION)))
        {
            if (!VirtualProtectEx(process, mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &old))
                throw std::runtime_error{ std::format("VirtualProtectEx: {}", GetLastError()) };
        }
        else
            throw std::runtime_error{ std::format("VirtualQueryEx: {}", GetLastError()) };
    }
    UnProtect(const UnProtect&) = delete;
    UnProtect& operator=(const UnProtect&) = delete;
    UnProtect& operator=(UnProtect&&) noexcept = delete;
    UnProtect(UnProtect&&) noexcept = delete;
    ~UnProtect()
    {
        DWORD _{};
        VirtualProtectEx(process_, mbi.BaseAddress, mbi.RegionSize, old, &_);
    }
};

void particle_fow_stuff(HANDLE process)
{
    const auto insn = ExternalSigscan(process, "particles.dll",
        "80 A6 ?? ?? ?? ?? ?? 41 0F B6 ?? 4C 8B 76 ?? C0 E0 07", "particle_fow_stuff");
    const auto flags_offset
        = ReadMemory<std::array<std::uint8_t, 4>>(process, insn + 0x12 + 2).value();
    auto patch = std::to_array<std::uint8_t>({ 0x80, 0x8E, '?', '?', '?', '?', 0x80, 0x90, 0x90 });
    std::ranges::copy(flags_offset, patch.data() + 2);
    WriteMemory(process, insn + 0xF, patch);
}

void fog_stuff(HANDLE process)
{
    const auto insn = ExternalSigscan(process, "engine2.dll",
        "48 8B 40 ?? 80 38 ?? 74 ?? B0 ?? EB", "fog_stuff");
    WriteMemory(process, insn + 0x7, std::to_array<std::uint8_t>({ 0xEB }));
}
void new_player_allow_pick_all_heroes_stuff(HANDLE process)
{
    const auto insn = ExternalSigscan(process, "client.dll",
        "44 39 ?? 0F 84 ?? ?? ?? ?? 48 FF ?? 48 83 C0 ?? 48 3B ?? 7C",
        "new_player_allow_pick_all_heroes_stuff");
    WriteMemory(process, insn + 0x15, std::to_array<std::uint8_t>({ 0x32, 0xC0 }));
}

void cooldown_stuff(HANDLE process)
{
    const auto insn = ExternalSigscan(process, "client.dll",
        "48 0F 45 ?? ?? 8D ?? ?? ?? ?? ?? 48 8B ?? FF 50 ?? 84 C0 0F 84", "cooldown_stuff");
    const auto jump_offset = ReadMemory<int>(process, insn + 0x13 + 2).value();
    auto patch = std::to_array<std::uint8_t>({ 0xE9, '?', '?', '?', '?', 0x90 });
    std::ranges::copy(serialize_word<std::endian::little>(jump_offset + 1), patch.data() + 1);
    WriteMemory(process, insn + 0x13, patch);
}

void level_show_stuff(HANDLE process)
{
    const auto show_level_insn = ExternalSigscan(process, "client.dll",
        "49 8B CC FF ?? ?? 84 C0 75 ?? 44", "level_show_stuff");
    WriteMemory(process, show_level_insn + 0x8, std::to_array<std::uint8_t>({ 0x90, 0x90 }));

    const auto colorize_insn = ExternalSigscan(process, "client.dll",
        "49 8B CC FF ?? ?? 41 BE", "level_colorize_stuff");
    WriteMemory(process, colorize_insn + 0xE, std::to_array<std::uint8_t>({ 0xEB }));
}

void manacost_stuff(HANDLE process)
{
    const auto insn = ExternalSigscan(process, "client.dll", "40 0F 9F ?? 41 80 BE", "manacost_stuff");
    WriteMemory(process, insn + 0x1D, std::to_array<std::uint8_t>({ 0xEB }));
}

void hidden_buffs_stuff(HANDLE process)
{
    const auto part_1_insn = ExternalSigscan(process, "client.dll",
        "48 8B CB FF 90 ?? ?? ?? ?? 84 C0 74 ?? 48 8B ?? 48 8B ?? FF 90 ?? ?? ?? ?? 84 C0 0F 84 ?? ?? ?? ?? 41 BF",
        "hidden_buffs_part_1_stuff");
    WriteMemory(process, part_1_insn + 0xB, std::to_array<std::uint8_t>({ 0xEB }));

    const auto part_2_insn = ExternalSigscan(process, "client.dll",
        "48 8B 10 48 8B C8 FF 92 ?? ?? ?? ?? 84 C0 74 ?? 48 8B 0D ?? ?? ?? ?? 83 B9",
        "hidden_buffs_part_2_stuff");
    WriteMemory(process, part_2_insn + 0xE, std::to_array<std::uint8_t>({ 0xEB }));
}

void camera_distance_stuff(HANDLE process, float value)
{
    const auto insn = ExternalSigscan(process, "client.dll",
        "EB ?? E8 ?? ?? ?? ?? F3 0F 10 ?? 48 8D 0D", "camera_distance_stuff");
    const auto call_insn = insn + 0x2;
    const auto call_rel32
        = ReadMemory<int>(process, call_insn + 1).value();
    const auto fl_CameraDistance_lea_insn
        = (call_insn + call_rel32 + 5) + 0x27;
    const auto fl_CameraDistance_rel32
        = ReadMemory<int>(process, fl_CameraDistance_lea_insn + 3).value();
    const auto p_fl_CameraDistance = fl_CameraDistance_lea_insn + 7 + fl_CameraDistance_rel32;
    std::cout << "p_fl_CameraDistance: " << static_cast<void*>(p_fl_CameraDistance) << std::endl;
    WriteMemory(process, p_fl_CameraDistance, serialize_word<std::endian::little>
        (std::bit_cast<std::uint32_t>(value)));
}

void r_farz_stuff(HANDLE process, float value)
{
    const auto insn = ExternalSigscan(process, "client.dll",
        "F3 0F 10 ?? ?? ?? ?? ?? 48 8B ?? 48 8B ?? ?? ?? ?? ?? 48 8B ?? FF ?? 0F 2E", "r_farz_stuff");
    const auto fl_r_farz_rel32
        = ReadMemory<int>(process, insn + 4).value();
    const auto p_fl_r_farz = (insn + fl_r_farz_rel32 + 8);
    std::cout << "p_fl_r_farz: " << static_cast<void*>(p_fl_r_farz) << std::endl;
    UnProtect _{ process, p_fl_r_farz };
    WriteMemory(process, p_fl_r_farz, serialize_word<std::endian::little>
        (std::bit_cast<std::uint32_t>(value)));
}

void crownfall_show_all(HANDLE process)
{
    const auto crownfall_show_all_insn = ExternalSigscan(process, "client.dll",
        "45 33 F6 40 84 FF 75 ?? 49", "crownfall_show_all_insn");
    WriteMemory(process, crownfall_show_all_insn + 0x6, std::to_array<std::uint8_t>({ 0xEB }));
}

void spectator_stats(HANDLE process)
{
    const auto spectator_stats_insn = ExternalSigscan(process, "client.dll",
        "0F 84 ?? ?? ?? ?? 8B CD E8 ?? ?? ?? ?? 83 F8 01", "spectator_stats");
    WriteMemory(process, spectator_stats_insn + 0xD, std::to_array<std::uint8_t>({ 0x84, 0xC0, 0x90, 0x74 }));
}

void spectator_items(HANDLE process)
{
    const auto spectator_items_insn = ExternalSigscan(process, "client.dll",
        "E8 ?? ?? ?? ?? 41 ?? ?? ?? ?? ?? 83 F8 01", "spectator_items");
    WriteMemory(process, spectator_items_insn + 0xB, std::to_array<std::uint8_t>({ 0x84, 0xC0, 0x90, 0x75 }));
}

int main()
{
    try
    {
        const auto pid = GetProcessID("dota2.exe");
        if (pid == invalid_pid_)
            throw std::runtime_error{ "No dota2.exe" };
        const handle_raii < HANDLE{ nullptr } > process
        {
            OpenProcess(PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, pid)
        };
        if (!process.IsValid())
            throw std::runtime_error{ "Can't open process" };

        camera_distance_stuff(process, 2000.0f);  // .data
        r_farz_stuff(process, 15432.0f);  // .rdata

        particle_fow_stuff(process);  // .text
        fog_stuff(process);  // .text
        //new_player_allow_pick_all_heroes_stuff(process); // .text
        cooldown_stuff(process);  // .text
        level_show_stuff(process);  // .text
        manacost_stuff(process);  // .text
        hidden_buffs_stuff(process);  // .text
        crownfall_show_all(process);  // .text
        spectator_stats(process);  // .text
        spectator_items(process);  // .text
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what() << std::endl;
    }


    return 0;
}
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
так а вроде же все модельки иллюзий по дефолту желтые/синие их просто для врагов перекрашивает нет?
ну я думаю изменением текст секции как в отображении тп можно сделать?(прошу камнями не бросаться я не сильно шарю)
я думал что можно запатчить проверку на m_iteamnum(чтоб дота думала что это тимейт),но чето не нашел вообще где на тиму проверяется при рисовке


спасибо даже чето не подумал про это,добавлю как будет не лень)
подсветка иллюзий обусловлена партиклей у баффа CDOTA_Modifier_Illusion(modifier_illusion)
у врагов нет баффа(баффы с сервака приходят) modifier_illusion и как следствие партикли и как следствие не окрашивается.
микро-хпбар(полосочка простая как на скрине у иллюзий) контроллируется C_DOTA_BaseNPC::m_bHasClientSeenIllusionModifier(у врагов он 0 если неизвестно что это иллюзия(ну бывают всякие иллюзии террорблейда и тд у них известно)) + модифаерфункцией MODIFIER_PROPERTY_IS_ILLUSION(у врагов ее нет если неизвестно что это иллюзия). патчить чеки тут не вариант ибо они не проверяют на иллюзию а просто проверяют известно ли что это иллюзия(если пропатчить то все будет считаться иллюзией включая настоящих героев).
если пиздец как захотеть можно мини-интернал написать с нужным функционалом и байтпатч хуками(724 байта у меня получилось) и по кодкейвам(кучки 0xCC(int3) байт в .text секции(обычно для выравнивания функции до 16-байтной границы)) раскидать(их в client.dll есть несколько штук на сотни байт; хотя вопрос всегда ли они будут существовать) из патчера
это уже не совсем традиционный патчинг будет но технически все еще патчинг ))
на скрине PoC(m_nUnitState64 |= MODIFIER_STATE_SPECIALLY_DENIABLE для вбе выбран(он для союзных иллюзий/не-героев(варды крипы и тд) не создает партикль восклицательного знака по дефолту но можно пропатчить чтобы создавал))
логика основная в хуке npc_think(это та функция которая вбе затирает), еще есть хук OnUnitStateChanged чтобы при изменении состояния юнита не слетал мой MODIFIER_STATE_SPECIALLY_DENIABLE(у меня просто логика вбе токо при изменении нетвара(до затирания. я его не патчу) происходит а не каждый кадр; ну можно было бы конечно просто затирание вбе пропатчить или что-то в этом духе и каждый кадр этот MODIFIER_STATE_SPECIALLY_DENIABLE ставить но мне похуй)
1720946932816.png
C++:
struct npc_think_data
{
    std::uint32_t m_iTeamNum;
    std::uint32_t m_iHeroID;
    std::uint32_t m_hReplicatingOtherHeroModel;
    std::uint32_t m_clrRender;
    std::uint32_t m_bHasClientSeenIllusionModifier;
    std::uint32_t m_iTaggedAsVisibleByTeam;
    std::uint32_t m_nUnitState64;
    std::uint32_t m_iUnitType;
    std::uint32_t m_hMyWearables;

    color_32 illusion_color{};

    std::uint64_t unit_state_flag{};

    const C_BaseEntity** LocalPlayerAddress;

    void(*OnColorChanged)(C_BaseEntity*);
    C_BaseEntity*(*GetEntityByIndex)(std::uint32_t index);

    std::uint8_t(*entity_get_team)(const C_BaseEntity*);
    std::uint8_t(*get_local_team)();
    bool(*is_good_hero)(const C_BaseEntity*);
    void(*illusion_think)(C_BaseEntity*);
    void(*vbe_think)(C_BaseEntity*);

    void(*npc_think_hook)(C_BaseEntity*, int unk);
    void(*npc_think_original)(C_BaseEntity*, int unk);

    void(*OnUnitStateChanged_hook)(C_BaseEntity*, C_BaseEntity*, std::uint64_t* new_value);
    void(*OnUnitStateChanged_original)(C_BaseEntity*, C_BaseEntity*, std::uint64_t* new_value);
};

npc_think_data* think_data_placeholder{};
extern "C"
{
    /*
    48 8B 05 ?? ?? ?? ?? 8B 00 0F B6 04 08 C3
    */
    std::uint8_t entity_get_team(const C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;
        return entity->Member<std::uint8_t>(data.m_iTeamNum);
    }
    auto assemble_entity_get_team(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x8B, 0x05,
            '?', '?', '?', '?',
            0x8B, 0x00, 0x0F, 0xB6, 0x04, 0x08, 0xC3
        });
        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto entity_get_team_code_size = std::tuple_size_v<decltype(assemble_entity_get_team(nullptr, nullptr))>;

    /*
    48 8B 15 ?? ?? ?? ?? 48 8B 42 30 48 8B 08 48 85 C9 74 07 48 8B 42 48 48 FF E0 32 C0 C3
    */
    std::uint8_t get_local_team()
    {
        const auto& data = *think_data_placeholder;
        if (const auto player = *data.LocalPlayerAddress; player)
        {
            return data.entity_get_team(player);
        }
        return 0;
    }
    auto assemble_get_local_team(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x8B, 0x15,
            '?', '?', '?', '?',
            0x48, 0x8B, 0x42, 0x30, 0x48, 0x8B, 0x08, 0x48, 0x85, 0xC9, 0x74, 0x07,
            0x48, 0x8B, 0x42, 0x48, 0x48, 0xFF, 0xE0, 0x32, 0xC0, 0xC3
            });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto get_local_team_code_size = std::tuple_size_v<decltype(assemble_get_local_team(nullptr, nullptr))>;


    /*
    48 8B 05 ?? ?? ?? ?? 8B 50 04 8B 04 0A 83 F8 0A 74 0D 83 F8 52 74 08 83 F8 71 74 03 B0 01 C3 32 C0 C3
    */
    bool is_good_hero(const C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;
        const auto heroID = entity->Member<std::uint32_t>(data.m_iHeroID);

        /* scripts/npc/npc_heroes.txt */
        constexpr auto Morphling = 10;
        constexpr auto Meepo = 82;
        constexpr auto ArcWarden = 113;

        return (heroID != Morphling)
            && (heroID != Meepo)
            && (heroID != ArcWarden);
    }
    auto assemble_is_good_hero(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x8B, 0x05,
            '?', '?', '?', '?',
            0x8B, 0x50, 0x04, 0x8B, 0x04, 0x0A, 0x83, 0xF8, 0x0A, 0x74, 0x0D, 0x83, 0xF8, 0x52, 0x74, 0x08, 0x83, 0xF8, 0x71, 0x74, 0x03, 0xB0, 0x01, 0xC3, 0x32, 0xC0, 0xC3
            });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto is_good_hero_code_size = std::tuple_size_v<decltype(assemble_is_good_hero(nullptr, nullptr))>;


    /*
    48 89 5C 24 18 57 48 83 EC 20 48 8B 3D ?? ?? ?? ?? 48 8B D9 48 8B 47 58 FF D0 84 C0 0F
    84 A6 00 00 00 8B 47 08 83 3C 18 FF 0F 84 99 00 00 00 0F B6 47 26 8B 4F 0C 48 89 74 24
    38 0F B6 77 27 C1 E6 08 0B F0 0F B6 47 25 C1 E6 08 0B F0 0F B6 47 24 C1 E6 08 0B F0 83
    3C 19 FF 75 67 8B 47 10 48 89 6C 24 30 C6 04 18 01 89 34 19 48 8B CB FF 57 38 8B 47 20
    48 03 C3 48 8B 58 08 48 63 00 48 8D 2C 83 48 3B DD 74 37 66 0F 1F 84 00 00 00 00 00 8B
    0B 48 8B 47 40 81 E1 FF 7F 00 00 FF D0 48 85 C0 74 12 8B 4F 0C 83 3C 01 FF 75 09 89 34
    01 48 8B C8 FF 57 38 48 83 C3 04 48 3B DD 75 D2 48 8B 6C 24 30 48 8B 74 24 38 48 8B 5C
    24 40 48 83 C4 20 5F C3
    */
    void illusion_think(C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;
        if (data.is_good_hero(entity))
        {
            if (entity->Member<std::uint32_t>(data.m_hReplicatingOtherHeroModel)
                != std::bit_cast<std::uint32_t>(-1))
            {
                const auto illu_clr = data.illusion_color.get();
                auto& ent_clr
                    = entity->Member<std::uint32_t>(data.m_clrRender);
                if (ent_clr == 0xFFFFFFFF)
                {
                    entity->Member<bool>(data.m_bHasClientSeenIllusionModifier) = true;
                    ent_clr = illu_clr;
                    data.OnColorChanged(entity);
                    const auto& wearable_handles
                        = entity->Member<CUtlVector<std::uint32_t>>(data.m_hMyWearables);
                    for (auto handle : wearable_handles.span())
                    {
                        const auto wearable = data.GetEntityByIndex(handle & 0x7FFF);
                        if (wearable)
                        {
                            auto& wearable_clr
                                = wearable->Member<std::uint32_t>(data.m_clrRender);
                            if (wearable_clr == 0xFFFFFFFF)
                            {
                                wearable_clr = illu_clr;
                                data.OnColorChanged(wearable);
                            }
                        }
                    }
                }
            }
        }
    }
    auto assemble_illusion_think(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /*  0 */ 0x48, 0x89, 0x5C, 0x24, 0x18, 0x57, 0x48, 0x83, 0xEC, 0x20,
            /*  A */ 0x48, 0x8B, 0x3D,
            '?', '?', '?', '?',
            0x48, 0x8B, 0xD9, 0x48, 0x8B, 0x47, 0x58, 0xFF, 0xD0, 0x84, 0xC0, 0x0F, 0x84,
            0xA6, 0x00, 0x00, 0x00, 0x8B, 0x47, 0x08, 0x83, 0x3C, 0x18, 0xFF, 0x0F, 0x84,
            0x99, 0x00, 0x00, 0x00, 0x0F, 0xB6, 0x47, 0x26, 0x8B, 0x4F, 0x0C, 0x48, 0x89,
            0x74, 0x24, 0x38, 0x0F, 0xB6, 0x77, 0x27, 0xC1, 0xE6, 0x08, 0x0B, 0xF0, 0x0F,
            0xB6, 0x47, 0x25, 0xC1, 0xE6, 0x08, 0x0B, 0xF0, 0x0F, 0xB6, 0x47, 0x24, 0xC1,
            0xE6, 0x08, 0x0B, 0xF0, 0x83, 0x3C, 0x19, 0xFF, 0x75, 0x67, 0x8B, 0x47, 0x10,
            0x48, 0x89, 0x6C, 0x24, 0x30, 0xC6, 0x04, 0x18, 0x01, 0x89, 0x34, 0x19, 0x48,
            0x8B, 0xCB, 0xFF, 0x57, 0x38, 0x8B, 0x47, 0x20, 0x48, 0x03, 0xC3, 0x48, 0x8B,
            0x58, 0x08, 0x48, 0x63, 0x00, 0x48, 0x8D, 0x2C, 0x83, 0x48, 0x3B, 0xDD, 0x74,
            0x37, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x0B, 0x48,
            0x8B, 0x47, 0x40, 0x81, 0xE1, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xD0, 0x48, 0x85,
            0xC0, 0x74, 0x12, 0x8B, 0x4F, 0x0C, 0x83, 0x3C, 0x01, 0xFF, 0x75, 0x09, 0x89,
            0x34, 0x01, 0x48, 0x8B, 0xC8, 0xFF, 0x57, 0x38, 0x48, 0x83, 0xC3, 0x04, 0x48,
            0x3B, 0xDD, 0x75, 0xD2, 0x48, 0x8B, 0x6C, 0x24, 0x30, 0x48, 0x8B, 0x74, 0x24,
            0x38, 0x48, 0x8B, 0x5C, 0x24, 0x40, 0x48, 0x83, 0xC4, 0x20, 0x5F, 0xC3
            });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0xA + 7, think_data_ptr)),
            result.data() + 0xA + 3);
        return result;
    }
    constexpr auto illusion_think_code_size = std::tuple_size_v<decltype(assemble_illusion_think(nullptr, nullptr))>;

    /*
    48 83 EC 28 4C 8B 0D ?? ?? ?? ?? 41 8B 41 14 44 8B 04 08 41 F6 C0 10 74 5C 41 8B 41 18
    41 83 E0 0C 49 8B 51 28 4C 8B 14 08 49 8B C2 48 23 C2 41 80 F8 0C 75 21 48 85 C0 75 3B
    4C 0B D2 4C 8D 44 24 30 48 8B D1 4C 89 54 24 30 41 FF 91 88 00 00 00 48 83 C4 28 C3 48
    85 C0 74 1A 48 F7 D2 4C 8D 44 24 30 49 23 D2 48 89 54 24 30 48 8B D1 41 FF 91 88 00 00
    00 48 83 C4 28 C3
    */
    void vbe_think(C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;

        enum DOTATeam_t
        {
            DOTA_TEAM_GOODGUYS = 2,
            DOTA_TEAM_BADGUYS = 3,
            DOTA_TEAM_NEUTRALS = 4,

        };

        constexpr auto both_teams = (1 << DOTA_TEAM_GOODGUYS) | (1 << DOTA_TEAM_BADGUYS);
        const auto vis = entity->Member<std::uint32_t>(data.m_iTaggedAsVisibleByTeam);

        /* 
        Neutrals always see you; if they don't, netvar's value was erased 
        if they do, netvar was just updated. 
        */
        if (vis & (1 << DOTA_TEAM_NEUTRALS))
        {
            const auto flag = data.unit_state_flag;
            auto& state = entity->Member<std::uint64_t>(data.m_nUnitState64);
            /* Visible */
            if ((vis & both_teams) == both_teams)
            {
                /* Wasn't visible */
                if ((state & flag) == 0)
                {
                    std::uint64_t new_state = state | flag;
                    data.OnUnitStateChanged_original(entity, entity, &new_state);
                }
            }
            /* Not visible */
            else
            {
                /* Was visible */
                if ((state & flag) != 0)
                {
                    std::uint64_t new_state = state & (~flag);
                    data.OnUnitStateChanged_original(entity, entity, &new_state);
                }
            }
        }
    }
    auto assemble_vbe_think(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /*  0 */ 0x48, 0x83, 0xEC, 0x28, 
            /*  4 */ 0x4C, 0x8B, 0x0D,
            '?', '?', '?', '?',
            0x41, 0x8B, 0x41, 0x14, 0x44, 0x8B, 0x04, 0x08, 0x41, 0xF6, 0xC0, 0x10, 0x74, 0x5C,
            0x41, 0x8B, 0x41, 0x18, 0x41, 0x83, 0xE0, 0x0C, 0x49, 0x8B, 0x51, 0x28, 0x4C, 0x8B,
            0x14, 0x08, 0x49, 0x8B, 0xC2, 0x48, 0x23, 0xC2, 0x41, 0x80, 0xF8, 0x0C, 0x75, 0x21,
            0x48, 0x85, 0xC0, 0x75, 0x3B, 0x4C, 0x0B, 0xD2, 0x4C, 0x8D, 0x44, 0x24, 0x30, 0x48,
            0x8B, 0xD1, 0x4C, 0x89, 0x54, 0x24, 0x30, 0x41, 0xFF, 0x91, 0x88, 0x00, 0x00, 0x00,
            0x48, 0x83, 0xC4, 0x28, 0xC3, 0x48, 0x85, 0xC0, 0x74, 0x1A, 0x48, 0xF7, 0xD2, 0x4C,
            0x8D, 0x44, 0x24, 0x30, 0x49, 0x23, 0xD2, 0x48, 0x89, 0x54, 0x24, 0x30, 0x48, 0x8B,
            0xD1, 0x41, 0xFF, 0x91, 0x88, 0x00, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x28, 0xC3
        });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x4 + 7, think_data_ptr)),
            result.data() + 0x4 + 3);
        return result;
    }
    constexpr auto vbe_think_code_size = std::tuple_size_v<decltype(assemble_vbe_think(nullptr, nullptr))>;

    /*
    4C 8B 15 ?? ?? ?? ?? 41 8B 42 18 4C 8B 0C 08 4D 23 4A 28 4D 09 08 49 FF A2 88 00 00 00
    */
    void OnUnitStateChanged_hook(C_BaseEntity* entity, C_BaseEntity* entity_, std::uint64_t* new_value)
    {
        const auto& data = *think_data_placeholder;

        const auto old_value = entity->Member<std::uint64_t>(data.m_nUnitState64);
        /* always preserve the flag even when the server changes the state */
        *new_value |= (old_value & data.unit_state_flag);
        return data.OnUnitStateChanged_original(entity, entity_, new_value);
    }
    auto assemble_OnUnitStateChanged_hook(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x4C, 0x8B, 0x15,
            '?', '?', '?', '?',
            0x41, 0x8B, 0x42, 0x18, 0x4C, 0x8B, 0x0C, 0x08, 0x4D, 0x23, 0x4A,
            0x28, 0x4D, 0x09, 0x08, 0x49, 0xFF, 0xA2, 0x88, 0x00, 0x00, 0x00
        });
        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto OnUnitStateChanged_hook_code_size = std::tuple_size_v<decltype(assemble_OnUnitStateChanged_hook(nullptr, nullptr))>;


    /*
    48 89 5C 24 10 48 89 6C 24 18 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 8B EA 48 8B F9 48 85 C9 74 3A
    48 89 74 24 30 FF 53 50 0F B6 F0 84 C0 74 26 4C 8B 43 48 48 8B CF 41 FF D0 40 3A C6 75 08 48 8B
    CF FF 53 68 EB 0F 8B 43 1C 83 3C 38 01 75 06 48 8B CF FF 53 60 48 8B 74 24 30 8B D5 48 8B CF 48
    8B 43 78 48 8B 5C 24 38 48 8B 6C 24 40 48 83 C4 20 5F 48 FF E0
    */
    void npc_think_hook(C_BaseEntity* entity, int unk)
    {
        const auto& data = *think_data_placeholder;
        if (entity)
        {

            const auto local_team = data.get_local_team();
            if (local_team != 0)
            {
                if (data.entity_get_team(entity) == local_team)
                {
                    data.vbe_think(entity);
                }
                else
                {
                    constexpr auto HERO = 1;
                    if (entity->Member<std::uint32_t>(data.m_iUnitType) == HERO)
                    {
                        data.illusion_think(entity);
                    }
                }
            }
        }
        return data.npc_think_original(entity, unk);
    }
    auto assemble_npc_think_hook(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x6C, 0x24, 0x18, 0x57, 0x48, 0x83, 0xEC, 0x20,
            /* F */ 0x48, 0x8B, 0x1D,
            '?', '?', '?', '?',
            0x8B, 0xEA, 0x48, 0x8B, 0xF9, 0x48, 0x85, 0xC9, 0x74, 0x3A, 0x48, 0x89, 0x74, 0x24,
            0x30, 0xFF, 0x53, 0x50, 0x0F, 0xB6, 0xF0, 0x84, 0xC0, 0x74, 0x26, 0x4C, 0x8B, 0x43,
            0x48, 0x48, 0x8B, 0xCF, 0x41, 0xFF, 0xD0, 0x40, 0x3A, 0xC6, 0x75, 0x08, 0x48, 0x8B,
            0xCF, 0xFF, 0x53, 0x68, 0xEB, 0x0F, 0x8B, 0x43, 0x1C, 0x83, 0x3C, 0x38, 0x01, 0x75,
            0x06, 0x48, 0x8B, 0xCF, 0xFF, 0x53, 0x60, 0x48, 0x8B, 0x74, 0x24, 0x30, 0x8B, 0xD5,
            0x48, 0x8B, 0xCF, 0x48, 0x8B, 0x43, 0x78, 0x48, 0x8B, 0x5C, 0x24, 0x38, 0x48, 0x8B,
            0x6C, 0x24, 0x40, 0x48, 0x83, 0xC4, 0x20, 0x5F, 0x48, 0xFF, 0xE0
        });
        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0xF + 7, think_data_ptr)),
            result.data() + 0xF + 3);
        return result;
    }
    constexpr auto npc_think_hook_code_size = std::tuple_size_v<decltype(assemble_npc_think_hook(nullptr, nullptr))>;
}
C++:
//client.dll - sha1: 14d0563945a48113acf6d534fe427b1f9c22b8f3 timestamp: 1720829080(00:04:40 13 Jul 2024)
void hook_npc_think_C(HANDLE process)
{
    const auto client_ext = GetModuleExternal(process, "client.dll");
    if (client_ext.empty())
        throw std::runtime_error{ "client.dll not found!" };
    const auto& client_int = ReadModule(process, client_ext);
    if (client_int.size() != client_ext.size())
        throw std::runtime_error{ "can't map client.dll!" };

    npc_think_data data{};
    data.m_iTeamNum = 0x3C3;
    data.m_iHeroID = 0x1AF0;
    data.m_hReplicatingOtherHeroModel = 0x1878;
    data.m_clrRender = 0x5C0;
    data.m_bHasClientSeenIllusionModifier = 0xADD;
    data.m_iTaggedAsVisibleByTeam = 0xC2C;
    data.m_nUnitState64 = 0x1008;
    data.m_iUnitType = 0xA4C;
    data.m_hMyWearables = 0x988;

    data.illusion_color = { .r = 0, .g = 0, .b = 255, .a = 255 };

    constexpr auto MODIFIER_STATE_SPECIALLY_DENIABLE = 1 << 18;
    data.unit_state_flag = MODIFIER_STATE_SPECIALLY_DENIABLE;

    /* CPrediction + 0x50 */
    data.LocalPlayerAddress = (decltype(data.LocalPlayerAddress))((client_ext.data() + 0x4763920) + 0x50);

    data.OnColorChanged = (decltype(data.OnColorChanged))(client_ext.data() + 0x11C61E0);
    data.GetEntityByIndex = (decltype(data.GetEntityByIndex))(client_ext.data() + 0x1337C20);

    CaveFinder caves{ client_int.data(), client_ext.data(), client_int.size() };
    const auto data_ptr_cave = caves.find(sizeof(void*));
    std::cout << "data_ptr_cave: " << (void*)data_ptr_cave << std::endl;

    UnProtect _{ process, data_ptr_cave };

    const auto data_cave = caves.find(sizeof(npc_think_data));
    std::cout << "data_cave: " << (void*)data_cave << std::endl;
    WriteMemory(process, data_ptr_cave, serialize_word<std::endian::little>((std::uintptr_t)data_cave));

#define emit_fn(fn) \
    const auto fn##_cave = caves.find(fn##_code_size); \
    std::cout << #fn << "_cave: " << (void*)fn##_cave << std::endl; \
    WriteMemory(process, fn##_cave, assemble_##fn(fn##_cave, data_ptr_cave)); \
    data.fn = (decltype(data.fn))fn##_cave

    emit_fn(entity_get_team);
    emit_fn(get_local_team);
    emit_fn(is_good_hero);
    emit_fn(illusion_think);
    emit_fn(vbe_think);
    emit_fn(OnUnitStateChanged_hook);
    emit_fn(npc_think_hook);

    const auto npc_think = client_ext.data() + 0x147C740;
    data.npc_think_original = (decltype(data.npc_think_original))
        caves.bytepatch_hook<5>(process, npc_think, npc_think_hook_cave);

    const auto OnUnitStateChanged = client_ext.data() + 0x1481530;
    data.OnUnitStateChanged_original = (decltype(data.OnUnitStateChanged_original))
        caves.bytepatch_hook<5>(process, OnUnitStateChanged, OnUnitStateChanged_hook_cave);

    WriteMemory(process, data_cave, (std::array<std::uint8_t, sizeof(npc_think_data)>&)data);
}
 
Последнее редактирование:
Пользователь
Статус
Оффлайн
Регистрация
8 Апр 2022
Сообщения
673
Реакции[?]
106
Поинты[?]
69K
если пиздец как захотеть можно мини-интернал написать с нужным функционалом и байтпатч хуками(724 байта у меня получилось) и по кодкейвам(кучки 0xCC(int3) байт в .text секции(обычно для выравнивания функции до 16-байтной границы)) раскидать(их в client.dll есть несколько штук на сотни байт; хотя вопрос всегда ли они будут существовать) из патчера
это уже не совсем традиционный патчинг будет но технически все еще патчинг ))
на скрине PoC(m_nUnitState64 |= MODIFIER_STATE_SPECIALLY_DENIABLE для вбе выбран(он для союзных иллюзий/не-героев(варды крипы и тд) не создает партикль восклицательного знака по дефолту но можно пропатчить чтобы создавал))
логика основная в хуке npc_think(это та функция которая вбе затирает), еще есть хук OnUnitStateChanged чтобы при изменении состояния юнита не слетал мой MODIFIER_STATE_SPECIALLY_DENIABLE(у меня просто логика вбе токо при изменении нетвара(до затирания. я его не патчу) происходит а не каждый кадр; ну можно было бы конечно просто затирание вбе пропатчить или что-то в этом духе и каждый кадр этот MODIFIER_STATE_SPECIALLY_DENIABLE ставить но мне похуй)
Посмотреть вложение 281117
C++:
struct npc_think_data
{
    std::uint32_t m_iTeamNum;
    std::uint32_t m_iHeroID;
    std::uint32_t m_hReplicatingOtherHeroModel;
    std::uint32_t m_clrRender;
    std::uint32_t m_bHasClientSeenIllusionModifier;
    std::uint32_t m_iTaggedAsVisibleByTeam;
    std::uint32_t m_nUnitState64;
    std::uint32_t m_iUnitType;
    std::uint32_t m_hMyWearables;

    color_32 illusion_color{};

    std::uint64_t unit_state_flag{};

    const C_BaseEntity** LocalPlayerAddress;

    void(*OnColorChanged)(C_BaseEntity*);
    C_BaseEntity*(*GetEntityByIndex)(std::uint32_t index);

    std::uint8_t(*entity_get_team)(const C_BaseEntity*);
    std::uint8_t(*get_local_team)();
    bool(*is_good_hero)(const C_BaseEntity*);
    void(*illusion_think)(C_BaseEntity*);
    void(*vbe_think)(C_BaseEntity*);

    void(*npc_think_hook)(C_BaseEntity*, int unk);
    void(*npc_think_original)(C_BaseEntity*, int unk);

    void(*OnUnitStateChanged_hook)(C_BaseEntity*, C_BaseEntity*, std::uint64_t* new_value);
    void(*OnUnitStateChanged_original)(C_BaseEntity*, C_BaseEntity*, std::uint64_t* new_value);
};

npc_think_data* think_data_placeholder{};
extern "C"
{
    /*
    48 8B 05 ?? ?? ?? ?? 8B 00 0F B6 04 08 C3
    */
    std::uint8_t entity_get_team(const C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;
        return entity->Member<std::uint8_t>(data.m_iTeamNum);
    }
    auto assemble_entity_get_team(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x8B, 0x05,
            '?', '?', '?', '?',
            0x8B, 0x00, 0x0F, 0xB6, 0x04, 0x08, 0xC3
        });
        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto entity_get_team_code_size = std::tuple_size_v<decltype(assemble_entity_get_team(nullptr, nullptr))>;

    /*
    48 8B 15 ?? ?? ?? ?? 48 8B 42 30 48 8B 08 48 85 C9 74 07 48 8B 42 48 48 FF E0 32 C0 C3
    */
    std::uint8_t get_local_team()
    {
        const auto& data = *think_data_placeholder;
        if (const auto player = *data.LocalPlayerAddress; player)
        {
            return data.entity_get_team(player);
        }
        return 0;
    }
    auto assemble_get_local_team(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x8B, 0x15,
            '?', '?', '?', '?',
            0x48, 0x8B, 0x42, 0x30, 0x48, 0x8B, 0x08, 0x48, 0x85, 0xC9, 0x74, 0x07,
            0x48, 0x8B, 0x42, 0x48, 0x48, 0xFF, 0xE0, 0x32, 0xC0, 0xC3
            });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto get_local_team_code_size = std::tuple_size_v<decltype(assemble_get_local_team(nullptr, nullptr))>;


    /*
    48 8B 05 ?? ?? ?? ?? 8B 50 04 8B 04 0A 83 F8 0A 74 0D 83 F8 52 74 08 83 F8 71 74 03 B0 01 C3 32 C0 C3
    */
    bool is_good_hero(const C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;
        const auto heroID = entity->Member<std::uint32_t>(data.m_iHeroID);

        /* scripts/npc/npc_heroes.txt */
        constexpr auto Morphling = 10;
        constexpr auto Meepo = 82;
        constexpr auto ArcWarden = 113;

        return (heroID != Morphling)
            && (heroID != Meepo)
            && (heroID != ArcWarden);
    }
    auto assemble_is_good_hero(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x8B, 0x05,
            '?', '?', '?', '?',
            0x8B, 0x50, 0x04, 0x8B, 0x04, 0x0A, 0x83, 0xF8, 0x0A, 0x74, 0x0D, 0x83, 0xF8, 0x52, 0x74, 0x08, 0x83, 0xF8, 0x71, 0x74, 0x03, 0xB0, 0x01, 0xC3, 0x32, 0xC0, 0xC3
            });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto is_good_hero_code_size = std::tuple_size_v<decltype(assemble_is_good_hero(nullptr, nullptr))>;


    /*
    48 89 5C 24 18 57 48 83 EC 20 48 8B 3D ?? ?? ?? ?? 48 8B D9 48 8B 47 58 FF D0 84 C0 0F
    84 A6 00 00 00 8B 47 08 83 3C 18 FF 0F 84 99 00 00 00 0F B6 47 26 8B 4F 0C 48 89 74 24
    38 0F B6 77 27 C1 E6 08 0B F0 0F B6 47 25 C1 E6 08 0B F0 0F B6 47 24 C1 E6 08 0B F0 83
    3C 19 FF 75 67 8B 47 10 48 89 6C 24 30 C6 04 18 01 89 34 19 48 8B CB FF 57 38 8B 47 20
    48 03 C3 48 8B 58 08 48 63 00 48 8D 2C 83 48 3B DD 74 37 66 0F 1F 84 00 00 00 00 00 8B
    0B 48 8B 47 40 81 E1 FF 7F 00 00 FF D0 48 85 C0 74 12 8B 4F 0C 83 3C 01 FF 75 09 89 34
    01 48 8B C8 FF 57 38 48 83 C3 04 48 3B DD 75 D2 48 8B 6C 24 30 48 8B 74 24 38 48 8B 5C
    24 40 48 83 C4 20 5F C3
    */
    void illusion_think(C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;
        if (data.is_good_hero(entity))
        {
            if (entity->Member<std::uint32_t>(data.m_hReplicatingOtherHeroModel)
                != std::bit_cast<std::uint32_t>(-1))
            {
                const auto illu_clr = data.illusion_color.get();
                auto& ent_clr
                    = entity->Member<std::uint32_t>(data.m_clrRender);
                if (ent_clr == 0xFFFFFFFF)
                {
                    entity->Member<bool>(data.m_bHasClientSeenIllusionModifier) = true;
                    ent_clr = illu_clr;
                    data.OnColorChanged(entity);
                    const auto& wearable_handles
                        = entity->Member<CUtlVector<std::uint32_t>>(data.m_hMyWearables);
                    for (auto handle : wearable_handles.span())
                    {
                        const auto wearable = data.GetEntityByIndex(handle & 0x7FFF);
                        if (wearable)
                        {
                            auto& wearable_clr
                                = wearable->Member<std::uint32_t>(data.m_clrRender);
                            if (wearable_clr == 0xFFFFFFFF)
                            {
                                wearable_clr = illu_clr;
                                data.OnColorChanged(wearable);
                            }
                        }
                    }
                }
            }
        }
    }
    auto assemble_illusion_think(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /*  0 */ 0x48, 0x89, 0x5C, 0x24, 0x18, 0x57, 0x48, 0x83, 0xEC, 0x20,
            /*  A */ 0x48, 0x8B, 0x3D,
            '?', '?', '?', '?',
            0x48, 0x8B, 0xD9, 0x48, 0x8B, 0x47, 0x58, 0xFF, 0xD0, 0x84, 0xC0, 0x0F, 0x84,
            0xA6, 0x00, 0x00, 0x00, 0x8B, 0x47, 0x08, 0x83, 0x3C, 0x18, 0xFF, 0x0F, 0x84,
            0x99, 0x00, 0x00, 0x00, 0x0F, 0xB6, 0x47, 0x26, 0x8B, 0x4F, 0x0C, 0x48, 0x89,
            0x74, 0x24, 0x38, 0x0F, 0xB6, 0x77, 0x27, 0xC1, 0xE6, 0x08, 0x0B, 0xF0, 0x0F,
            0xB6, 0x47, 0x25, 0xC1, 0xE6, 0x08, 0x0B, 0xF0, 0x0F, 0xB6, 0x47, 0x24, 0xC1,
            0xE6, 0x08, 0x0B, 0xF0, 0x83, 0x3C, 0x19, 0xFF, 0x75, 0x67, 0x8B, 0x47, 0x10,
            0x48, 0x89, 0x6C, 0x24, 0x30, 0xC6, 0x04, 0x18, 0x01, 0x89, 0x34, 0x19, 0x48,
            0x8B, 0xCB, 0xFF, 0x57, 0x38, 0x8B, 0x47, 0x20, 0x48, 0x03, 0xC3, 0x48, 0x8B,
            0x58, 0x08, 0x48, 0x63, 0x00, 0x48, 0x8D, 0x2C, 0x83, 0x48, 0x3B, 0xDD, 0x74,
            0x37, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x0B, 0x48,
            0x8B, 0x47, 0x40, 0x81, 0xE1, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xD0, 0x48, 0x85,
            0xC0, 0x74, 0x12, 0x8B, 0x4F, 0x0C, 0x83, 0x3C, 0x01, 0xFF, 0x75, 0x09, 0x89,
            0x34, 0x01, 0x48, 0x8B, 0xC8, 0xFF, 0x57, 0x38, 0x48, 0x83, 0xC3, 0x04, 0x48,
            0x3B, 0xDD, 0x75, 0xD2, 0x48, 0x8B, 0x6C, 0x24, 0x30, 0x48, 0x8B, 0x74, 0x24,
            0x38, 0x48, 0x8B, 0x5C, 0x24, 0x40, 0x48, 0x83, 0xC4, 0x20, 0x5F, 0xC3
            });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0xA + 7, think_data_ptr)),
            result.data() + 0xA + 3);
        return result;
    }
    constexpr auto illusion_think_code_size = std::tuple_size_v<decltype(assemble_illusion_think(nullptr, nullptr))>;

    /*
    48 83 EC 28 4C 8B 0D ?? ?? ?? ?? 41 8B 41 14 44 8B 14 08 41 83 E2 18 74 58 41 8B 41 18 49 8B 51
    28 4C 8B 04 08 49 8B C0 48 23 C2 41 83 FA 18 75 21 48 85 C0 75 3B 4C 0B C2 48 8B D1 4C 89 44 24
    30 4C 8D 44 24 30 41 FF 91 88 00 00 00 48 83 C4 28 C3 48 85 C0 74 1A 48 F7 D2 49 23 D0 4C 8D 44
    24 30 48 89 54 24 30 48 8B D1 41 FF 91 88 00 00 00 48 83 C4 28 C3
    */
    void vbe_think(C_BaseEntity* entity)
    {
        const auto& data = *think_data_placeholder;

        constexpr auto radiant = 0b10000;
        constexpr auto dire = 0b01000;
        constexpr auto both = radiant | dire;
        const auto radiant_or_dire
            = entity->Member<std::uint32_t>(data.m_iTaggedAsVisibleByTeam)
            & both;
        /* m_iTaggedAsVisibleByTeam's value was just updated */
        if (radiant_or_dire != 0)
        {
            const auto flag = data.unit_state_flag;
            auto& state = entity->Member<std::uint64_t>(data.m_nUnitState64);
            /* Visible */
            if (radiant_or_dire == both)
            {
                /* Wasn't visible */
                if ((state & flag) == 0)
                {
                    std::uint64_t new_state = state | flag;
                    data.OnUnitStateChanged_original(entity, entity, &new_state);
                }
            }
            /* Not visible */
            else
            {
                /* Was visible */
                if ((state & flag) != 0)
                {
                    std::uint64_t new_state = state & (~flag);
                    data.OnUnitStateChanged_original(entity, entity, &new_state);
                }
            }
        }
    }
    auto assemble_vbe_think(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /*  0 */ 0x48, 0x83, 0xEC, 0x28,
            /*  4 */ 0x4C, 0x8B, 0x0D,
            '?', '?', '?', '?',
            0x41, 0x8B, 0x41, 0x14, 0x44, 0x8B, 0x14, 0x08, 0x41, 0x83, 0xE2, 0x18, 0x74,
            0x58, 0x41, 0x8B, 0x41, 0x18, 0x49, 0x8B, 0x51, 0x28, 0x4C, 0x8B, 0x04, 0x08,
            0x49, 0x8B, 0xC0, 0x48, 0x23, 0xC2, 0x41, 0x83, 0xFA, 0x18, 0x75, 0x21, 0x48,
            0x85, 0xC0, 0x75, 0x3B, 0x4C, 0x0B, 0xC2, 0x48, 0x8B, 0xD1, 0x4C, 0x89, 0x44,
            0x24, 0x30, 0x4C, 0x8D, 0x44, 0x24, 0x30, 0x41, 0xFF, 0x91, 0x88, 0x00, 0x00,
            0x00, 0x48, 0x83, 0xC4, 0x28, 0xC3, 0x48, 0x85, 0xC0, 0x74, 0x1A, 0x48, 0xF7,
            0xD2, 0x49, 0x23, 0xD0, 0x4C, 0x8D, 0x44, 0x24, 0x30, 0x48, 0x89, 0x54, 0x24,
            0x30, 0x48, 0x8B, 0xD1, 0x41, 0xFF, 0x91, 0x88, 0x00, 0x00, 0x00, 0x48, 0x83,
            0xC4, 0x28, 0xC3
        });

        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x4 + 7, think_data_ptr)),
            result.data() + 0x4 + 3);
        return result;
    }
    constexpr auto vbe_think_code_size = std::tuple_size_v<decltype(assemble_vbe_think(nullptr, nullptr))>;

    /*
    4C 8B 15 ?? ?? ?? ?? 41 8B 42 18 4C 8B 0C 08 4D 23 4A 28 4D 09 08 49 FF A2 88 00 00 00
    */
    void OnUnitStateChanged_hook(C_BaseEntity* entity, C_BaseEntity* entity_, std::uint64_t* new_value)
    {
        const auto& data = *think_data_placeholder;

        const auto old_value = entity->Member<std::uint64_t>(data.m_nUnitState64);
        /* always preserve the flag even when the server changes the state */
        *new_value |= (old_value & data.unit_state_flag);
        return data.OnUnitStateChanged_original(entity, entity_, new_value);
    }
    auto assemble_OnUnitStateChanged_hook(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x4C, 0x8B, 0x15,
            '?', '?', '?', '?',
            0x41, 0x8B, 0x42, 0x18, 0x4C, 0x8B, 0x0C, 0x08, 0x4D, 0x23, 0x4A,
            0x28, 0x4D, 0x09, 0x08, 0x49, 0xFF, 0xA2, 0x88, 0x00, 0x00, 0x00
        });
        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0x0 + 7, think_data_ptr)),
            result.data() + 0x0 + 3);
        return result;
    }
    constexpr auto OnUnitStateChanged_hook_code_size = std::tuple_size_v<decltype(assemble_OnUnitStateChanged_hook(nullptr, nullptr))>;


    /*
    48 89 5C 24 10 48 89 6C 24 18 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 8B EA 48 8B F9 48 85 C9 74 3A
    48 89 74 24 30 FF 53 50 0F B6 F0 84 C0 74 26 4C 8B 43 48 48 8B CF 41 FF D0 40 3A C6 75 08 48 8B
    CF FF 53 68 EB 0F 8B 43 1C 83 3C 38 01 75 06 48 8B CF FF 53 60 48 8B 74 24 30 8B D5 48 8B CF 48
    8B 43 78 48 8B 5C 24 38 48 8B 6C 24 40 48 83 C4 20 5F 48 FF E0
    */
    void npc_think_hook(C_BaseEntity* entity, int unk)
    {
        const auto& data = *think_data_placeholder;
        if (entity)
        {

            const auto local_team = data.get_local_team();
            if (local_team != 0)
            {
                if (data.entity_get_team(entity) == local_team)
                {
                    data.vbe_think(entity);
                }
                else
                {
                    constexpr auto HERO = 1;
                    if (entity->Member<std::uint32_t>(data.m_iUnitType) == HERO)
                    {
                        data.illusion_think(entity);
                    }
                }
            }
        }
        return data.npc_think_original(entity, unk);
    }
    auto assemble_npc_think_hook(const std::uint8_t* at, void* think_data_ptr)
    {
        auto result = std::to_array<std::uint8_t>({
            /* 0 */ 0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x6C, 0x24, 0x18, 0x57, 0x48, 0x83, 0xEC, 0x20,
            /* F */ 0x48, 0x8B, 0x1D,
            '?', '?', '?', '?',
            0x8B, 0xEA, 0x48, 0x8B, 0xF9, 0x48, 0x85, 0xC9, 0x74, 0x3A, 0x48, 0x89, 0x74, 0x24,
            0x30, 0xFF, 0x53, 0x50, 0x0F, 0xB6, 0xF0, 0x84, 0xC0, 0x74, 0x26, 0x4C, 0x8B, 0x43,
            0x48, 0x48, 0x8B, 0xCF, 0x41, 0xFF, 0xD0, 0x40, 0x3A, 0xC6, 0x75, 0x08, 0x48, 0x8B,
            0xCF, 0xFF, 0x53, 0x68, 0xEB, 0x0F, 0x8B, 0x43, 0x1C, 0x83, 0x3C, 0x38, 0x01, 0x75,
            0x06, 0x48, 0x8B, 0xCF, 0xFF, 0x53, 0x60, 0x48, 0x8B, 0x74, 0x24, 0x30, 0x8B, 0xD5,
            0x48, 0x8B, 0xCF, 0x48, 0x8B, 0x43, 0x78, 0x48, 0x8B, 0x5C, 0x24, 0x38, 0x48, 0x8B,
            0x6C, 0x24, 0x40, 0x48, 0x83, 0xC4, 0x20, 0x5F, 0x48, 0xFF, 0xE0
        });
        std::ranges::copy(serialize_word<std::endian::little>
            (disp32(at + 0xF + 7, think_data_ptr)),
            result.data() + 0xF + 3);
        return result;
    }
    constexpr auto npc_think_hook_code_size = std::tuple_size_v<decltype(assemble_npc_think_hook(nullptr, nullptr))>;
}
C++:
//client.dll - sha1: 14d0563945a48113acf6d534fe427b1f9c22b8f3 timestamp: 1720829080(00:04:40 13 Jul 2024)
void hook_npc_think_C(HANDLE process)
{
    const auto client_ext = GetModuleExternal(process, "client.dll");
    if (client_ext.empty())
        throw std::runtime_error{ "client.dll not found!" };
    const auto& client_int = ReadModule(process, client_ext);
    if (client_int.size() != client_ext.size())
        throw std::runtime_error{ "can't map client.dll!" };

    npc_think_data data{};
    data.m_iTeamNum = 0x3C3;
    data.m_iHeroID = 0x1AF0;
    data.m_hReplicatingOtherHeroModel = 0x1878;
    data.m_clrRender = 0x5C0;
    data.m_bHasClientSeenIllusionModifier = 0xADD;
    data.m_iTaggedAsVisibleByTeam = 0xC2C;
    data.m_nUnitState64 = 0x1008;
    data.m_iUnitType = 0xA4C;
    data.m_hMyWearables = 0x988;

    data.illusion_color = { .r = 0, .g = 0, .b = 255, .a = 255 };

    constexpr auto MODIFIER_STATE_SPECIALLY_DENIABLE = 1 << 18;
    data.unit_state_flag = MODIFIER_STATE_SPECIALLY_DENIABLE;

    /* CPrediction + 0x50 */
    data.LocalPlayerAddress = (decltype(data.LocalPlayerAddress))((client_ext.data() + 0x4763920) + 0x50);

    data.OnColorChanged = (decltype(data.OnColorChanged))(client_ext.data() + 0x11C61E0);
    data.GetEntityByIndex = (decltype(data.GetEntityByIndex))(client_ext.data() + 0x1337C20);

    CaveFinder caves{ client_int.data(), client_ext.data(), client_int.size() };
    const auto data_ptr_cave = caves.find(sizeof(void*));
    std::cout << "data_ptr_cave: " << (void*)data_ptr_cave << std::endl;

    UnProtect _{ process, data_ptr_cave };

    const auto data_cave = caves.find(sizeof(npc_think_data));
    std::cout << "data_cave: " << (void*)data_cave << std::endl;
    WriteMemory(process, data_ptr_cave, serialize_word<std::endian::little>((std::uintptr_t)data_cave));

#define emit_fn(fn) \
    const auto fn##_cave = caves.find(fn##_code_size); \
    std::cout << #fn << "_cave: " << (void*)fn##_cave << std::endl; \
    WriteMemory(process, fn##_cave, assemble_##fn(fn##_cave, data_ptr_cave)); \
    data.fn = (decltype(data.fn))fn##_cave

    emit_fn(entity_get_team);
    emit_fn(get_local_team);
    emit_fn(is_good_hero);
    emit_fn(illusion_think);
    emit_fn(vbe_think);
    emit_fn(OnUnitStateChanged_hook);
    emit_fn(npc_think_hook);

    const auto npc_think = client_ext.data() + 0x147C740;
    data.npc_think_original = (decltype(data.npc_think_original))
        caves.bytepatch_hook<5>(process, npc_think, npc_think_hook_cave);

    const auto OnUnitStateChanged = client_ext.data() + 0x1481530;
    data.OnUnitStateChanged_original = (decltype(data.OnUnitStateChanged_original))
        caves.bytepatch_hook<5>(process, OnUnitStateChanged, OnUnitStateChanged_hook_cave);

    WriteMemory(process, data_cave, (std::array<std::uint8_t, sizeof(npc_think_data)>&)data);
}
нифига себе ты работу проделал) проще наверное тогда интернал сделать энивей байт патч вроде детект хотя хуй его знает как оно там у вольво детектится ваще без понятие
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
нифига себе ты работу проделал) проще наверное тогда интернал сделать энивей байт патч вроде детект хотя хуй его знает как оно там у вольво детектится ваще без понятие
ну люди шеллкод обычно так и пишут. на языке(С например) сначала пишут потом компилят один раз и копипастят получившийся асм и фиксят если надо.
ну понятно что он детект, хули взял секцию из файла и сравнил с секцией в памяти(.text вообще полностью совпадает там нечего фиксить. можно даже хеш просто секции оригинальной с сервера например получить и сравнить с хешем секции в памяти), либо если надо импорты зарезолвил и релокейты и сравнил(в .rdata таблица импортов лежит и релокации там нужны); с тем же успехом детект и всё остальное(любые модификации ридонли данных наверно не очень будут нравиться античиту, особенно в игровом модуле) что в этом треде(кроме просто записи в ридврайт переменные(типа дистанции камеры), и то если их не захотят чекнуть руками); но если на свой страх и риск то почему бы нет. тот же самый интернал просто не в новую ехекютабл секцию пишется а в уже существующую. вроде сколько играл всю жизнь похуй было вальвам на целостность ридонли секций(ну или может они просто не банят а другие какието меры принимают)(ну естественно это не значит что они не могут задетектить или что не будут в будущем); если конкретно прыжки rel32 беспокоят(E9) то можно юзать другие формы прыжков(хули нет подумаешь на несколько байт больше хук) но сомневаюсь что там токо конкретные модификации(типа E9) запрещены а остальные разрешены(любая это уже хуево. я бы на их месте любые запрещал) но конеш некоторые люди хардкодят. можно опять-таки и другие механизмы хуков тоже юзать(ну правда если и так уже и без этого трогаешь ридонли данные то все равно уже хуево) это же обычный интернал(просто по размеру ограничен), а патчер это обычный экстернал
 
Начинающий
Статус
Оффлайн
Регистрация
31 Окт 2023
Сообщения
69
Реакции[?]
28
Поинты[?]
27K
ну люди шеллкод обычно так и пишут. на языке(С например) сначала пишут потом компилят один раз и копипастят получившийся асм и фиксят если надо.
ну понятно что он детект, хули взял секцию из файла и сравнил с секцией в памяти(.text вообще полностью совпадает там нечего фиксить. можно даже хеш просто секции оригинальной с сервера например получить и сравнить с хешем секции в памяти), либо если надо импорты зарезолвил и релокейты и сравнил(в .rdata таблица импортов лежит и релокации там нужны); с тем же успехом детект и всё остальное(любые модификации ридонли данных наверно не очень будут нравиться античиту, особенно в игровом модуле) что в этом треде(кроме просто записи в ридврайт переменные(типа дистанции камеры), и то если их не захотят чекнуть руками); но если на свой страх и риск то почему бы нет. тот же самый интернал просто не в новую ехекютабл секцию пишется а в уже существующую. вроде сколько играл всю жизнь похуй было вальвам на целостность ридонли секций(ну или может они просто не банят а другие какието меры принимают)(ну естественно это не значит что они не могут задетектить или что не будут в будущем); если конкретно прыжки rel32 беспокоят(E9) то можно юзать другие формы прыжков(хули нет подумаешь на несколько байт больше хук) но сомневаюсь что там токо конкретные модификации(типа E9) запрещены а остальные разрешены(любая это уже хуево. я бы на их месте любые запрещал) но конеш некоторые люди хардкодят. можно опять-таки и другие механизмы хуков тоже юзать(ну правда если и так уже и без этого трогаешь ридонли данные то все равно уже хуево) это же обычный интернал(просто по размеру ограничен), а патчер это обычный экстернал
любое изменение .text секции вызывает детект, как только сработает чек crc32 модулей игры. меточка стоит, будут смотреть, и на волне забанят
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
781
Реакции[?]
331
Поинты[?]
63K
любое изменение .text секции вызывает детект, как только сработает чек crc32 модулей игры. меточка стоит, будут смотреть, и на волне забанят
я не совсем понял про какие изменения ты говоришь - в файле(возможно ты их имел ввиду. но в этом треде файлы не трогаются) или на рантайме(в этом треде рантайм модификации используются, ниже именно про них пишу ниже), но, тем не менее, в сообщении которое ты цитируешь и так уже написано то же самое что ты говоришь, но
1) этот чек может быть выключен в античите в какойто момент времени(ну это не значит что его вообще нет конечно же)
2) детект не значит бан(мб понижение этакого "траст фактора") и не значит инста бан(мб можно будет несколько месяцев поиграть успешно перед баном)
плюс есть еще вопрос разницы VirtualSize и RawSize секции, и ещё выравнивания. в client.dll ~около 500 байт(ну конечно число нестабильное) в памяти в конце .text секции есть которые не входят физически в секцию(они там чисто как паддинг, остатки виртуальной странички, выставлены виндой в ноль), они конечно должны быть 0 но в зависимости от кода детектора(ну нормальный детектор мог бы паддинг на нули проверять в теории) мб их можно модифицировать(не знаю чекают вальвы паддинг или нет не смотрел)
 
Начинающий
Статус
Оффлайн
Регистрация
31 Окт 2023
Сообщения
69
Реакции[?]
28
Поинты[?]
27K
я не совсем понял про какие изменения ты говоришь - в файле(возможно ты их имел ввиду. но в этом треде файлы не трогаются) или на рантайме(в этом треде рантайм модификации используются, ниже именно про них пишу ниже), но, тем не менее, в сообщении которое ты цитируешь и так уже написано то же самое что ты говоришь, но
1) этот чек может быть выключен в античите в какойто момент времени(ну это не значит что его вообще нет конечно же)
2) детект не значит бан(мб понижение этакого "траст фактора") и не значит инста бан(мб можно будет несколько месяцев поиграть успешно перед баном)
плюс есть еще вопрос разницы VirtualSize и RawSize секции, и ещё выравнивания. в client.dll ~около 500 байт(ну конечно число нестабильное) в памяти в конце .text секции есть которые не входят физически в секцию(они там чисто как паддинг, остатки виртуальной странички, выставлены виндой в ноль), они конечно должны быть 0 но в зависимости от кода детектора(ну нормальный детектор мог бы паддинг на нули проверять в теории) мб их можно модифицировать(не знаю чекают вальвы паддинг или нет не смотрел)
Достаточно найти функцию по рефу rtti CUserMessage_Inventory_Response

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

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

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

и ко всему, подобный сбор инфы существует ещё отдельно в вак модулях.
то что тебя не забанило, не означает что такие патчи - андетект.
вот инфа пиздатая, да, полезная(хард пруф что нужные чеки существуют в игре), но я чет не припомню чтобы я тут гдето писал что это андетект(я писал ровно наоборот - цитирую "ну понятно что он детект, хули взял секцию из файла и сравнил с секцией в памяти(.text вообще полностью совпадает там нечего фиксить. можно даже хеш просто секции оригинальной с сервера например получить и сравнить с хешем секции в памяти)")
 
Пользователь
Статус
Оффлайн
Регистрация
8 Апр 2022
Сообщения
673
Реакции[?]
106
Поинты[?]
69K
любое изменение .text секции вызывает детект, как только сработает чек crc32 модулей игры. меточка стоит, будут смотреть, и на волне забанят
а ты знаешь когда или при каких условиях этот чек срабатывает?
CUserMessage_Inventory_Response
Пожалуйста, авторизуйтесь для просмотра ссылки.
это осюда если что я так понял можно хеш просто в хуке в протобафе подменять и все
 
Начинающий
Статус
Оффлайн
Регистрация
31 Окт 2023
Сообщения
69
Реакции[?]
28
Поинты[?]
27K
а ты знаешь когда или при каких условиях этот чек срабатывает?
автоматически по команде с сервера игры выполняется.

Пожалуйста, авторизуйтесь для просмотра ссылки.
это осюда если что я так понял можно хеш просто в хуке в протобафе подменять и все
да обойти можно, но есть подобные сканы уже в самих вак модулях.
 
Начинающий
Статус
Оффлайн
Регистрация
11 Фев 2024
Сообщения
7
Реакции[?]
0
Поинты[?]
0
idk, valve obviously seems to have the tools to detect byte patching but they clearly aren't using it to full extend. I byte patch dota plus (only client side of course) and camera distance for years already and nothing happened. Hell, you can even use cheat engine without vac ban if you know how.

Morphling : Are you planning to update the cheat or have you abandoned it? Still thanks for your work so far!
 
Сверху Снизу