excaliber Posted December 17, 2004 Posted December 17, 2004 Hi all. This post is actually stemming from this conversation over at XVBT: http://www.xtremevbtalk.com/showthread.php?p=908999 I had PM'd Mike_R about some questions that went "deep" into .Net, to which he replied as a thread for others to see. I had also mentioned to him a piece of code I had found on the net (in C#.Net) that allowed pseudo-inline ASM. I converted it to VB.Net (being the language I'm most comfortable in), and had asked a few questions. He wasn't quite sure, so referred me here. The original code is from here: here Private MEM_COMMIT As UInt32 = Convert.ToUInt32(4096) Private PAGE_EXECUTE_READWRITE As UInt32 = Convert.ToUInt32(64) Private MEM_RELEASE As UInt32 = Convert.ToUInt32(32768) ' <DllImport("KERNEL32.DLL")> _ Private Shared Function VirtualAlloc(ByVal lpStartAddr As UInt32, ByVal size As UInt32, _ ByVal flAllocationType As UInt32, ByVal flProtect As UInt32) As UInt32 End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function VirtualFree(ByVal lpAddress As IntPtr, ByVal dwSize As UInt32, _ ByVal dwFreeType As UInt32) As Boolean End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function CreateThread(ByVal lpThreadAttributes As UInt32, ByVal dwStackSize As UInt32, _ ByVal lpStartAddress As UInt32, ByVal param As IntPtr, ByVal dwCreationFlags As UInt32, _ ByRef lpThreadId As UInt32) As IntPtr End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function CloseHandle(ByVal handle As IntPtr) As Boolean End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function WaitForSingleObject(ByVal hHandle As IntPtr, ByVal dwMilliseconds As UInt32) As UInt32 End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function GetModuleHandle(ByVal moduleName As String) As IntPtr End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function GetProcAddress(ByVal hModule As IntPtr, ByVal procName As String) As UInt32 End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function LoadLibrary(ByVal lpFileName As String) As UInt32 End Function ' <DllImport("KERNEL32.DLL")> _ Private Shared Function GetLastError() As UInt32 End Function ' <StructLayout(LayoutKind.Sequential)> _ Friend Structure PROCESSOR_INFO Public dwMax As UInt32 Public id0 As UInt32 Public id1 As UInt32 Public id2 As UInt32 Public dwStandard As UInt32 Public dwFeature As UInt32 Public dwExt As UInt32 End Structure ' ' ' Private Sub TestInline() Dim proc() As Byte = {85, 139, 236, 131, 236, 0, 83, 81, 82, 87, 139, 125, 8, 51, _ 192, 15, 162, 137, 7, 137, 95, 4, 137, 87, 8, 137, 79, 12, 184, 1, 0, 0, 0, 15, _ 162, 137, 71, 16, 137, 87, 20, 184, 0, 0, 0, 128, 15, 162, 61, 0, 0, 0, 128, _ 114, 10, 184, 1, 0, 0, 128, 15, 162, 137, 87, 24, 95, 89, 91, 90, 139, 229, _ 93, 51, 192, 194, 4, 0} ' Dim funcAddr As UInt32 = VirtualAlloc(Convert.ToUInt32(0), Convert.ToUInt32(proc.Length), MEM_COMMIT, PAGE_EXECUTE_READWRITE) Dim handle As New IntPtr(Convert.ToInt64(funcAddr)) ' Marshal.Copy(proc, 0, handle, proc.Length) ' Dim hThread As IntPtr = IntPtr.Zero Dim threadId As UInt32 = Convert.ToUInt32(0) Dim info As PROCESSOR_INFO = New PROCESSOR_INFO Dim pinfo As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(PROCESSOR_INFO))) Marshal.StructureToPtr(info, pinfo, False) hThread = CreateThread(Convert.ToUInt32(0), Convert.ToUInt32(0), funcAddr, pinfo, Convert.ToUInt32(0), threadId) ' WaitForSingleObject(hThread, Convert.ToUInt32(10000)) info = CType(Marshal.PtrToStructure(pinfo, GetType(PROCESSOR_INFO)), PROCESSOR_INFO) ' Marshal.FreeHGlobal(pinfo) CloseHandle(hThread) VirtualFree(handle, Convert.ToUInt32(0), MEM_RELEASE) End Sub As you can see, I had to do some changing around. VB.Net (for some unexplicable reason) does not like type coercion. For instance, it will not let you CType() or DirectCast() an UInt32 (or even a Int32 for that matter) into a IntPtr like C# will. Also, VB.Net doesn't seem to play nice with the unsigned numbers either (all the UIntXX types). For instance, on this line: Dim handle As New IntPtr(Convert.ToInt64(funcAddr)) The declaration will only allow signed numbers, so I manually forced the Unsigned Int32 into a a Signed 64 (which should make sure nothing is lost in the process from unsigned to signed). This particular line is also different from the original. Because of some limitations, I decided to declare a IntPtr and pass in the pointer value instead of direct casting (mentioned earlier) or Marshal.WriteIntXX methods. As to the explanation of how it works, here is what I *think* is going on. The byte array proc() contains the ASM opcodes and data to execute (you can find a disassembly of it here: http://www.tong-web.com/disassembly.txt). VirtualAlloc is called (imported method from Kernel32) which allocates a chunk of memory and returns the address of the base of that chunk. From there, we put the address into a IntPtr for .Net to use. Now we use the IntPtr to copy the data into the allocated chunk of memory. Next we make a few declarations (a new pointer for a thread) and declare an object to hold our return data (in this case a PROCESSOR_INFO object). It looks like we then allocate some unmanaged heap space for our ROCESSOR_INFO object, grab the base address of the object and store it into pinfo (IntPtr). And now for the real genius part. We use the Kernel32 CreateThread method to make a new child thread, passing it the pointer to our inline ASM (the array proc() ) as well as a pointer to our return object's allocated (but unused) heap space. We call a WaitForSingleObject to give the thread time to finish what it was doing, then use Marshal.PtrToStructure to convert the pinfo pointer (and the space allocated with it) back into a managed object. Lastly, we unallocated heap space and generally clean up. Now, some questions of my own. I know that data is passed into the thread (and therefore the ASM) via pinfo. But inside the ASM itself, where is the data located? Is it located at the base memory address for the thread? Is the pointer to the data stored in one of the registers? Conversly, how is the data returned? From the disassembly of the code, it looks like it stores it to memory then RET's a memory location (which is converted back into a managed structure later). Let me know if I have messed up this conversion from C# to VB.Net. When running, I got the same values in C# as I did in my converted code, but there could be a bug somewhere. Quote
excaliber Posted January 14, 2005 Author Posted January 14, 2005 Bump for interest. I'll let the thread die this time if there is no interest. Thanks :) Quote
Mike_R Posted February 26, 2005 Posted February 26, 2005 Huh, well this looks related as well, also from TheCodeProject: http://www.codeproject.com/vb/net/DotNetCompilerArticle.asp I really don't have any experience with Reflection, but this does look intriguing... Quote Posting Guidelines Avatar by Lebb
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.