ufrisk / MemProcFS

MemProcFS
GNU Affero General Public License v3.0
2.88k stars 354 forks source link

Compatible tools? #22

Closed olegyadrov closed 4 years ago

olegyadrov commented 4 years ago

Hi, I don't have any expertise in reverse engineering and memory analysis fields, but I'm interested in learning this stuff, and my main question is about the compatible tooling that can be used with FPGA configuration. The project descriptions says "Use your favorite tools to analyze memory - use your favorite hex editors, your python and powershell scripts, WinDbg or your favorite disassemblers and debuggers - all will work trivally with MemProcFS by just reading and writing files!", but I read discussion on the issue #9 and found out that, as I expected, debugger functionality is seriously limited, as you can't set breakpoints, thus even if you manage to figure out the address of a value, you can't simply check what code modifies it. But first, of course, you need to somehow know the right address, and I am wondering if any memory scanner tools compatible with MemProcFS exist at all? I am looking for functionality similar to that Cheat Engine provides, where you can select a process, a value type, and do a serie of scans to filter out wrong values. If there is currently no such a tool, I could create one, as I have experience with Python and Qt framework that has a Python binding; I looked at vmmpy.py, and it should be more or less trivial to create a memory scanner app using provided API, but I would not like waste my time if something like this is already out there. My other question is about kernel drivers: I know already that PCILeech allows you to load arbitrary kernel modules, but can I manipulate the drivers? More specifically, I would like to be able to load unsigned driver and unload any kernel drivers, even those that the operating system does not allow to unload (though I am not even sure if it's technically feasible, I suppose it could lead to a BSOD). The usecase I am thinking about is ability to unload a driver that constantly keeps an open IO request to prevent being unloaded (some anticheat drivers do that). The third question is about code injection functionality. Does PCILeech/MemProcFS allow to inject and execute code into a process with selected pid? I think the answer to this one is no; Again, I just started to learn x86 Assembly, but given that you have access to the stack, I think it should be possible to do a remote code injection, but I'm not sure about the exact steps. And, of course, it would be very handy to have this feature out of the box. Thanks a lot for reading this, any advice is much appreciated!

ufrisk commented 4 years ago
  1. WinDBG works with MemProcFS, but as you mention actively setting breakpoints do not; so it's more useful in analysis scenarios. It would be very hard to get it to work over DMA, and in some scenarios outright impossible.

  2. A lot of default utilities working on files directly for obvious reasons work quite well; if you want to do strings or similar on a process for example. For more specialized tools I'm aware that various cheats do work with it (even if not supported by me).

  3. I had functionality for quite a while ago which allowed you to load unsigned kernel drivers from disk from the target system. It patched the signature verification check, loaded the driver and then restored the check. I haven't tested that for a while, but you should be able to find the source code for it in the PCILeech project. Chances are that it might not work with the most recent Win10 though. To load drivers without touching disk would require you to have some kind of loader in shellcode - it would be feasible, but I have not looked into it.

  4. PCILeech/MemProcFS allows you to kind of write to already allocated process virtual memory, but not directly allocate new virtual memory. The writes will happen to underlying physical memory, so if multiple processes use the same .dll/.exe a write will affect all processes. I'm not likely to support allocation of memory directly via MemProcFS. The way to go would be to insert a small shellcode with DMA that allocates the required memory and spawn a thread. Since the initial shellcode would affect multiple processes some kind of PID and Atomicity check would be required in there.

  5. The stack would be an obvious point to allow code injection. Please keep in mind that the stack is likely to be in a lot of flux though; and MemProcFS read/write would be millions of times slower than the topmost stack operations... Another way could be to patch some kind of function with a small trampoline to some shellcode in a code cave...

If you're interested in some integrations I'd be very happy to add that plugin and/or do some promotion for it. I've gotten similar contributions for the awesome pypykatz plugin which provides mimikatz style functionality. You may code plugins in either Python or C/C++. If you would need more features in the core functionality that the API provides please let me know and I'll add it if it's reasonable work for me.

About changing core functionality (non-API functionality) please do check with me before starting to work on something. I don't want to get in a situation with you doing coding for something that I might not accept a pull request for.

Also, If you're looking into doing a more specialized plugin for cheating I won't be able to add it or promote it though. I know my projects are used for cheating and that's the way how things are; but I won't actively promote it.

olegyadrov commented 4 years ago

Thanks for a quick response!

The stack would be an obvious point to allow code injection. Please keep in mind that the stack is likely to be in a lot of flux though;

Yeah, but what if I suspend the target process prior to modifying the stack? That would help, right?

I'm not likely to support allocation of memory directly via MemProcFS.

Why not? To me it seems as an essential feature that this kind of software should have. (I am not trying to talk you into supporting it, just sharing my opinion and curious about your view)

Since the initial shellcode would affect multiple processes some kind of PID and Atomicity check would be required in there.

I don't quite understand why it would affect multiple processes. Also, if I were to consider adding code injection functionality, I would probably start from requiring suspension of the target process to simplify things. It would be a reasonable requirement given the complexity of the task (limited support would be better than no support).

If you're interested in some integrations I'd be very happy to add that plugin and/or do some promotion for it. ... Also, If you're looking into doing a more specialized plugin for cheating I won't be able to add it or promote it though.

It would not be a specialized plugin for cheating. In fact, it would not be a plugin at all, but a standalone GUI application that would make search of values of interest easier. See, most memory scanners can do search in files, but only in one file at a time; with MemProcFS you need to search in bunch of files which makes them not suitable for this job.

ufrisk commented 4 years ago
  1. I'm not aware of any way to suspend a process using DMA. For that you would already need to have code execution on the target system already I think. Or if you know a way of doing it by just writing some bytes to memory I would be super interested in knowing how :) I guess it's possible to make it rely on a PCILeech kernel module on the target system to do the work, but then you won't need MemProcFS anyway, just do the coding inside the injected kernel shellcode.

  2. Reason for not supporting allocation of memory in MemProcFS is that the Windows Kernel Memory Manager is super complex, memory structures are in a constant flux and might be changed/updated thousands of times worst case just in between a single DMA read/write cycle.

  3. exe's and dll's and other executable code are most often loaded as images. As an example the kernel32.dll which exists in every process only exist in one spot in RAM. The executable code in Kernel32.dll is shared between all processes in physical memory. I'm only able to write to actual physical memory (I do virtual->physical translation first and then write to the physical memory). To change this I would need to be able to Allocate/Modity virtual memory over DMA as per 2. If you should happen to find some piece of executable code that is mapped as "process private" then it would be quite easy; but such memory is quite rare nowdays.

I think MemProcFS would be really nice for your project. It's also multi-threaded by design so it should be possible to parallelize such tasks quite easy to have a good performance :) Looking forward to seeing the result, and please let me know if you need any enhancements for the API. Allocation of virtual memory would be very complex if doing it properly though.

olegyadrov commented 4 years ago

Okay, thanks a lot for your input, that definitely helped!

olegyadrov commented 4 years ago

I created a prototype using Python (https://github.com/olegyadrov/RemoteMemoryScanner) and got to the point when I tried to use it to find an integer value in a running videogame which uses over 9 GB of RAM, and it took way too long. Because Python sucks when it comes to multithreading, I am rewriting everything from scratch in C++ now. I have some questions about VMMDLL_ProcessGetInformation function:

  • Retrieve various process information from a PID. Process information such as
  • name, page directory bases and the process state may be retrieved.
  • -- dwPID
  • -- pProcessInformation = if null, size is given in *pcbProcessInfo
  • -- pcbProcessInformation = size of pProcessInfo (in bytes) on entry and exit
  • -- return = success/fail.
  1. In what situation pProcessInformation can be null?
  2. If pProcessInformation is null, how knowing size will help me?
  3. What is the meaning of pcbProcessInformation at all? pProcessInformation is a pointer to a structure; how can size of a pointer or size of a structure not be constant?
ufrisk commented 4 years ago

interesting, I'll take a look at the tool tomorrow :)

The API is just somewhat over-engineered; right now it should be perfectly fine to do this:

BOOL result;
DWORD dwPID = 4;
VMMDLL_PROCESS_INFORMATION Info = { 0 };
SIZE_T cbInfo = sizeof(VMMDLL_PROCESS_INFORMATION);
Info.magic = VMMDLL_PROCESS_INFORMATION_MAGIC;
Info.wVersion = VMMDLL_PROCESS_INFORMATION_VERSION;
result = VMMDLL_ProcessGetInformation(dwPID, &Info, &cbInfo);

logic here is to make the function more similar to other similar functions, and also to allow variable length data in the future. but right now it's just a bit over-engineered.


but to answer the questions,

  1. set pProcessInformation to NULL when you wish to retrieve the size required to hold the struct. currently this will always return sizeof(VMMDLL_PROCESS_INFORMATION); so it's a bit meaningless at the moment.

  2. it won't at the moment.

  3. sometimes I add variable length data right after the structure, with pointers in the structure into this data. the VMMDLLProcessMap* functions are a prime example of this, so length can be variable; not in VMMDLL_ProcessGetInformation right now though.

olegyadrov commented 4 years ago

Oh, I see, I misunderstood the idea and was under impression that in certain circumstances the function would set pProcessInformation to null. Okay, but now I'm confused by your code snippet. Is it necessary that I set Info.magic and Info.wVersion before I call VMMDLL_ProcessGetInformation?

ufrisk commented 4 years ago

yes, as it's coded right now it's required.