View Single Post
Forgetest
Member
Join Date: Aug 2020
Old 09-23-2021 , 03:01   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #8

Quote:
Originally Posted by Psyk0tik View Post
Patching out function calls with NOPs on Windows isn't sufficient most of the time. Even if you patch out the entire instruction (which is almost always needed on Windows), it won't work most of the time and you'll have to worry about patching a lot of bytes. You can instead try patching with unconditional jumps.

If you don't know how those work:

Basically, your patch should jump from one instruction to another (while skipping any other instruction between your source and your destination). The destination of the jump depends on which part(s) of the function you want to skip over.

For example, if you want to skip the function call for CTerrorGun::Holster, you'll want to start at the first byte of that function's call instruction. Since an unconditional jump (E9 ? ? ? ?) takes 5 bytes, the length of your jump will be calculated by the following equation:

Let a1 = Address of the patch location + 5 bytes (because the patch itself takes five bytes)
Let a2 = Address of the start of the instruction after the CTerrorGun::Holster's call instruction.
Equation: a2 - a1 = JMP instruction length

Here is CTerrorGun::Holster's call instruction on Windows:
Code:
.text:103E4712 8B 10                                         mov     edx, [eax]
.text:103E4714 8B C8                                         mov     ecx, eax
.text:103E4716 8B 82 24 04 00 00                             mov     eax, [edx+424h]
.text:103E471C 6A 00                                         push    0
.text:103E471E FF D0                                         call    eax
Your patch will start at the first byte (8B). The offset for that is 172h (370). Your patch length is 5 bytes so a1 will be 103E4717 (82).

Here's the next instruction:
Code:
.text:103E4720 56                                            push    esi
.text:103E4721 8B CB                                         mov     ecx, ebx
.text:103E4723 E8 C8 F9 C5 FF                                call    sub_100440F0
Your a2 will be 103E4720. Now you can use the equation and get 9. Your patch bytes will now be \xE9\x09\x00\x00\x00.

Since I like to be consistent on both platforms and both games as much as possible, I used the unconditional jump method for Linux as well. These are my offsets and sigs:
PHP Code:
"Games"
{
    
"#default"
    
{
        
"Offsets"
        
{
            
"OS"
            
{
                
"linux"            "1"
                "windows"        "0"
            
}
        }
    }
    
"left4dead"
    
{
        
"MemPatches"
        
{
            
"EquipSecondWeapon_StopHolster"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "297"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x0E\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "403"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x09\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_SetActiveWeapon"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "316"
                    "verify"    "\x89"
                    "patch"        "\xE9\x0B\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "417"
                    "verify"    "\x56"
                    "patch"        "\xE9\x03\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_Deploy"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "332"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x06\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "425"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
            }
        }
        
"Signatures"
        
{
            
/**
             * CTerrorGun::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1. Find the function's offsets with asherkin's VTable dump.
             * 2. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x85\x2A\x8B\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B"
                        
/* ? ? ? ? ? ? ? ? ? 85 ? 8B ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B ? 8B */
            
}
            
/**
             * CTerrorGun::EquipSecondWeapon()
             *
             * How to find on Windows:
             * 1a. Search for the "Player.PickupWeapon" string.
             * 2a. One of the functions that references that string should be this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4b. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_EquipSecondWeapon"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun17EquipSecondWeaponEv"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x80\xBE\x2A\x2A\x2A\x2A\x2A\x8D\x9E\x2A\x2A\x2A\x2A\x74\x2A\x5E"
                        
/* ? ? ? ? ? ? ? 80 BE ? ? ? ? ? 8D 9E ? ? ? ? 74 ? 5E */
            
}
            
/**
             * CWeaponSpawn::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1a. Search for the "spawner_give_item" string.
             * 2a. The string can only be found in this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
             * 3b. Search for "CWeaponSpawn::`vftable'" to jump to the "CWeaponSpawn" vtable.
             * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 5b. Look for the target function in the Windows binary.
             **/
            
"CWeaponSpawn::Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN12CWeaponSpawn3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x85\x2A\x8B\x2A\x89\x2A\x2A\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B"
                        
/* ? ? ? ? ? ? ? ? ? 85 ? 8B ? 89 ? ? ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B */
            
}
        }
    }
    
    
"left4dead2"
    
{
        
"MemPatches"
        
{
            
"EquipSecondWeapon_StopHolster"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "294"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x0E\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "370"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x09\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_SetActiveWeapon"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "313"
                    "verify"    "\x89"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "384"
                    "verify"    "\x56"
                    "patch"        "\xE9\x03\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_Deploy"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "325"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x06\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "392"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
            }
        }
        
        
"Signatures"
        
{
            
/**
             * CTerrorGun::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1. Find the function's offsets with asherkin's VTable dump.
             * 2. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x56\x57\x8B\x2A\x2A\x8B\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B"
                        
/* ? ? ? ? ? ? 56 57 8B ? ? 8B ? 85 ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B ? 8B */
            
}
            
/**
             * CTerrorGun::EquipSecondWeapon()
             *
             * How to find on Windows:
             * 1a. Search for the "Player.PickupWeapon" string.
             * 2a. One of the functions that references that string should be this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4b. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_EquipSecondWeapon"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun17EquipSecondWeaponEv"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x56\x8B\x2A\x80\xBE\x2A\x2A\x2A\x2A\x2A\x74\x2A\x32\x2A\x5E\x8B"
                        
/* ? ? ? ? ? ? 56 8B ? 80 BE ? ? ? ? ? 74 ? 32 ? 5E 8B */
            
}
            
/**
             * CWeaponSpawn::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1a. Search for the "spawner_give_item" string.
             * 2a. The string can only be found in this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
             * 3b. Search for "CWeaponSpawn::`vftable'" to jump to the "CWeaponSpawn" vtable.
             * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 5b. Look for the target function in the Windows binary.
             **/
            
"CWeaponSpawn::Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN12CWeaponSpawn3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x53\x57\x8B\x2A\x2A\x8B\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B"
                        
/* ? ? ? ? ? ? 53 57 8B ? ? 8B ? 85 ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B */
            
}
        }
    }

I've tested both games on Windows and the patches work as intended without any crashes.
Thanks a lot bro! You're always helpful and innovative to introduce another good method.

And thank you Marttt and Silvers for your time, but as Harry replied using events is afterwards and not perfect for this.

At first I wanted to change the pistol netprops instead, to fake client a pick-up, but it could be messy when targeting weapon_spawn. Memory patching is what I'm not actually willing for, while it should be the best choice.

Eventually I decided to make it into l4d2_pickup made by SirPlease, which originally provides functionality to block pick-up switches.

Link: https://github.com/Target5150/MoYu_S...ckup/README.md

Last edited by Forgetest; 09-23-2021 at 03:07. Reason: dead keyboard
Forgetest is offline