/// <summary> /// Finds a game process with a known engine.dll. /// </summary> Process GetGameProcess(out IntPtr curMapPtr, out IntPtr curTimePtr, out IntPtr signOnStatePtr) { string[] procs = _settings.GameProcesses.Select(x => x.ToLower().Replace(".exe", String.Empty)).ToArray(); Process p = Process.GetProcesses().FirstOrDefault(x => procs.Contains(x.ProcessName.ToLower())); if (p != null && !p.HasExited && !IsVACProtectedProcess(p)) { ProcessModuleEx engine = GetProcessModules(p).FirstOrDefault(x => x.ModuleName.ToLower() == "engine.dll"); if (engine != null) { var scanner = new SignatureScanner(p, engine.BaseAddress, engine.ModuleMemorySize); curMapPtr = scanner.Scan(_curMapTarget); curTimePtr = scanner.Scan(_curTimeTarget); signOnStatePtr = scanner.Scan(_signOnStateTarget1); if (signOnStatePtr == IntPtr.Zero) { signOnStatePtr = scanner.Scan(_signOnStateTarget2); } if (curMapPtr != IntPtr.Zero && curTimePtr != IntPtr.Zero && signOnStatePtr != IntPtr.Zero) { return(p); } } Debug.WriteLine("Invalid process or unknown engine.dll"); } curMapPtr = curTimePtr = signOnStatePtr = IntPtr.Zero; return(null); }
// also works for anything derived from CBaseEntity (player etc) (no multiple inheritance) // var must be included by one of the DEFINE_FIELD macros public static bool GetBaseEntityMemberOffset(string member, Process game, SignatureScanner scanner, out int offset) { offset = -1; IntPtr stringPtr = scanner.Scan(new SigScanTarget(0, Encoding.ASCII.GetBytes(member))); if (stringPtr == IntPtr.Zero) { return(false); } var b = BitConverter.GetBytes(stringPtr.ToInt32()); var target = new SigScanTarget(10, String.Format("C7 05 ?? ?? ?? ?? {0:X02} {1:X02} {2:X02} {3:X02}", b[0], b[1], b[2], b[3])); // mov dword_15E2BF1C, offset aM_fflags ; "m_fFlags" target.OnFound = (proc, s, ptr) => { // this instruction is almost always directly after above one, but there are a few cases where it isn't // so we have to scan down and find it var proximityScanner = new SignatureScanner(proc, ptr, 256); return(proximityScanner.Scan(new SigScanTarget(6, "C7 05 ?? ?? ?? ?? ?? ?? 00 00"))); // mov dword_15E2BF20, 0CCh }; IntPtr addr = scanner.Scan(target); if (addr == IntPtr.Zero) { return(false); } return(game.ReadInt32(addr, out offset)); }
public void Init(GameState state) { _remoteOps = new RemoteOpsHandler(state.GameProcess); _cmdBufferPtr = IntPtr.Zero; ProcessModuleWow64Safe engine = state.GetModule("engine.dll"); var scanner = new SignatureScanner(state.GameProcess, engine.BaseAddress, engine.ModuleMemorySize); IntPtr ptr = scanner.Scan(new SigScanTarget("68" + scanner.Scan(new SigScanTarget("execing %s\n".ConvertToHex())).GetByteString())); if (ptr == IntPtr.Zero) { goto fail; } byte[] bytes = state.GameProcess.ReadBytes(ptr, 100); for (int i = 0; i < 100; i++) { byte e = bytes[i]; if (e == 0xA1 || (bytes[i] >= 0xB8 && bytes[i] <= 0xBF)) { uint val = state.GameProcess.ReadValue <uint>(ptr + i + 1); if (scanner.IsWithin(val)) { _cmdBufferPtr = (IntPtr)val; Debug.WriteLine("Command buffer found at 0x" + _cmdBufferPtr.ToString("X")); break; } } } GetExecPtr(state); Update(state); SendConsoleMsg("\nSourceSplit Custom Commands are present, enter \"ss_list\" to list them, or \"ss_h\" for help!\n\n"); return; fail: _cmdBufferPtr = IntPtr.Zero; Debug.WriteLine("Failed to initialize custom command handler!"); return; }
// also works for anything derived from CBaseEntity (player etc) (no multiple inheritance) // var must be included by one of the DEFINE_FIELD macros public static bool GetBaseEntityMemberOffset(string member, Process game, SignatureScanner scanner, out int offset) { offset = -1; IntPtr stringPtr = scanner.Scan(new SigScanTarget(0, Encoding.ASCII.GetBytes(member))); if (stringPtr == IntPtr.Zero) { return(false); } var b = BitConverter.GetBytes(stringPtr.ToInt32()); var target = new SigScanTarget(10, $"C7 05 ?? ?? ?? ?? {b[0]:X02} {b[1]:X02} {b[2]:X02} {b[3]:X02}"); // mov dword_15E2BF1C, offset aM_fflags ; "m_fFlags" target.OnFound = (proc, s, ptr) => { // this instruction is almost always directly after above one, but there are a few cases where it isn't // so we have to scan down and find it var proximityScanner = new SignatureScanner(proc, ptr, 256); return(proximityScanner.Scan(new SigScanTarget(6, "C7 05 ?? ?? ?? ?? ?? ?? 00 00"))); // mov dword_15E2BF20, 0CCh }; IntPtr addr = scanner.Scan(target); if (addr == IntPtr.Zero) { // seen in Black Mesa Source (legacy version) var target2 = new SigScanTarget(1, "68 ?? ?? ?? ??", // push 256 $"68 {b[0]:X02} {b[1]:X02} {b[2]:X02} {b[3]:X02}"); // push offset aM_fflags ; "m_fFlags" addr = scanner.Scan(target2); if (addr == IntPtr.Zero) { return(false); } } return(game.ReadValue(addr, out offset)); }
// TODO: log fails bool TryGetGameProcess(out Process p, out GameOffsets offsets) { #if DEBUG var sw = Stopwatch.StartNew(); #endif string[] procs = _settings.GameProcesses.Select(x => x.ToLower().Replace(".exe", String.Empty)).ToArray(); p = Process.GetProcesses().FirstOrDefault(x => procs.Contains(x.ProcessName.ToLower())); offsets = new GameOffsets(); if (p == null || p.HasExited || Util.IsVACProtectedProcess(p)) { return(false); } // process is up, check if engine and server are both loaded yet ProcessModule engine = p.Modules.Cast <ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "engine.dll"); ProcessModule server = p.Modules.Cast <ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "server.dll"); if (engine == null || server == null) { return(false); } // required engine stuff var scanner = new SignatureScanner(p, engine.BaseAddress, engine.ModuleMemorySize); if ((offsets.CurMapPtr = scanner.Scan(_curMapTarget)) == IntPtr.Zero || (offsets.CurTimePtr = scanner.Scan(_curTimeTarget)) == IntPtr.Zero || (offsets.GameDirPtr = scanner.Scan(_gameDirTarget)) == IntPtr.Zero || (offsets.HostStatePtr = scanner.Scan(_hostStateTarget)) == IntPtr.Zero) { return(false); } if ((offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget1)) == IntPtr.Zero && (offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget2)) == IntPtr.Zero) { return(false); } // required server stuff var serverScanner = new SignatureScanner(p, server.BaseAddress, server.ModuleMemorySize); if ((offsets.GlobalEntityListPtr = serverScanner.Scan(_globalEntityListTarget)) == IntPtr.Zero) { return(false); } // entity offsets if (!GetBaseEntityMemberOffset("m_fFlags", p, serverScanner, out offsets.BaseEntityFlagsOffset) || !GetBaseEntityMemberOffset("m_vecAbsOrigin", p, serverScanner, out offsets.BaseEntityAbsOriginOffset) || !GetBaseEntityMemberOffset("m_iName", p, serverScanner, out offsets.BaseEntityTargetNameOffset) || !GetBaseEntityMemberOffset("m_hViewEntity", p, serverScanner, out offsets.BasePlayerViewEntity)) { return(false); } // find m_pParent offset. the string "m_pParent" occurs more than once so we have to do something else // in old engine it's right before m_iParentAttachment. in new engine it's right before m_nTransmitStateOwnedCounter // TODO: test on all engines int tmp; if (!GetBaseEntityMemberOffset("m_nTransmitStateOwnedCounter", p, serverScanner, out tmp)) { if (!GetBaseEntityMemberOffset("m_iParentAttachment", p, serverScanner, out tmp)) { return(false); } tmp -= 4; // sizeof m_iParentAttachment } tmp -= 4; // sizeof m_nTransmitStateOwnedCounter (4 aligned byte) offsets.BaseEntityParentHandleOffset = tmp; Debug.WriteLine("CBaseServer::m_szMapname ptr = 0x" + offsets.CurMapPtr.ToString("X")); Debug.WriteLine("CGlobalVarsBase::curtime ptr = 0x" + offsets.CurTimePtr.ToString("X")); Debug.WriteLine("CBaseClientState::m_nSignonState ptr = 0x" + offsets.SignOnStatePtr.ToString("X")); Debug.WriteLine("CBaseEntityList::(CEntInfo)m_EntPtrArray ptr = 0x" + offsets.GlobalEntityListPtr.ToString("X")); Debug.WriteLine("CBaseEntity::m_fFlags offset = 0x" + offsets.BaseEntityFlagsOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_vecAbsOrigin offset = 0x" + offsets.BaseEntityAbsOriginOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_iName offset = 0x" + offsets.BaseEntityTargetNameOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_pParent offset = 0x" + offsets.BaseEntityParentHandleOffset.ToString("X")); Debug.WriteLine("CBasePlayer::m_hViewEntity offset = 0x" + offsets.BasePlayerViewEntity.ToString("X")); #if DEBUG Debug.WriteLine("TryGetGameProcess took: " + sw.Elapsed); #endif return(true); }
// TODO: log fails bool TryGetGameProcess(out Process p, out GameOffsets offsets) { #if DEBUG var sw = Stopwatch.StartNew(); #endif string[] procs = _settings.GameProcesses.Select(x => x.ToLower().Replace(".exe", String.Empty)).ToArray(); p = Process.GetProcesses().FirstOrDefault(x => procs.Contains(x.ProcessName.ToLower())); offsets = new GameOffsets(); if (p == null || p.HasExited || Util.IsVACProtectedProcess(p)) return false; // process is up, check if engine and server are both loaded yet ProcessModule engine = p.Modules.Cast<ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "engine.dll"); ProcessModule server = p.Modules.Cast<ProcessModule>().FirstOrDefault(x => x.ModuleName.ToLower() == "server.dll"); if (engine == null || server == null) return false; // required engine stuff var scanner = new SignatureScanner(p, engine.BaseAddress, engine.ModuleMemorySize); if ((offsets.CurMapPtr = scanner.Scan(_curMapTarget)) == IntPtr.Zero || (offsets.CurTimePtr = scanner.Scan(_curTimeTarget)) == IntPtr.Zero || (offsets.GameDirPtr = scanner.Scan(_gameDirTarget)) == IntPtr.Zero || (offsets.HostStatePtr = scanner.Scan(_hostStateTarget)) == IntPtr.Zero) return false; if ((offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget1)) == IntPtr.Zero && (offsets.SignOnStatePtr = scanner.Scan(_signOnStateTarget2)) == IntPtr.Zero) return false; // required server stuff var serverScanner = new SignatureScanner(p, server.BaseAddress, server.ModuleMemorySize); if ((offsets.GlobalEntityListPtr = serverScanner.Scan(_globalEntityListTarget)) == IntPtr.Zero) return false; // entity offsets if ( !GetBaseEntityMemberOffset("m_fFlags", p, serverScanner, out offsets.BaseEntityFlagsOffset) || !GetBaseEntityMemberOffset("m_vecAbsOrigin", p, serverScanner, out offsets.BaseEntityAbsOriginOffset) || !GetBaseEntityMemberOffset("m_iName", p, serverScanner, out offsets.BaseEntityTargetNameOffset) || !GetBaseEntityMemberOffset("m_hViewEntity", p, serverScanner, out offsets.BasePlayerViewEntity)) return false; // find m_pParent offset. the string "m_pParent" occurs more than once so we have to do something else // in old engine it's right before m_iParentAttachment. in new engine it's right before m_nTransmitStateOwnedCounter // TODO: test on all engines int tmp; if (!GetBaseEntityMemberOffset("m_nTransmitStateOwnedCounter", p, serverScanner, out tmp)) { if (!GetBaseEntityMemberOffset("m_iParentAttachment", p, serverScanner, out tmp)) return false; tmp -= 4; // sizeof m_iParentAttachment } tmp -= 4; // sizeof m_nTransmitStateOwnedCounter (4 aligned byte) offsets.BaseEntityParentHandleOffset = tmp; Debug.WriteLine("CBaseServer::m_szMapname ptr = 0x" + offsets.CurMapPtr.ToString("X")); Debug.WriteLine("CGlobalVarsBase::curtime ptr = 0x" + offsets.CurTimePtr.ToString("X")); Debug.WriteLine("CBaseClientState::m_nSignonState ptr = 0x" + offsets.SignOnStatePtr.ToString("X")); Debug.WriteLine("CBaseEntityList::(CEntInfo)m_EntPtrArray ptr = 0x" + offsets.GlobalEntityListPtr.ToString("X")); Debug.WriteLine("CBaseEntity::m_fFlags offset = 0x" + offsets.BaseEntityFlagsOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_vecAbsOrigin offset = 0x" + offsets.BaseEntityAbsOriginOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_iName offset = 0x" + offsets.BaseEntityTargetNameOffset.ToString("X")); Debug.WriteLine("CBaseEntity::m_pParent offset = 0x" + offsets.BaseEntityParentHandleOffset.ToString("X")); Debug.WriteLine("CBasePlayer::m_hViewEntity offset = 0x" + offsets.BasePlayerViewEntity.ToString("X")); #if DEBUG Debug.WriteLine("TryGetGameProcess took: " + sw.Elapsed); #endif return true; }
// also works for anything derived from CBaseEntity (player etc) (no multiple inheritance) // var must be included by one of the DEFINE_FIELD macros public static bool GetBaseEntityMemberOffset(string member, Process game, SignatureScanner scanner, out int offset) { offset = -1; IntPtr stringPtr = scanner.Scan(new SigScanTarget(0, Encoding.ASCII.GetBytes(member))); if (stringPtr == IntPtr.Zero) return false; var b = BitConverter.GetBytes(stringPtr.ToInt32()); var target = new SigScanTarget(10, String.Format("C7 05 ?? ?? ?? ?? {0:X02} {1:X02} {2:X02} {3:X02}", b[0], b[1], b[2], b[3])); // mov dword_15E2BF1C, offset aM_fflags ; "m_fFlags" target.OnFound = (proc, s, ptr) => { // this instruction is almost always directly after above one, but there are a few cases where it isn't // so we have to scan down and find it var proximityScanner = new SignatureScanner(proc, ptr, 256); return proximityScanner.Scan(new SigScanTarget(6, "C7 05 ?? ?? ?? ?? ?? ?? 00 00")); // mov dword_15E2BF20, 0CCh }; IntPtr addr = scanner.Scan(target); if (addr == IntPtr.Zero) return false; return game.ReadInt32(addr, out offset); }