-
Notifications
You must be signed in to change notification settings - Fork 135
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
64-bit support #5
Comments
The 64-bit support is my next target for MemorySharp. No ETA yet :) |
It shouldn't be too much work to do. at least for the memory side of things. Need a new struct for VirtualQueryEx. As for Fasm, spin up a 32 bit process and use IPC to ask the process to assemble the bytes (easier said than done) I'd raise a PR, but I only pulled in certain parts of the project and refactored them heavily -- so there is no easy way for me to add my contributions back to this project. Here is what I did. Essentially either populate the MemoryBasicInformation32 or MemoryBasicInformation64 struct depending on the environment, then just always return the 64 bit struct. If its a 32 bit process, just copy the data into the 64 bit struct manually.
|
Hey @zcanann, Interesting! I've searched a way to use fasm in x86-64 for long time and basically come to the conclusion that either we request to the developers of Fasm to provide a compatible code (even not worth trying) or we isolate the assembler in a separated process, as you suggested. I don't really like the idea of having an executable next to the library but we don't have a lot of choice in this situation. As you seem to use this way, did you notice some perf issues around IPC ? I think I'll go for a lightweight usage of WCF, as Microsoft was nice enough to provide this high-level API. :) Cheers |
I didn't notice any performance issues, but I only used it lightly (assembling < 50 bytes of data at a time very rarely) I tried desperately to get 64 bit to work, but some form of IPC looks like it was the only solution. I was also considering exploring Nasm and Masm to see if there was similar capabilities, but I gave up because I was going insane. I ended up going with WCF as well (or at least I think so -- Microsoft has so many damn IPC options). I'll post the relevant code from my project, just in case there are any pieces you want to use. Ended up with 3 projects. A main project, a shared library to define shared interfaces, and a 32 bit project. Pardon the PascaleCase in advance as well as the use of full structure names for primitives (ie Int32), I know these aren't the same convention as your project In the project FasmProxy I had 2 classes and 1 interface:
In the project FasmProxy32:
Then finally a static singleton class in the main project
|
Excellent @zcanann, many thanks for your code. No worries about the naming convention, you code is easy to read and well commented. I think this is a nice approach. In all the case, if it suffers from performance issues, I will add some caching in MemorySharp in order to keep high performance. From what I could read, some software need to heavily rely on assembly injection, which I would like to promote, as a out-of-process memory editing library. A side note about your code, I'm a bit concerned by the attribute ServiceBehavior, more specifically the parameter InstanceContextMode = InstanceContextMode.PerSession (to not confuse with ConcurrencyMode). With this parameter, we configure the server to have a thread per client. The consequence is when you have two clients, the server can handle their requests simultaneously. I've made a test with this configuration based on [this article](http://www.codeproject.com/Articles/89858/WCF-Concurrency-Single-Multiple-and-Reentrant-and#Instance mode = per session and Concurrency = single). After a bit of pimping in order to display when the connections start and end, I got the following result: As you can see, the server indeed manage multiple requests at the same time (multiple consecutive [start] flags). The issue here is the Fasm assembler is not thread-safe, meaning your hosted Fasm service can lead undefined results. I've tried with the parameter InstanceContextMode = InstanceContextMode.Single afterwards. As illustrated below, the server is handling request synchronously for all the clients this time: Thank you again @zcanann. I'll add a contributor file to reference everybody who help/helped me to design the library. :) Cheers, |
Nice catch, thanks for letting me know. And no problem, it's the least I can do to give back. |
@ZenLulz
Some things can make "in-process" C# much more interesting to the casual programmer, mostly the same things that attract external users, increased simplicity. Here are some of the main issues that turn people off about in-process c# code, and my solutions so far. -Injecting a C# app into a process. The answer to this is rather simple. If you review this project I updated https://github.com/lolp1/DomainWrapper you can see the process is simplified to injecting the DLL and calling the HostDomain export with the path to the C# program to host. Everything from WinForms, Console apps, and even class library's can be hosted easily. In the case of C# projects with out an "entry point", the injector can use an attribute [such as STAThread] to search for in reflection for the "entry" method to call when the c# domain is hosted. All you really need is to write a user-friendly injector. -Using pointers (both for delegates/functions and data) -Making use of in-process code (such as calling functions in the process via unmanaged function pointer delegates) in a thread-safe way. A little example of some of my syntax sugar.. internal class Function<T> where T : class
{
internal Function(IntPtr address)
{
Instance = address.ToDelegate<T>();
}
internal T Instance { get; }
internal void Execute(params object[] objects)
{
Process.Instance.Window.Invoke(Instance, objects);
}
internal TT Execute<TT>(params object[] objects)
{
return (TT) Process.Instance.Window.Invoke(Instance, objects);
}
} This lets you do stuff like this to invoke code inside the main thread in a safe way using the WndProc messages to communicate via a wrapper that makes use of a queue and the WndProc override linked. private Function<IsAutoTrackingDelegate> _isSomethingTrueFunction;
// _isSomethingTrueFunction = new Function<IsSomethingTrue>(IntPtr.Zero);
// bool isItTrue = _isSomethingTrueFunction.Execute<bool>((IntPtr)0x500);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate bool IsSomethingTrue(IntPtr thisSomething); -Making use of hooks using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace TestApp
{
internal class Program
{
internal static readonly List TimeStamps = new List();
internal static readonly Process Process = Process.GetCurrentProcess();
private static D3DHook _d3DHook;
private static void Main(string[] args)
{
_d3DHook = new D3D9(new DetourManager(Process.Handle));
_d3DHook.Initialize();
_d3DHook.Frame += D3DHookOnFrame;
}
private static void D3DHookOnFrame(object sender, EventArgs eventArgs)
{
TimeStamps.Add(DateTime.Now);
}
}
} There is more, but if you ask me, using a well written user-friendly C# injector and a well-designed C# library that supports in-process can make in-process stuff a lot more attractive and simple. Being entirely done in C# while doing that is a nice bonus. Aside from that.. I supported x64 in my memory sharp clone by turning everything into interfaces that will be used forever, and implementing anything that broke as I went on the fly. I had the same issue with bringing the ASM code execution/injection support MemorySharp brought in x32 to x64. In the end I just went full C# in-process, but in my efforts, I had the same solutions thought of here. I used a third party to handle assembling things. Microsoft has great built in IPC stuff as well as amazing interop with c# in general. I'd love to see an effort to bring MemorySharp to the in-process world along side its out-of-process world that is already mostly built. |
Hey @lolp1, The main reason of being out-of-process is to reduce the footprint of memory editing in the targeted process, keeping it as low as possible and have the full control of what is altered in the target process. This is why I oriented MemorySharp to be out-of-process. As soon as you end up injecting managed code in a process, you require to start the CLR runtime in that process. That is not a bad practice by essense of course, nonetheless, this can restrain developers to use it in several scenarios, where the target process is wise enough to detect such modifications. The code you posted is very interesting. I'm going to have a look to the abstraction you used in your clone of the library. :) Most of the stuff (detour, hooking, etc.) can also be achieved while being out-of-process, without having the users of the library to write assembly code. That's obviously more work in the library and much more interesting to do. :D MemorySharp was initially written to be efficient as an out-of-process library and the implementation of memory management/type conversion was thought in that way. A lot of stuff in that library should be rewritten differently to make it worth using MemorySharp in a context of in-process. Obviously, those changes would be only applicable if the library is in-process. This is why I'm not so keen to inject MemorySharp in the target process. I'll add you in Skype this week-end. :) Cheers |
Sounds good :) I have more thoughts but of course this place is not the best for such communication. [email protected] is my skype :) add that one when you have a chance. |
When will 64-bit support come? |
I created the branch x86-64 to work on this feature. This enables you to follow the progress as well. Cheers |
Side note for me. PEB 32 and 64-bit supportPEB structures vary depending on the OS and architecture. A research around all the PEB from XP is available here: http://blog.rewolf.pl/blog/?p=573. This leads to the Terminus Project, that enables us to compare Windows structures here: http://blog.rewolf.pl/blog/?p=1438. The PEB structures on Terminus Project can be found here: http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_combined.html. MemorySharp will certainly only maintain the independent OS fields. TEB 32 and 64-bit supportSimilar to the PEB, the TEB's are available here: http://terminus.rewolf.pl/terminus/structures/ntdll/_TEB_combined.html |
What about the workings for 64 bit support? |
This is in progress ! :) |
I want to help but I know little of c# language. |
Any update/ETA on this milestone for the project? |
Just so people know @ZenLulz is a professional dev who does this for a living and AFAIK this is a bit of a hobby project that he takes great pride in it being quality work so it takes some time for releases. In the meantime, feel free to suggest anything you want in MemorySharp and I'll do my best to get some of that done in ways that meets the standards ZenLulz desires and pull request it. You may also check out my spin-off (FULL CREDITS to @ZenLulz for my project too!) in the mean-time which does support x64. |
Was this ever finalized? I tried using the files built from the x86-64 and no matter what I do I keep getting 'Couldn't get the information from the thread, error code '-1073741820'.' |
@KairuByte That error is due to an issue in his structures with x32-64, will look into fixing it later and pull requesting. |
@lolp1 I still have this error, hasn't this been fixed yet? |
I too have the error code '-1073741820'.' one year later, this is sad :( |
For those interested, I have 64-bit support working for some features (just read/write and the x86/x64 assembler) https://github.com/Squalr/Squalr This project was never really meant to be used as a library though -- but it may be possible to repurpose it for that. EDIT: My backend has been made available via NuGet |
Thanks, but I need to inject my c# dll to another unmanaged process. For read / write there already many libs. RIP |
@zcanann I want to execute calls on x64 remote process , is that possible&how ? |
Hey @MohamedAlaaJameel, The branch deepening-project implements some support for x64 calling convention, notably here: https://github.com/JamesMenetrey/MemorySharp/blob/deepening-project/src/MemorySharp/Assembly/CallingConvention/MicrosoftX64CallingConvention.cs. Feel free to go to that branch, compile the library and check to see if that fits your needs :) |
A sidenote for everyone here, I'm still planning in revamping this library with the latest C# features (e.g., span, etc.). :) |
I have compiled the library , but I get these errors / on 32-Compile : can't open 64 bit proc from 32 . |
@MohamedAlaaJameel Try to force your .NET app to be compiled as 64-bit. This is usually done in Visual Studio by changing AnyCPU into 64bit. |
@JamesMenetrey |
Great! To answer your other question: I have dropped Fasm for Keystone for 64-bit assembler support :) |
Off-topic, but I forgot your discord name could you throw me a private message over on there :D? |
MR @JamesMenetrey
|
@MohamedAlaaJameel Convert/cast the IntPtr values to Long or ULong. About a year ago, I wrote my own 32-bit and 64-bit definitions of these structs. Instead of For structs with definitions dependent on bit-length, I wrote a managed definition (e.g. public class ProcessBasicInformation
{
public ProcessBasicInformation(PROCESS_BASIC_INFORMATION pbi)
{
ExitStatus = pbi.ExitStatus;
unsafe { PebBaseAddress = Environment.Is64BitProcess ? (null, (ulong)pbi.PebBaseAddress) : ((uint)pbi.PebBaseAddress, null); }
AffinityMask = Environment.Is64BitProcess ? (null, (ulong)pbi.AffinityMask) : ((uint)pbi.AffinityMask, null);
BasePriority = pbi.BasePriority;
ProcessId = pbi.ProcessId;
ParentProcessId = pbi.ParentProcessId;
}
public ProcessBasicInformation(PROCESS_BASIC_INFORMATION32 pbi)
{
ExitStatus = pbi.ExitStatus;
PebBaseAddress = (pbi.PebBaseAddress, null);
AffinityMask = (pbi.AffinityMask, null);
BasePriority = pbi.BasePriority;
ProcessId = pbi.UniqueProcessId;
ParentProcessId = pbi.InheritedFromUniqueProcessId;
}
public ProcessBasicInformation(PROCESS_BASIC_INFORMATION64 pbi)
{
ExitStatus = pbi.ExitStatus;
PebBaseAddress = (null, pbi.PebBaseAddress);
AffinityMask = (null, pbi.AffinityMask);
BasePriority = pbi.BasePriority;
ProcessId = (uint)pbi.UniqueProcessId;
ParentProcessId = (uint)pbi.InheritedFromUniqueProcessId;
}
public (UIntPtr32<PEB32>? w32, UIntPtr64<PEB64>? w64) PebBaseAddress { get; }
public ProcessEnvironmentBlock? ProcessEnvironmentBlock { get; private set; }
public NTSTATUS ExitStatus { get; }
public (uint? w32, ulong? w64) AffinityMask { get; }
public KPRIORITY BasePriority { get; }
public uint ProcessId { get; }
public uint ParentProcessId { get; }
/// <summary>Read the process's private memory to recursively copy the PEB.</summary>
/// <param name="hProcess">A handle opened with <see cref="PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ"/>. Requires Debug and/or admin privileges.</param>
/// <exception cref="AccessViolationException">Read operation failed; The memory region is protected and Read access to the memory region was denied.</exception>
/// <exception cref="NullReferenceException">Unable to copy PEB; The 32-bit and 64-bit pointers are both null.</exception>
/// <exception cref="NTStatusException">NtWow64ReadVirtualMemory failed to copy 64-bit PEB from target process; (native error message)</exception>
/// <exception cref="Exception">ReadProcessMemory failed; (native error message)</exception>
public unsafe ProcessEnvironmentBlock GetPEB(SafeProcessHandle hProcess)
{
if (PebBaseAddress is (null, null))
throw new NullReferenceException("Unable to copy PEB; The 32-bit and 64-bit pointers are both null.");
using SafeBuffer<PEB64> buffer = new(numElements: 2); // We only use the type for allocation length. It's large enough for either PEB64 or PEB32.
if (!Environment.Is64BitProcess && PebBaseAddress.w64 is not null)
{
ulong bytesRead64 = 0;
NTSTATUS status;
if ((status = PInvoke.NtWow64ReadVirtualMemory64(hProcess, (UIntPtr64)PebBaseAddress.w64, (void*)buffer.DangerousGetHandle(), buffer.ByteLength, &bytesRead64)).Code is Code.STATUS_PARTIAL_COPY)
throw new AccessViolationException("NtWow64ReadVirtualMemory64 failed; The memory region is protected and Read access to the memory region was denied.", new NTStatusException(status));
else if (status.Code is not Code.STATUS_SUCCESS)
throw new NTStatusException(status, "NtWow64ReadVirtualMemory failed to copy 64-bit PEB from target process; " + status.Message);
else
return ProcessEnvironmentBlock = new ProcessEnvironmentBlock(buffer.Read<PEB64>(0));
}
else
{
nuint bytesRead = 0;
if (PebBaseAddress.w32 is not null && PInvoke.ReadProcessMemory(hProcess, (void*)PebBaseAddress.w32, (void*)buffer.DangerousGetHandle(), (nuint)buffer.ByteLength, &bytesRead))
{
return ProcessEnvironmentBlock = new(buffer.Read<PEB32>(0));
}
else if (PebBaseAddress.w64 is not null && PInvoke.ReadProcessMemory(hProcess, (void*)PebBaseAddress.w64, (void*)buffer.DangerousGetHandle(), (nuint)buffer.ByteLength, &bytesRead))
{
return ProcessEnvironmentBlock = new(buffer.Read<PEB64>(0));
}
else
{
Win32ErrorCode err = (Win32ErrorCode)Marshal.GetLastPInvokeError();
if (err is Win32ErrorCode.ERROR_PARTIAL_COPY)
throw new AccessViolationException("ReadProcessMemory failed; The memory region is protected and Read access to the memory region was denied.", new Win32Exception(err));
else
throw new Exception("ReadProcessMemory failed; " + err.GetMessage(), new Win32Exception(err));
}
}
} Some relevant definitions and
|
When it will support 64bit processes?
The text was updated successfully, but these errors were encountered: