Open process with debug privileges and read / write

Short version:

I am trying to open a process descriptor with debug privileges and determine a pointer that points to an object in debuggee memory.

Long version

I graduated from a computer science university in my last year of study and was given the task of creating an application that should be used for educational purposes for the next generation of students.

Why am I asking for help here, you ask? Well, the target platform is Windows, and I, unfortunately, do not know about WinAPI at all ...

Ok, here is the basic requirement:

  • Programming Language : C ++
  • Platform : Windows (7 Professional)
  • IDE Used : Visual Studio 2012
  • There are no additional libraries if they are not essential to facilitate development.

What will the application be used for?

Using this application, students must learn how to handle addresses, in this case static: the debuggee process will have some static pointers that lead to other pointers to form a multidimensional pointer. Students should find these base addresses using some debugging methods (which are not part of my work!) And try to find the values ​​at the end of these pointers.

My application will be used by teachers to randomly change values ​​and / or structures during the debuggee process.

Some searches gave the first answer: using ReadProcessMemory and WriteProcessMemory you can easily change the values ​​in the memory of another process without having to obtain debug privileges.

However, my teachers want to be able to define pointers (let say unsigned int) that should point to the memory space of the debuggee process, effectively preserving the base addresses that I wrote about earlier. They really want it, and I couldn't even talk about it, so I was stuck to do it at the end ...

And what exactly should work?

Well, I would complete my task if the following (pseudo) code works:

 grantThisProcessDebugPrivileges(); openAnotherProcessWhileItsRunning("targetProcess.exe"); unsigned int * targetValue = (unsigned int*) 0xDE123F00; // or even myCustomClass * targetClass = (myCustomClass*) 0xDE123F00; 

where the address 0xDE123F00 is in the targetProcess.exe memory space.

I know that this is possible, otherwise there would be no debuggers that could show this information.

What have I done so far (or tried ...)

Well, the thing is that I am really confused if I should activate debug privileges for my application before by opening the target process, doing it after opening, or rather by giving the target process these privileges.

So, I found an example on MSDN and tried to implement it:

  BOOL SetPrivilege( HANDLE hToken, // token handle LPCTSTR Privilege, // Privilege to enable/disable BOOL bEnablePrivilege // TRUE to enable. FALSE to disable ) { TOKEN_PRIVILEGES tp; LUID luid; TOKEN_PRIVILEGES tpPrevious; DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES); if(!LookupPrivilegeValue( NULL, Privilege, &luid )) return FALSE; // // first pass. get current privilege setting // tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = 0; AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpPrevious, &cbPrevious ); if (GetLastError() != ERROR_SUCCESS) return FALSE; // // second pass. set privilege based on previous setting // tpPrevious.PrivilegeCount = 1; tpPrevious.Privileges[0].Luid = luid; if(bEnablePrivilege) { tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED); } else { tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes); } AdjustTokenPrivileges( hToken, FALSE, &tpPrevious, cbPrevious, NULL, NULL ); if (GetLastError() != ERROR_SUCCESS) return FALSE; return TRUE; }; 

And basically:

 HANDLE mainToken; // I really don't know what this block of code does :< if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &mainToken)) { if (GetLastError() == ERROR_NO_TOKEN) { if (!ImpersonateSelf(SecurityImpersonation)) return 1; if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &mainToken)){ cout << GetLastError(); return 1; } } else return 1; } if (!SetPrivilege(mainToken, SE_DEBUG_NAME, true)) { CloseHandle(mainToken); cout << "Couldn't set DEBUG MODE: " << GetLastError() << endl; return 1; }; unsigned int processID = getPID("targetProcess.exe"); HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); if (hproc == NULL) { cout << "Couldn't open the process" << endl; return 1; }; unsigned int * theValue = (unsigned int*) 0xDE123F; 

Well, this code works without any errors, SetPrivilege returns TRUE, so I think it really set SE_DEBUG_NAME , which I think is the flag I need to set.

But after - for example - the dereferenced value of theValue is output, the application crashes with an access violation message that indicates that my approach is not working. I especially drew attention to starting VisualStudio Debugger with administrator privileges (otherwise SetPrivilege failed).

I'm really shameless, the fact that I don't know if setting SE_DEBUG_NAME right approach adds to my general confusion.

I hope you can help me :) My hands are connected with specific requests of the application, if you have ideas for achieving my goal, using the whole tidy approach, you can tell me about it, but I will not be able to introduce it to the authorities so that it will only add as far as I know: D

+6
source share
4 answers

From your description, it seems you have reached the point where you can open a process using SE_DEBUG. At the moment, you now have a handle to the target process.

What your code seems to be missing is using ReadProcessMemory.

First we need to take a look at the definition of ReadProcessMemory:

 BOOL WINAPI ReadProcessMemory( _In_ HANDLE hProcess, _In_ LPCVOID lpBaseAddress, _Out_ LPVOID lpBuffer, _In_ SIZE_T nSize, _Out_ SIZE_T *lpNumberOfBytesRead); 

This function gives you the ability to copy a block of memory from one process space to a process space. Therefore, you need to use this method to read the memory block, the size of the data structure that you want to read in your process space, then you can reinterpret the memory block as this data type.

So, the semi-pseudo code for reading an unsigned int from your target process is as follows:

 unsigned int ReadUInt(HANDLE process, const void * address) { // Add parameter validation unsigned char buffer[sizeof(unsigned int)] = {}; size_t bytesRead = 0; BOOL res = ::ReadProcessMemory(process, // The handle you opened with SE_DEBUG privs address, // The location in the other process buffer, // Where to transfer the memory to sizeof(unsigned int), // The number of bytes to read &bytesRead); // The number of bytes actually read if (!res) { // Deal with the error } if (bytesRead != sizeof(unsigned int)) { // Deal with error where we didn't get enough memory } return *reinterpret_cast<unsigned int *>(buffer); } 

Instead of this line:

 unsigned int * theValue = (unsigned int*) 0xDE123F00; 

You would do this:

 unsigned int theValue = ReadUInt(hproc, 0xDE123F00); 

Keep in mind that this requires you to know the size and layout of the memory types you are trying to read. Simple types that are contained in continuous memory can be obtained in a single call to ReadProcessMemory. Types containing pointers, as well as values, will require additional ReadProcessMemory calls to find the values ​​referenced by pointers.

+2
source

Each process has its own virtual address space. An address in one process makes sense only in this process. Removing a pointer reference in C ++ code will result in access to the virtual address space of the execution process.

When you removed the pointer into your code, you were actually trying to access memory in your process. No amount of desired action on the part of your teachers can make a back link to delete a pointer in another process.

If you want to read and write memory from other processes, you must use ReadProcessMemory and WriteProcessMemory.

I don’t think you really need to go all these lengths with tokens and privileges. If I remember correctly, you add the debug privilege, call OpenProcess, and go straight to it. And I think you can just skip adding privileges.

Some searches gave the first answer: using ReadProcessMemory and WriteProcessMemory, you can easily change the values ​​in the memory of another process without any need to obtain debug privileges. However, my tutors want to be able to define pointers (let them say unsigned int) that should point to the memory space of the debuggee process, effectively preserving the base addresses that I wrote about earlier. They really want it, and I couldn't even talk about it, so I was stuck to do it at the end ...

What they want is impossible. I suggest you tell them to better understand virtual memory before making impossible requirements!


@Cody Gray helps mention memory mapped files. If debuggee and debugger work together, they can use memory mapped files to share a common memory area. In this situation, both processes can map memory to their virtual address space and access it in the usual way.

I rather suggested that your debuggee was a reluctant victim, but if it is ready for collaboration, then shared memory may be an option.

Even then, you need to be careful with any pointers in this shared memory, because in the general case, the memory will map to different virtual addresses in each process.

+2
source

I think you are trying to access the kernel memory area and therefore the exception.

The user's land plots range from 0x00000000 to 7FFFFFFF, so try accessing this range, since all of the above is kernel space.

I assume you are on a 32 bit machine.

Check User Space and System Space (Microsoft Docs).

+1
source

You can create a type that behaves like a pointer by implementing the appropriate operators, just like shared_ptr does:

 foreign_ptr<int> ptr{0xDE123F00}; int a = *ptr; *ptr = 1; 
0
source

Source: https://habr.com/ru/post/947874/


All Articles