├── README.md └── patch.py /README.md: -------------------------------------------------------------------------------- 1 | # Script-Enforcer-Bypass 2 | A Garry's Mod Script Enforcer bypass to allow `lua_openscript_cl` commands to execute. 3 | 4 | * 💫 Easy to use 5 | * 💡 Simple and safe 6 | * 💥 64-bit and 32-bit support 7 | 8 | # Recommended Installation (Using Python) 🐍 9 | 10 | 1. Copy `steamapps\common\GarrysMod\garrysmod\bin\client.dll` (x86) or `steamapps\common\GarrysMod\bin\win64\client.dll` (x64) to the current directory. 11 | 2. Run `python patch.py` (get [Python3](https://www.python.org/downloads/) here). 12 | 3. Copy the patched `client.dll` into `steamapps\common\GarrysMod\garrysmod\bin\client.dll` (x86) or `steamapps\common\GarrysMod\bin\win64\client.dll` (x64). 13 | 4. Start Garry's Mod. 14 | 5. Load scripts from your `lua/` directory with `lua_openscript_cl ` using the console. 15 | 16 | # Easy Installation 17 | 18 | [Get the latest .dll from the releases page.](https://github.com/qubard/Script-Enforcer-Bypass/releases) 19 | 20 | 1. Drag and drop `client.dll` into `steamapps\common\Garrysmod\bin`. 21 | 2. Restart Garry's Mod 22 | 3. Load scripts from your `lua/` directory with `lua_openscript_cl `. 23 | 24 | This isn't guaranteed to work if `client.dll` in this repository has not been updated and patched for a new version of Garry's Mod. 25 | 26 | # How It Works 27 | 28 | This NOPs the `JNZ` branch called by `lua_openscript_cl` and adds `mov byte ptr [ecx + b8], 1` (x86) or `mov byte ptr [rcx + 168], 1` (x64) aka byte patch `C681B800000001FF 90 84 01 00 00 5E 5D C3` to the end of the function call to escalate lua execution privileges and change where the file loader searches for your script. [For more info see this article.](https://tarasyk.ca/2019/12/14/bypassing-script-enforcer.html) 29 | 30 | This patch does NOT modify the `sv_allowcslua` convar whatsoever which can easily be detected by servers, and otherwise is fully undetectable (there are no sigchecks on `client.dll`) + can't get you VAC banned (no VAC on garry's mod). 31 | 32 | # Additional tips 33 | 34 | To obfuscate your scripts, run this Python3 script which outputs a new script that base64 encodes the script into base64 and then decodes it. Alternatively, you should override `render.capture` which can be used by server admins to take screenshots. 35 | 36 | Check out [Chrollo](https://github.com/qubard/Chrollo) which lets you block anti-cheat scripts among many other things. 37 | 38 | ``` 39 | code = "" 40 | print("Input the path to your script:") 41 | path = input() 42 | with open(path, 'r') as file: 43 | code = file.read() 44 | from base64 import b64encode 45 | code = b64encode(bytes(code, 'utf-8')) 46 | 47 | final = "local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' \ 48 | function dec(data) \ 49 | data = string.gsub(data, '[^'..b..'=]', '') \ 50 | return (data:gsub('.', function(x) \ 51 | if (x == '=') then return '' end \ 52 | local r,f='',(b:find(x)-1) \ 53 | for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end \ 54 | return r; \ 55 | end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) \ 56 | if (#x ~= 8) then return '' end \ 57 | local c=0 \ 58 | for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end \ 59 | return string.char(c) \ 60 | end)) \ 61 | end \ 62 | RunString(dec(\"" + code.decode("utf-8") + "\"))" 63 | ``` 64 | -------------------------------------------------------------------------------- /patch.py: -------------------------------------------------------------------------------- 1 | sigpe = "50,45,00,00".split(',') 2 | sig86 = "55,8b,ec,6a,01,e8,?,?,?,?,83,c4,04,84,c0,74,?,?,?,?,?,?,?,?,74,?,56,8b,75,08,83,3e,02".split(',') 3 | sig64 = "40,53,48,83,ec,30,48,8b,d9,b1,01,e8,?,?,?,?,84,c0,0f,?,?,?,?,?,?,?,?,?,?,?,?,?,0f,?,?,?,?,?,?,?,?,7c,?,e8".split(',') 4 | NOP = 0x90 5 | 6 | 7 | # return the index of the matching signature 8 | def sigscan(bytearr, sig): 9 | for i in range(0, len(bytearr)): 10 | if i + len(sig) < len(bytearr): 11 | found = True 12 | for j in range(0, len(sig)): 13 | if sig[j] != '?' and bytearr[i + j] != int(sig[j], 16): 14 | found = False 15 | break 16 | 17 | if found: 18 | return i 19 | return None 20 | 21 | 22 | def is_64(input_bytes: bytearray) -> bool: 23 | pe_start = sigscan(input_bytes, sigpe) 24 | return input_bytes[pe_start + 0x4] == 0x64 25 | 26 | 27 | def patch_86(input_bytes: bytearray) -> bool: 28 | func_start = sigscan(input_bytes, sig86) 29 | 30 | if func_start is not None: 31 | print("Found function! Patching.. at offset " + hex(func_start)) 32 | 33 | input_bytes[func_start + 0x33] = NOP 34 | input_bytes[func_start + 0x34] = NOP 35 | end = func_start + 0x7c 36 | patch = [0xC6, 0x81, 0xB8, 0x00, 0x00, 0x00, 0x01, 0xFF, 0x90, 0x84, 0x01, 0x00, 0x00, 0x5E, 0x5D, 0xC3] 37 | for i in range(0, len(patch)): 38 | input_bytes[end + i] = patch[i] 39 | 40 | return True 41 | 42 | return False 43 | 44 | 45 | def patch_64(input_bytes: bytearray) -> bool: 46 | func_start = sigscan(input_bytes, sig64) 47 | 48 | if func_start is not None: 49 | print("Found function! Patching.. at offset " + hex(func_start)) 50 | 51 | input_bytes[func_start + 0x3B] = NOP 52 | input_bytes[func_start + 0x3C] = NOP 53 | end = func_start + 0x78 54 | patch = [0xC6, 0x81, 0x68, 0x01, 0x00, 0x00, 0x01] 55 | 56 | for i in range(0, 0x2F + 0x1): 57 | patch.append(input_bytes[end + 0x5 + i]) 58 | 59 | for i in range(0, len(patch)): 60 | input_bytes[end + i] = patch[i] 61 | 62 | return True 63 | 64 | return False 65 | 66 | 67 | with open("client.dll", "rb") as f: 68 | file_bytes = f.read() 69 | file_bytes = bytearray(file_bytes) 70 | 71 | success = False 72 | 73 | if is_64(file_bytes): 74 | print("Detected x64 executeable...") 75 | success = patch_64(file_bytes) 76 | else: 77 | print("Detected x86 executeable...") 78 | success = patch_86(file_bytes) 79 | 80 | if success: 81 | print("Patched..saving to disk") 82 | 83 | import shutil 84 | 85 | print("Backing up client.dll") 86 | shutil.copyfile("client.dll", "client.dll.bak") 87 | 88 | with open("client.dll", "wb") as f2: 89 | f2.write(file_bytes) 90 | else: 91 | print("Could not find matching function") 92 | --------------------------------------------------------------------------------