mirror of
https://github.com/Nereziel/cs2-WeaponPaints.git
synced 2026-02-17 10:31:00 +00:00
Add files via upload
This commit is contained in:
221
Patches/MemoryLinux.cs
Normal file
221
Patches/MemoryLinux.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WeaponPaints;
|
||||
|
||||
public static class MemoryLinux
|
||||
{
|
||||
// Based on https://github.com/Source2ZE/CS2Fixes/blob/main/src/utils/plat_unix.cpp
|
||||
static int ParseProt(string s)
|
||||
{
|
||||
int prot = 0;
|
||||
|
||||
foreach (var c in s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '-':
|
||||
break;
|
||||
case 'r':
|
||||
prot |= NativeMethods.PROT_READ;
|
||||
break;
|
||||
case 'w':
|
||||
prot |= NativeMethods.PROT_WRITE;
|
||||
break;
|
||||
case 'x':
|
||||
prot |= NativeMethods.PROT_EXEC;
|
||||
break;
|
||||
case 's':
|
||||
break;
|
||||
case 'p':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
static int GetProt(IntPtr pAddr, uint nSize)
|
||||
{
|
||||
using (var f = File.OpenRead("/proc/self/maps"))
|
||||
using (var reader = new StreamReader(f))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line == null)
|
||||
continue;
|
||||
|
||||
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length < 5)
|
||||
continue;
|
||||
|
||||
var range = parts[0];
|
||||
var prot = parts[1];
|
||||
|
||||
var startEnd = range.Split('-');
|
||||
if (startEnd.Length != 2)
|
||||
continue;
|
||||
|
||||
var start = Convert.ToUInt64(startEnd[0], 16);
|
||||
var end = Convert.ToUInt64(startEnd[1], 16);
|
||||
|
||||
if (start < (ulong)pAddr && end > (ulong)pAddr + nSize)
|
||||
{
|
||||
return ParseProt(prot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void PatchBytesAtAddress(IntPtr pPatchAddress, byte[] pPatch, int iPatchSize)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return;
|
||||
|
||||
var oldProt = GetProt(pPatchAddress, (uint)iPatchSize);
|
||||
|
||||
var pageSize = (ulong)NativeMethods.sysconf(NativeMethods._SC_PAGESIZE);
|
||||
var alignAddr = (IntPtr)((long)pPatchAddress & ~(long)(pageSize - 1));
|
||||
|
||||
var end = (IntPtr)((long)pPatchAddress + iPatchSize);
|
||||
var alignSize = (ulong)((long)end - (long)alignAddr);
|
||||
|
||||
var result = NativeMethods.mprotect(alignAddr, alignSize, NativeMethods.PROT_READ | NativeMethods.PROT_WRITE);
|
||||
|
||||
Marshal.Copy(pPatch, 0, pPatchAddress, iPatchSize);
|
||||
|
||||
result = NativeMethods.mprotect(alignAddr, alignSize, oldProt);
|
||||
}
|
||||
|
||||
private static byte[]? ReadProcessMemory(int pid, long address, int size)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return null;
|
||||
|
||||
byte[] buffer = new byte[size];
|
||||
|
||||
NativeMethods.Iovec local = new NativeMethods.Iovec
|
||||
{
|
||||
iov_base = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0),
|
||||
iov_len = new IntPtr(size)
|
||||
};
|
||||
|
||||
NativeMethods.Iovec remote = new NativeMethods.Iovec
|
||||
{
|
||||
iov_base = new IntPtr(address),
|
||||
iov_len = new IntPtr(size)
|
||||
};
|
||||
|
||||
long bytesRead = NativeMethods.process_vm_readv(pid, new NativeMethods.Iovec[] { local }, 1, new NativeMethods.Iovec[] { remote }, 1, 0);
|
||||
if (bytesRead == -1)
|
||||
{
|
||||
throw new Exception($"process_vm_readv failed with error {Marshal.GetLastPInvokeError()}");
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static byte[]? ReadMemory(IntPtr address, int size)
|
||||
{
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return null;
|
||||
|
||||
return ReadProcessMemory(Process.GetCurrentProcess().Id, (long)address, size);
|
||||
}
|
||||
|
||||
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
|
||||
#pragma warning disable CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
|
||||
static class NativeMethods
|
||||
{
|
||||
public const int O_RDONLY = 0;
|
||||
public const int PROT_READ = 0x1;
|
||||
public const int PROT_WRITE = 0x2;
|
||||
public const int PROT_EXEC = 0x4;
|
||||
public const int MAP_PRIVATE = 0x2;
|
||||
public const int PT_LOAD = 1;
|
||||
public const int PF_X = 0x1;
|
||||
public const int _SC_PAGESIZE = 30;
|
||||
public const int RTLD_DI_LINKMAP = 2;
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int dlinfo(IntPtr handle, int request, out link_map lmap);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int dlclose(IntPtr handle);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int open(string pathname, int flags);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int fstat(int fd, out stat buf);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern IntPtr mmap(IntPtr addr, ulong length, int prot, int flags, int fd, ulong offset);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int munmap(IntPtr addr, ulong length);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern int mprotect(IntPtr addr, ulong len, int prot);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern long sysconf(int name);
|
||||
|
||||
[DllImport("libc")]
|
||||
public static extern long process_vm_readv(int pid, Iovec[] local_iov, ulong liovcnt, Iovec[] remote_iov, ulong riovcnt, ulong flags);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Iovec
|
||||
{
|
||||
public IntPtr iov_base;
|
||||
public IntPtr iov_len;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct link_map
|
||||
{
|
||||
public IntPtr l_addr;
|
||||
public IntPtr l_name;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ElfW
|
||||
{
|
||||
public struct Ehdr
|
||||
{
|
||||
public byte e_shnum;
|
||||
public uint e_shoff;
|
||||
public ushort e_phnum;
|
||||
public uint e_phoff;
|
||||
}
|
||||
|
||||
public struct Phdr
|
||||
{
|
||||
public int p_type;
|
||||
public int p_flags;
|
||||
|
||||
public ulong p_vaddr;
|
||||
public ulong p_filesz;
|
||||
}
|
||||
|
||||
public struct Shdr
|
||||
{
|
||||
public uint sh_name;
|
||||
public uint sh_offset;
|
||||
public uint sh_size;
|
||||
public ulong sh_addr;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
|
||||
public struct stat
|
||||
{
|
||||
public ulong st_size;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
|
||||
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
|
||||
}
|
||||
34
Patches/MemoryWindows.cs
Normal file
34
Patches/MemoryWindows.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WeaponPaints;
|
||||
|
||||
public static class MemoryWindows
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out IntPtr lpNumberOfBytesWritten);
|
||||
|
||||
public static void PatchBytesAtAddress(IntPtr pPatchAddress, byte[] pPatch, int iPatchSize)
|
||||
{
|
||||
if(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
|
||||
|
||||
IntPtr bytesWritten;
|
||||
WriteProcessMemory(Process.GetCurrentProcess().Handle, pPatchAddress, pPatch, (uint)iPatchSize, out bytesWritten);
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);
|
||||
|
||||
public static byte[]? ReadMemory(IntPtr address, int size)
|
||||
{
|
||||
if(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return null;
|
||||
|
||||
byte[] buffer = new byte[size];
|
||||
int bytesRead;
|
||||
ReadProcessMemory(Process.GetCurrentProcess().Handle, address, buffer, size, out bytesRead);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
104
Patches/Patch.cs
Normal file
104
Patches/Patch.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
|
||||
namespace WeaponPaints;
|
||||
|
||||
// Thanks cssharp-fixes
|
||||
public static class Patch
|
||||
{
|
||||
private static IntPtr GetAddress(string modulePath, string signature)
|
||||
{
|
||||
// Returns address if found, otherwise a C++ nullptr which is a IntPtr.Zero in C#
|
||||
var address = NativeAPI.FindSignature(modulePath, signature);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
public static void PerformPatch(string signature, string patch)
|
||||
{
|
||||
IntPtr address = GetAddress(Addresses.ServerPath, signature);
|
||||
if(address == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WriteBytesToAddress(address, HexToByte(patch));
|
||||
}
|
||||
|
||||
private static void WriteBytesToAddress(IntPtr address, List<byte> bytes)
|
||||
{
|
||||
int patchSize = bytes.Count;
|
||||
if(patchSize == 0) throw new ArgumentException("Patch bytes list cannot be empty.");
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
MemoryLinux.PatchBytesAtAddress(address, bytes.ToArray(), patchSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryWindows.PatchBytesAtAddress(address, bytes.ToArray(), patchSize);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<byte> HexToByte(string src)
|
||||
{
|
||||
if (string.IsNullOrEmpty(src))
|
||||
{
|
||||
return new List<byte>();
|
||||
}
|
||||
|
||||
byte HexCharToByte(char c)
|
||||
{
|
||||
if (c is >= '0' and <= '9') return (byte)(c - '0');
|
||||
if (c is >= 'A' and <= 'F') return (byte)(c - 'A' + 10);
|
||||
if (c is >= 'a' and <= 'f') return (byte)(c - 'a' + 10);
|
||||
return 0xFF; // Invalid hex character
|
||||
}
|
||||
|
||||
List<byte> result = new List<byte>();
|
||||
bool isCodeStyle = src[0] == '\\';
|
||||
string pattern = isCodeStyle ? "\\x" : " ";
|
||||
string wildcard = isCodeStyle ? "2A" : "?";
|
||||
int pos = 0;
|
||||
|
||||
while (pos < src.Length)
|
||||
{
|
||||
int found = src.IndexOf(pattern, pos);
|
||||
if (found == -1)
|
||||
{
|
||||
found = src.Length;
|
||||
}
|
||||
|
||||
string str = src.Substring(pos, found - pos);
|
||||
pos = found + pattern.Length;
|
||||
|
||||
if (string.IsNullOrEmpty(str)) continue;
|
||||
|
||||
string byteStr = str;
|
||||
|
||||
if (byteStr.Substring(0, wildcard.Length) == wildcard)
|
||||
{
|
||||
result.Add(0xFF); // Representing wildcard as 0xFF
|
||||
continue;
|
||||
}
|
||||
|
||||
if (byteStr.Length < 2)
|
||||
{
|
||||
return new List<byte>(); // Invalid byte length
|
||||
}
|
||||
|
||||
byte high = HexCharToByte(byteStr[0]);
|
||||
byte low = HexCharToByte(byteStr[1]);
|
||||
|
||||
if (high == 0xFF || low == 0xFF)
|
||||
{
|
||||
return new List<byte>(); // Invalid hex character
|
||||
}
|
||||
|
||||
result.Add((byte)((high << 4) | low));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
@@ -16,10 +17,16 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
|
||||
public override string ModuleAuthor => "Nereziel & daffyy";
|
||||
public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based";
|
||||
public override string ModuleName => "WeaponPaints";
|
||||
public override string ModuleVersion => "3.1c";
|
||||
public override string ModuleVersion => "3.1d";
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
// Hardcoded hotfix needs to be changed later
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
Patch.PerformPatch("0F 85 ? ? ? ? 31 C0 B9 ? ? ? ? BA ? ? ? ? 66 0F EF C0 31 F6 31 FF 48 C7 45 ? ? ? ? ? 48 C7 45 ? ? ? ? ? 48 C7 45 ? ? ? ? ? 48 C7 45 ? ? ? ? ? 0F 29 45 ? 48 C7 45 ? ? ? ? ? C7 45 ? ? ? ? ? 66 89 45 ? E8 ? ? ? ? 41 89 C5 85 C0 0F 8E", "90 90 90 90 90 90");
|
||||
else
|
||||
Patch.PerformPatch("74 ? 48 8D 0D ? ? ? ? FF 15 ? ? ? ? EB ? BA", "EB");
|
||||
|
||||
Instance = this;
|
||||
|
||||
if (hotReload)
|
||||
|
||||
Reference in New Issue
Block a user