Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about mixing MemorySharp and P/Invoke method #27

Open
smardine opened this issue Dec 8, 2022 · 5 comments
Open

Question about mixing MemorySharp and P/Invoke method #27

smardine opened this issue Dec 8, 2022 · 5 comments

Comments

@smardine
Copy link

smardine commented Dec 8, 2022

    HI, sorry for having posting on the wrong repo.

I modify my code to use MemorySharp instead.
Here is the code:

internal static string CPUIIDMemorySharp()
        {
            var sharp = new MemorySharp(System.Diagnostics.Process.GetCurrentProcess());

            using (var memory = sharp.Memory.Allocate(24))
            {
                AssemblyTransaction fasmNet;
                using (fasmNet = sharp.Assembly.BeginTransaction(memory.BaseAddress))
                {
                    fasmNet.AddLine("use32"); //Tell FASM.Net to use x86 (32bit) mode
                    fasmNet.AddLine("PUSH EBX"); //{Save affected register}
                    fasmNet.AddLine("PUSH EDI");
                    fasmNet.AddLine("MOV EDI, EAX"); //{@Resukt}
                    fasmNet.AddLine("MOV EAX, 1");
                    fasmNet.AddLine("DW $A20F"); //CPUID Command}
                    fasmNet.AddLine("STOSD");  //{ CPUID[1]}
                    fasmNet.AddLine("MOV EAX, EBX");
                    fasmNet.AddLine("STOSD"); //{CPUID[2]}
                    fasmNet.AddLine("MOV EAX, ECX");
                    fasmNet.AddLine("STOSD"); //{CPUID[3]}
                    fasmNet.AddLine("MOV EAX, EDX");
                    fasmNet.AddLine("STOSD"); //{CPUID[4]}
                    fasmNet.AddLine("POP EDI"); //{Restore registers}
                    fasmNet.AddLine("POP EBX");
                    fasmNet.AddLine("RETN");  // in cdecl calling convention, return value is stored in EAX; so this will return both params added up
                }

                var myAssemblyFunction = Marshal.GetDelegateForFunctionPointer<CpuIDDelegate>(memory.BaseAddress);
                CpuIdResult result = myAssemblyFunction(1);              
                int cpuid1 = result.Eax;
                int cpuidpluscomplement = cpuid1 & 0x0FFF7FFF;
                string converted = cpuidpluscomplement.ToString("X8");
                return converted;
            }

I end with the same problem, if i execute this code first, i have a value in

CpuIdResult result = myAssemblyFunction(1);  
int cpuid1 = result.Eax;

If i made a call to a P/Invoke method first, the struct CpuIdResult has all this properties to 0.
When i first call MemorySharp
image

When i call a P/Invoke method before calling MemorySharp:
image

Here is the modified example /repro project.
ConsoleApp1.zip

I'm pretty sure i'm missing something but what...
I hope you will have the time to take a look, and maybe a lead for me

Originally posted by @smardine in #26 (comment)

@smardine smardine changed the title HI, sorry for having posting on the wrong repo. Question about mixing Fasm.net and P/Invoke method with MemorySharp Dec 8, 2022
@smardine smardine changed the title Question about mixing Fasm.net and P/Invoke method with MemorySharp Question about mixing MemorySharp and P/Invoke method Dec 8, 2022
@JamesMenetrey
Copy link
Owner

JamesMenetrey commented Dec 12, 2022

Hey,

I'm unsure if you can call GetDelegateForFunctionPointer with a delegate that returns a complex structure.

Could you please try to modify the delegate type to return an IntPtr and check if the return value is reliably filled in with the value of EAX?

Cheers

@smardine
Copy link
Author

Hello and thanks for your time :)
I have modified my delegate as suggested.

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr CpuIDDelegatePtr(int level);

Now, i have a result, but it is incomplete.
I'll explain myself.
What i'm trying to do is making an equivalent of the wmi request to find "Processor id"
Why i do that: because the wmi request did not work on virtual machine (return string.Empty).

public static string GetCPUID()
       {
           string cpuid = string.Empty;
           try
           {
               using (var win32Proc = new ManagementObjectSearcher("Select ProcessorID From Win32_processor"))
               {
                   foreach (ManagementObject obj in win32Proc.Get())
                   {
                       return obj["ProcessorID"].ToString();
                   }
               }
               return cpuid;
           }
           catch
           {
               return string.Empty;
           }
       }

This call return "BFEBFBFF000906EA" where the Memory sharp call with the ptr modification return only the first part (BFEBFBFF)

image

At this point, if i try to marshal the IntPtr to my result struc, i have the following erreor message:
image

I have updated the sample project in attachment.
ConsoleApp1.zip

Hope this can help

@JamesMenetrey
Copy link
Owner

JamesMenetrey commented Dec 13, 2022

Indeed, on 32-bit systems, registers are 4-byte long. Since you need more than 4 bytes, you will need to pass the pointer of a buffer as an argument where you can freely assign values.

Modify one more time your delegate to receive a pointer of unmanaged memory. The type of this parameter will be IntPtr and will be allocated using sharp.Memory.Allocate(Marshal.SizeOf<CpuIdResult>()). Modify your assembly code to store the values you are interested in this allocated buffer. Make sure to respect the order of the fields of your structure CpuIDDelegatePtr when assigning the memory from your assembly code.

Finally, when the call of myAssemblyFunction returns, you can read the values using Read<CpuIDDelegatePtr>() onto that previously-allocated memory.

EDIT: Use CpuIdResult as a generic type argument for SizeOf.

@smardine
Copy link
Author

Ok, according to what you says, i 've made the following modifications :

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
 private delegate IntPtr CpuIDDelegatePtr2(IntPtr level);
 internal static string CPUIIDWithPtrAsresultMemorySharp()
        {
            using (var sharp = new MemorySharp(System.Diagnostics.Process.GetCurrentProcess()))
            using (var memory = sharp.Memory.Allocate(24))
            {
                AssemblyTransaction t;
                using (t = sharp.Assembly.BeginTransaction(memory.BaseAddress, autoExecute: true))
                {
                    t.AddLine("use32"); //Tell FASM.Net to use x86 (32bit) mode
                    t.AddLine("PUSH EBX"); //{Save affected register}
                    t.AddLine("PUSH EDI");
                    t.AddLine("MOV EDI, EAX"); //{@Resukt}
                    t.AddLine("MOV EAX, 1");
                    t.AddLine("DW $A20F"); //CPUID Command}
                    t.AddLine($"STOSD");  //{ CPUID[1]}
                    t.AddLine("MOV EAX, EBX");
                    t.AddLine("STOSD"); //{CPUID[2]}
                    t.AddLine("MOV EAX, ECX");
                    t.AddLine("STOSD"); //{CPUID[3]}
                    t.AddLine("MOV EAX, EDX");
                    t.AddLine("STOSD"); //{CPUID[4]}
                    t.AddLine("POP EDI"); //{Restore registers}
                    t.AddLine("POP EBX");
                    t.AddLine("RET");  // in cdecl calling convention, return value is stored in EAX; so this will return both params added up

                }
                var ptrStruct = sharp.Memory.Allocate(Marshal.SizeOf<CpuIdResult>());
                var myAssemblyFunction = Marshal.GetDelegateForFunctionPointer<CpuIDDelegatePtr2>(memory.BaseAddress);

                IntPtr executeResult = myAssemblyFunction(ptrStruct.BaseAddress);
                CpuIdResult readedValue = executeResult.Read< CpuIdResult>();
                int cpuid1 = readedValue.Eax;
                int cpuidpluscomplement = cpuid1 & 0x0FFF7FFF;
                string converted = cpuidpluscomplement.ToString("X8");
                return converted;
            }
        }

please note that the method sharp.Memory.Allocate(Marshal.SizeOf()); is based on CpuIdResult and not on CpuIDDelegatePtr because Marshal don't want to marshal this type.
image

As you can see, the result is the same than with the previous implementation
image

But, now, when i call Read onto that previously-allocated memory, it throw an exception:
image

in your comment, you said

Modify your assembly code to store the values you are interested in this allocated buffer

Maybe it is the part i'm missing.

Thanks again for your time.

@JamesMenetrey
Copy link
Owner

please note that the method sharp.Memory.Allocate(Marshal.SizeOf()); is based on CpuIdResult and not on CpuIDDelegatePtr because Marshal don't want to marshal this type.

Correct, I meant to evaluate the size of CpuIdResult, as you have done.

The value to read as CpuIdResult is the unmanaged buffer you allocated, called ptrStruct, not the returned value. By the way, you can change the return type of your delegate to void, since we no longer require it, you will use the buffer to store the value of the registers.

Maybe it is the part i'm missing.

This is also something that is needed. You have allocated a buffer of memory that you passed to your assembly code. You now need to save the values you are interested in that buffer. This requires you to modify your assembly code to write the CPUID values inside that region of memory. Once your assembly code returns, you will use MemorySharp to read that filled buffer into your managed code, using that Read call.

Said otherwise, you need a temporary memory buffer to store more than 4 bytes. This is your variable ptrStruct. Obviously, this buffer won't be magically populated, you need to do it in your assembly code. Once populated and your assembly code returned, reading that area of memory using Read will enable you to read the values you copied in your assembly code.

Another point regarding the Read function: this function attempts to interpret a raw buffer of bytes into a managed type (e.g., CpuIdResult). There is no magical behavior that will extract the value of the CPU registers on itself, regardless of the name of the fields of the structure. It's up to your assembly code to map the registers to the corresponding fields of your structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants