//UnixGate.cs - BY MARCUSSACANA using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; internal static class UnixGate { static bool Enabled; static byte[] OriLoadLibExW; static byte[] JmpLoadLibExW; static byte[] OriGetProc; static byte[] JmpGetProc; static IntPtr LoadLibraryExWAddr; static IntPtr GetProcAddr; static IntPtr BaseLoadLibExAddr; static IntPtr BaseGetProcAddr; static IntPtr dlsymAddr; static IntPtr dlopenAddr; static IntPtr GetModFN; static IntPtr GetProcHookAddr; static IntPtr LoadLibExWHookAddr; static IntPtr Kernel; static IntPtr KernelBase; internal static void Initialize() { if (LoadLibraryExWAddr != IntPtr.Zero) return; Kernel = LoadLibraryW("kernel32"); KernelBase = LoadLibraryW("kernelbase"); LoadLibraryExWAddr = GetProcAddressManaged(Kernel, "LoadLibraryExW"); GetProcAddr = GetProcAddressManaged(Kernel, "GetProcAddress"); GetModFN = GetProcAddressManaged(Kernel, "GetModuleFileNameA"); BaseGetProcAddr = GetProcAddressManaged(KernelBase, "GetProcAddress"); BaseLoadLibExAddr = GetProcAddressManaged(KernelBase, "LoadLibraryExW"); if (BaseLoadLibExAddr == IntPtr.Zero || BaseGetProcAddr == IntPtr.Zero) throw new Exception("Unsupported Wine Version"); OriLoadLibExW = Read(LoadLibraryExWAddr, (uint)JmpSize); OriGetProc = Read(GetProcAddr, (uint)JmpSize); dGetProcAddr = new GetProcAddressDel(GetProcHook); dLoadLibExW = new LoadLibraryExWDel(LoadLibraryExHook); dLoadLibExWReal = (LoadLibraryExWDel)Marshal.GetDelegateForFunctionPointer(BaseLoadLibExAddr, typeof(LoadLibraryExWDel)); dGetModuleFileName = (GetModuleFileNameDel)Marshal.GetDelegateForFunctionPointer(GetModFN, typeof(GetModuleFileNameDel)); GetProcHookAddr = Marshal.GetFunctionPointerForDelegate(dGetProcAddr); LoadLibExWHookAddr = Marshal.GetFunctionPointerForDelegate(dLoadLibExW); dlopenAddr = GetSymbol("libwine.so.1!wine_dlopen"); dlsymAddr = GetSymbol("libwine.so.1!wine_dlsym"); if (dlopenAddr == IntPtr.Zero || dlsymAddr == IntPtr.Zero) throw new Exception("Invalid Wine Envoriment: Failed to Load the libwine.so"); byte[] PEBData = NTCurPebData; var PEBAddr = Marshal.AllocHGlobal(PEBData.Length); Write(PEBAddr, PEBData, Protection.PAGE_EXECUTE_READWRITE); NtCurrentPeb = (Ret0)Marshal.GetDelegateForFunctionPointer(PEBAddr, typeof(Ret0)); ddlopen = (dlopenDel)Marshal.GetDelegateForFunctionPointer(dlopenAddr, typeof(dlopenDel)); ddlsym = (dlsymDel)Marshal.GetDelegateForFunctionPointer(dlsymAddr, typeof(dlsymDel)); JmpGetProc = AssembleJump(GetProcAddr, GetProcHookAddr); JmpLoadLibExW = AssembleJump(LoadLibraryExWAddr, LoadLibExWHookAddr); } internal static void Enable() { if (Enabled) return; Initialize(); Write(GetProcAddr, JmpGetProc); Write(LoadLibraryExWAddr, JmpLoadLibExW); Enabled = true; } internal static void Disable() { if (!Enabled) return; Write(GetProcAddr, OriGetProc); Write(LoadLibraryExWAddr, OriLoadLibExW); Enabled = false; } static IntPtr GetProcHook(IntPtr hModule, IntPtr Proc) { IntPtr WinFunc = GetProcAddressManaged(hModule, Proc); if (WinFunc == IntPtr.Zero && hModule != IntPtr.Zero) { string ModuleFN = GetModuleFileName(hModule); if (string.IsNullOrEmpty(ModuleFN)) return dlsym(hModule, Proc); } return WinFunc; } static IntPtr LoadLibraryExHook(string Name, IntPtr Reserved, LoadLibraryFlags Flags) { IntPtr hModule = dLoadLibExWReal(Name, Reserved, Flags); if (hModule == IntPtr.Zero) return dlopen(Name, RTLD_NOW); return hModule; } static IntPtr GetProcAddressManaged(IntPtr hModule, string ProcName) { IntPtr Proc = Marshal.StringToHGlobalAnsi(ProcName); IntPtr Rst = GetProcAddressManaged(hModule, Proc); Marshal.FreeHGlobal(Proc); return Rst; } static IntPtr GetProcAddressManaged(IntPtr hModule, IntPtr ProcNamePtr) { ushort Ordinal = 0; if (ProcNamePtr.ToUlong() > ushort.MaxValue) { var ProcedureName = new ANSI_STRING(); var ProcName = Marshal.PtrToStringAnsi(ProcNamePtr); ProcedureName.Length = (short)ProcName.Length; ProcedureName.MaximumLength = ProcedureName.Length; ProcedureName.Buffer = ProcName; var Addr = Marshal.AllocHGlobal(ProcName.Length + 5); Marshal.StructureToPtr(ProcedureName, Addr, false); ProcNamePtr = Addr; } else Ordinal = (ushort)ProcNamePtr.ToUlong(); IntPtr hMapped = BasepMapModuleHandle(hModule, false); var Status = LdrGetProcedureAddress(hMapped, ProcNamePtr, Ordinal, out IntPtr fnExp); Marshal.FreeHGlobal(ProcNamePtr); if (!NT_SUCCESS(Status)) { SetLastError(Status.ToInt32()); return IntPtr.Zero; } if (fnExp == hMapped) { if (ProcNamePtr.ToUlong() > ushort.MaxValue) SetLastError(0xC0000139);//STATUS_ENTRYPOINT_NOT_FOUND else SetLastError(0xC0000138);//STATUS_ORDINAL_NOT_FOUND return IntPtr.Zero; } return fnExp; } static IntPtr BasepMapModuleHandle(IntPtr hModule, bool AsDataFile) { if (hModule == IntPtr.Zero) return GetCurrentImageBaseAddress(); if ((hModule.ToUlong() & 1) != 0 && AsDataFile) return IntPtr.Zero; return hModule; } static IntPtr GetCurrentImageBaseAddress() { var PEB = NtCurrentPeb(); return Marshal.ReadIntPtr(PEB, IntPtr.Size == 8 ? 0x10 : 0x08); } static bool? IsRealWin = null; public static bool IsRealWindows { get { if (IsRealWin.HasValue) return IsRealWin.Value; IntPtr hModule = GetModuleHandle("ntdll.dll"); if (hModule == IntPtr.Zero) IsRealWin = false; else { IntPtr fptr = GetProcAddressManaged(hModule, "wine_get_version"); IsRealWin = fptr == IntPtr.Zero; } return IsRealWin.Value; } } static bool NT_SUCCESS(IntPtr STATUS) => STATUS.ToInt32() >= 0; static Ret0 NtCurrentPeb; delegate IntPtr Ret0(); /* #include <intrin.h> void* NtCurrentPeb() { #ifdef _WIN64 return (void*)__readgsqword(0x60); #else __asm { mov eax, fs:[0x30]; } #endif } */ static byte[] NTCurPebData { get { if (IntPtr.Size == 8) return NTCurPebx64; return NTCurPebx86; } } static readonly byte[] NTCurPebx64 = new byte[] { 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00 }; static readonly byte[] NTCurPebx86 = new byte[] { 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00 }; const uint SYMOPT_DEFERRED_LOADS = 0x00000004; const uint SYMOPT_PUBLICS_ONLY = 0x00004000; internal const uint RTLD_NOW = 0x002; static GetProcAddressDel dGetProcAddr; static LoadLibraryExWDel dLoadLibExW; static LoadLibraryExWDel dLoadLibExWReal; static dlsymDel ddlsym; static dlopenDel ddlopen; [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] delegate IntPtr GetProcAddressDel(IntPtr hModule, IntPtr Proc); [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] delegate IntPtr LoadLibraryExWDel(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true)] delegate IntPtr dlsymDel(IntPtr hModule, IntPtr Symbol, IntPtr StrError, uint ErrorSize); [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError = true)] delegate IntPtr dlopenDel(IntPtr lpFileName, uint Flags, IntPtr StrError, uint ErrorSize); internal static IntPtr dlsym(IntPtr hModule, string Symbol) => dlsym(hModule, Marshal.StringToHGlobalAnsi(Symbol)); internal static IntPtr dlopen(string lpFilename, uint Flags) => dlopen(Marshal.StringToHGlobalAnsi(lpFilename), Flags); internal static IntPtr dlsym(IntPtr hModule, IntPtr Symbol) { if (IntPtr.Size == 8) return UnixFastCall(dlsymAddr, hModule, Symbol, IntPtr.Zero, 0); return ddlsym(hModule, Symbol, IntPtr.Zero, 0); } internal static IntPtr dlopen(IntPtr lpFileName, uint Flags) { if (IntPtr.Size == 8) return UnixFastCall(dlopenAddr, lpFileName, Flags, IntPtr.Zero, 0); return ddlopen(lpFileName, Flags, IntPtr.Zero, 0); } public static IntPtr UnixFastCall(IntPtr Function, params object[] Paramters) { IntPtr[] Args = new IntPtr[Paramters.Length]; for (int i = 0; i < Paramters.Length; i++) { object Paramter = Paramters[i]; IntPtr NParamter = IntPtr.Zero; switch (Type.GetTypeCode(Paramter.GetType())) { case TypeCode.Boolean: NParamter = new IntPtr(((bool)Paramter) ? 1 : 0); break; case TypeCode.Byte: NParamter = new IntPtr((byte)Paramter); break; case TypeCode.SByte: NParamter = new IntPtr((sbyte)Paramter); break; case TypeCode.Int16: NParamter = new IntPtr((short)Paramter); break; case TypeCode.UInt16: NParamter = new IntPtr((ushort)Paramter); break; case TypeCode.Int32: NParamter = new IntPtr((int)Paramter); break; case TypeCode.UInt32: NParamter = new IntPtr((uint)Paramter); break; case TypeCode.Int64: NParamter = new IntPtr((long)Paramter); break; case TypeCode.UInt64: NParamter = new IntPtr(unchecked((long)(ulong)Paramter)); break; case TypeCode.Char: NParamter = new IntPtr((char)Paramter); break; case TypeCode.String: NParamter = Marshal.StringToHGlobalAnsi(((string)Paramter)); break; default: var TP = Paramter.GetType(); if (Paramter is IntPtr) NParamter = (IntPtr)Paramter; else if (Paramter is UIntPtr) NParamter = new IntPtr(unchecked((long)((UIntPtr)Paramter).ToUInt64())); else if (TP.IsValueType && !TP.IsEnum) { //Is Struct NParamter = Marshal.AllocHGlobal(Marshal.SizeOf(Paramter)); Marshal.StructureToPtr(Paramter, NParamter, false); } else throw new ArgumentException(Paramter.ToString() + " Can't be automatically parsed."); break; } Args[i] = NParamter; } return UnixFastCall(Function, Args); } public static IntPtr UnixFastCall(IntPtr Function, params IntPtr[] Paramters) { List<byte> Buffer = new List<byte>(); for (int i = 0; i < Paramters.Length; i++) { switch (i) { case -1: Buffer.AddRange(BitConverter.GetBytes(Paramters[i].ToUlong())); break; case 0: Buffer.AddRange(new byte[] { 0x48, 0xbf });//RDI goto case -1; case 1: Buffer.AddRange(new byte[] { 0x48, 0xbe });//RSI goto case -1; case 2: Buffer.AddRange(new byte[] { 0x48, 0xba });//RDX goto case -1; case 3: Buffer.AddRange(new byte[] { 0x48, 0xb9 });//RCX goto case -1; case 4: Buffer.AddRange(new byte[] { 0x48, 0xb8 });//R8 goto case -1; case 5: Buffer.AddRange(new byte[] { 0x48, 0xb9 });//R9 goto case -1; default: Buffer.AddRange(new byte[] { 0x48, 0xb8 });//RAX Buffer.AddRange(BitConverter.GetBytes(Paramters[i].ToUlong())); Buffer.Add(0x50);//push rax break; } } Buffer.AddRange(new byte[] { 0x48, 0xb8 });//RAX Buffer.AddRange(BitConverter.GetBytes(Function.ToUlong())); Buffer.AddRange(new byte[] { 0xFF, 0xE0 });//jmp RAX IntPtr TmpFunc = Marshal.AllocHGlobal(Buffer.Count); Write(TmpFunc, Buffer.ToArray(), Protection.PAGE_EXECUTE_READWRITE); Ret0 TmpDel = (Ret0)Marshal.GetDelegateForFunctionPointer(TmpFunc, typeof(Ret0)); IntPtr Result = TmpDel(); Marshal.FreeHGlobal(TmpFunc); return Result; } static IntPtr GetSymbol(string name) => GetSymbol(System.Diagnostics.Process.GetCurrentProcess().Handle, name); static IntPtr GetSymbol(IntPtr hProcess, string name) { unchecked { IntPtr ret = IntPtr.Zero; SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_PUBLICS_ONLY | 0x40000000); if (SymInitialize(hProcess, null, true)) { var si = new SYMBOL_INFO(); si.SizeOfStruct = 592; si.MaxNameLen = 512; IntPtr Addr = Marshal.AllocHGlobal(1024); Marshal.StructureToPtr(si, Addr, false); #if false if (SymFromName(hProcess, name, Addr)) { ret = Marshal.ReadIntPtr(Addr, 56); var str = Marshal.PtrToStringAnsi(new IntPtr((long)((ulong)Addr.ToInt64() + 84))); Console.WriteLine("SymFromName Sucess 0x{0:X8}, {1}", IntPtr.Size == 8 ? ret.ToInt64() : ret.ToInt32(), str); } else Console.WriteLine("SymFromName Failed"); #else if (SymFromName(hProcess, name, Addr)) ret = Marshal.ReadIntPtr(Addr, 56); #endif SymCleanup(hProcess); } return ret; } } static readonly int JmpSize = IntPtr.Size == 8 ? 12 : 5; static byte[] AssembleJump(IntPtr From, IntPtr Destination) { byte[] jmp = new byte[JmpSize]; if (IntPtr.Size == 8) { //x64 new byte[] { 0x48, 0xb8 }.CopyTo(jmp, 0); BitConverter.GetBytes(unchecked((ulong)Destination.ToInt64())).CopyTo(jmp, 2); new byte[] { 0xFF, 0xE0 }.CopyTo(jmp, 10); } else { //x86 jmp[0] = 0xE9; int Result = (int)(Destination.ToInt64() - From.ToInt64() - JmpSize); BitConverter.GetBytes(Result).CopyTo(jmp, 1); } return jmp; } static byte[] Read(IntPtr Address, uint Length) { byte[] Buffer = new byte[Length]; if (!ChangeProtection(Address, Buffer.Length, Protection.PAGE_EXECUTE_READWRITE, out Protection Original)) throw new Exception($"Falied to change the R/W memory permissions at {Address.ToInt64():X8}"); Marshal.Copy(Address, Buffer, 0, Buffer.Length); if (!ChangeProtection(Address, Buffer.Length, Original)) throw new Exception($"Falied to restore the memory permissions at {Address.ToInt64():X8}"); return Buffer; } static bool Write(IntPtr Address, byte[] Content, Protection? NewProtection = null) { ChangeProtection(Address, Content.Length, Protection.PAGE_EXECUTE_READWRITE, out Protection Original); uint Saved = (uint)Content.LongLength; Marshal.Copy(Content, 0, Address, Content.Length); if (NewProtection.HasValue) ChangeProtection(Address, Content.Length, NewProtection.Value); else ChangeProtection(Address, Content.Length, Original); if (Saved != Content.Length) return false; return true; } static bool ChangeProtection(IntPtr Address, int Range, Protection Protection, out Protection OriginalProtection) { return VirtualProtect(Address, Range, Protection, out OriginalProtection); } static bool ChangeProtection(IntPtr Address, int Range, Protection Protection) { return VirtualProtect(Address, Range, Protection, out _); } [DllImport("kernel32", SetLastError = true)] static extern bool VirtualProtect(IntPtr lpAddress, int dwSize, Protection flNewProtect, out Protection lpflOldProtect); [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr LoadLibraryW(string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] static extern IntPtr GetModuleHandle(string lpModuleName); [UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = true)] delegate uint GetModuleFileNameDel([In] IntPtr hModule, [Out] StringBuilder lpFilename, [In][MarshalAs(UnmanagedType.U4)] int nSize); static GetModuleFileNameDel dGetModuleFileName; static string GetModuleFileName(IntPtr hModule) { StringBuilder fileName = new StringBuilder(1024); if (dGetModuleFileName(hModule, fileName, fileName.Capacity) == 0) return null; return fileName.ToString(); } internal static ulong ToUlong(this IntPtr Pointer) => unchecked((ulong)Pointer.ToInt64()); [DllImport("dbghelp.dll")] static extern IntPtr SymSetOptions(uint SymOptions); [DllImport("dbghelp.dll")] static extern bool SymInitialize(IntPtr hProcess, string UserSearchPath, bool fInvadeProcess); [DllImport("dbghelp.dll")] static extern bool SymCleanup(IntPtr hProcess); [DllImport("dbghelp.dll")] static extern bool SymFromName(IntPtr hProcess, string Name, IntPtr Symbol); [DllImport("ntdll.dll")] static extern IntPtr LdrGetProcedureAddress(IntPtr hModule, IntPtr FunctionName, ushort Oridinal, out IntPtr FunctionAddress); [DllImport("kernel32.dll", SetLastError = true)] static extern void SetLastError(int ErrorCode); [DllImport("kernel32.dll", SetLastError = true)] static extern void SetLastError(uint ErrorCode); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct SYMBOL_INFO { public uint SizeOfStruct;//+0 public uint TypeIndex;//+4 public ulong ReservedA;//+8 public ulong ReservedB;//+16 public uint Index;//+24 public uint Size;//+28 public ulong ModBase;//+32 public uint Flags;//+40 public ulong Value;//+44 public ulong Address;//+52 public uint Register;//+60 public uint Scope;//+64 public uint Tag;//+68 public uint NameLen;//+72 public uint MaxNameLen;//+76 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)] public string Name;//+80 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct ANSI_STRING { public short Length; public short MaximumLength; public string Buffer; } [Flags] enum LoadLibraryFlags : uint { None = 0, DONT_RESOLVE_DLL_REFERENCES = 0x00000001, LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, LOAD_LIBRARY_AS_DATAFILE = 0x00000002, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100, LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800, LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400, LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 } enum Protection { PAGE_NOACCESS = 0x01, PAGE_READONLY = 0x02, PAGE_READWRITE = 0x04, PAGE_WRITECOPY = 0x08, PAGE_EXECUTE = 0x10, PAGE_EXECUTE_READ = 0x20, PAGE_EXECUTE_READWRITE = 0x40, PAGE_EXECUTE_WRITECOPY = 0x80, PAGE_GUARD = 0x100, PAGE_NOCACHE = 0x200, PAGE_WRITECOMBINE = 0x400 } }