Link to home
Start Free TrialLog in
Avatar of rmangino
rmangino

asked on

Page protection in another process

I am working on an NT 5 (2128) machine and cannot seem to get VirtualProtectEx/WriteProcessMemory working correctly.  I need to make some changes in ntdll.dll in a process that I have called CreateProcess(... SUSUPENDED...) on.

This is what I am doing:

MEMORY_BASIC_INFORMATION mbi_thunk ;
VirtualQueryEx ( ppi->hProcess,
                 (LPVOID)pvLoc,
                 &mbi_thunk,
                 sizeof ( MEMORY_BASIC_INFORMATION )  );

VirtualProtectEx( ppi->hProcess,
                  mbi_thunk.BaseAddress,
                  mbi_thunk.RegionSize,
                  PAGE_READWRITE,
                  &mbi_thunk.Protect );

   if ( !WriteProcessMemory( ppi->hProcess, (LPVOID)pvLoc,
                             (LPVOID)pvBuffer, sizeof(DWORD), &dwWritten ) )
   {
      printf("WriteProcessMemory failed: 0x%X\n", GetLastError() );
      return false;
   }

The WriteProcessMemory always fails with: 0x3E6 - "Invalid access to memory location."

Even though I didn't show it above, the VirtualQueryEx and VirtualProtectCalls both succeed.

Any clues as to what I am doint wrong here?

TIA
Avatar of rmangino
rmangino

ASKER

Edited text of question.
ASKER CERTIFIED SOLUTION
Avatar of NickRepin
NickRepin

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Use the following (call to VirtualQueryEx is not necessary):

VirtualProtectEx( ppi->hProcess,
                  pvLoc,
                  sizeof(DWORD),
                  PAGE_READWRITE,
                  &oldProtect );

WriteProcessMemory ...
May be, I'm wrong regarding VirtualQueryEx, but anyway this call is not necessary, so please try the code above.
Actually, it should work with VirtualQueryEx also. Check again that calls to Virtual..() are successful.
Here is the test program, it works fine on my NT.

#include <windows.h>
#include <iostream.h>

void main(void)
{
   STARTUPINFO si;
   memset(&si,0,sizeof(si));
   si.cb=sizeof(si);
   PROCESS_INFORMATION pi;
   if(CreateProcess(0,"a.exe",0,0,FALSE,CREATE_SUSPENDED,0,0,&si,&pi)) {
      char buf[16];
      LPVOID p=LPVOID(0x400001); // I know that 0x400000 is the base addr of process a.exe
      DWORD written;
      cout<<VirtualProtectEx(pi.hProcess,p,sizeof(DWORD),PAGE_READWRITE,
         &written)<<endl;
      // This call fails w/o previous one
      cout<<WriteProcessMemory(pi.hProcess,p,&buf,4,&written)<<endl;
   }
}
Thanks for all the work so far Nick.  Could you try your test again, but this time try to write to an area that ntdll.dll is mapped to?  

Thanks!
Unfortunately, I have no NT5 installed at the moment. I make the test on NT4. I don't think that behaviours of NT4 and NT5 are dirrefent - in this case it means application incompatibility.

Of course, you can reject my answer.

I tested writting to ntdll.dll - it also works fine.

As far as I can guess from your code,  'mbi_thunk' is the pointer to something like import thunk. ntdll on NT4 has no import section, I suppose that it is true for NT5 also. May be, the problem with your code is not near the WriteProcessMemory, but in the code which assigns value to mbi_thunk?

You can run my test program yourself. But you have to compile a.exe first. Make it simple, just like void main(void) { }. Then run a.exe under debugger and write down the base address of ntdll.dll. Then hard-code it in my test code.

Also I can suggest to print the contents of mbi_thunk and check whether it really lies inside ntdll.
Thanks again for the info Nick.  The mbi_thunk variable is the structure that gets passed to VirtualQueryEx - MEMORY_BASIC_INFORMATION.  Post your little test - as that would prove that my problem is based upon NT5.0.

As an aside, I checked out your profile and I thought it was cool that you are interested in Windows internals - I work for NuMega Labs - on the BoundsChecker team - so I am REALLY into this stuff!

Post the test and I'll accept your answer.

Thanks!
>>The mbi_thunk variable is the
>>structure that gets passed to

Sorry, it is my mistake. Really I told about pvLoc. Sure, mbi_thunk is just a Windows structure.

It was not necessary to accept my answer if it doesn't answer anything.

-----
Test programs:
Just compile both and run parent.exe.
The output on my NT:

Child: NTDLL base=0x77F60000
Child: signature=MZ
Parent: got message, NTDLL base 0x77F60000
Parent: signature=MZ
Parent: Test write failed - OK
Parent: Write success - OK

---------
// Child.cpp
#include <windows.h>
#include <iostream.h>

void main(int argc,char* argv[])
{
   // Get address of NTDLL
   DWORD ntdllBase=DWORD(GetModuleHandle("NTDLL"));
   if(!ntdllBase) {
      cout<<"Child: NTDLL is not loaded!"<<endl;
      return;
   }
   cout<<"Child: NTDLL base="<<LPVOID(ntdllBase)<<endl;
   
   // Just to make sure, following lines must print 'MZ' signature
   char sign[3];
   *LPWORD(sign)=*LPWORD(ntdllBase);
   sign[2]=0;
   cout<<"Child: signature="<<sign<<endl;

   // Extract parent's thread id
   if(argc<2) {  
      cout<<"Child: no parameter passed!"<<endl;
      return;
   }
   DWORD parent=strtoul(argv[1],0,10);

   // Pass address of NTDLL to the parent
   PostThreadMessage(parent,WM_USER+10,ntdllBase,0);

   // Allow parent to do its job
   Sleep(INFINITE);
}

--------

// Parent.cpp
#include <windows.h>
#include <iostream.h>

void main(void)
{
   // Create message queue
   MSG msg;
   PeekMessage(&msg,0,WM_USER,WM_USER,PM_NOREMOVE);

   // Run child process, pass as parameter our thread id.
   char cmdLine[120];
   wsprintf(cmdLine,"child.exe %u",GetCurrentThreadId());

   STARTUPINFO si;
   memset(&si,0,sizeof(si));
   si.cb=sizeof(si);
   PROCESS_INFORMATION pi;
   if(!CreateProcess(0,cmdLine,0,0,FALSE,0,0,0,&si,&pi)) {
      cout<<"Parent: unable to run child.exe!"<<endl;
      return;
   }
   CloseHandle(pi.hThread);

   // Wait for message from the child
   GetMessage(&msg,0,WM_USER+10,WM_USER+10);
   LPVOID ntdllBase=LPVOID(msg.wParam);
   cout<<"Parent: got message, NTDLL base "<<ntdllBase<<endl;

   DWORD n;

   __try {
      // Just to make sure, following lines must print 'MZ' signature
      char sign[3];
      if(!ReadProcessMemory(pi.hProcess,ntdllBase,&sign,2,&n)) {
         cout<<"Parent: unable to read memory!"<<endl;
         return;
      }
      sign[2]=0;
      cout<<"Parent: signature="<<sign<<endl;

      // Test - following call must fail
      if(WriteProcessMemory(pi.hProcess,ntdllBase,&sign,3,&n))
         cout<<"Parent: Test write success - VERY STRANGE!"<<endl;
      else
         cout<<"Parent: Test write failed - OK"<<endl;

      // Now change protection
      if(!VirtualProtectEx(pi.hProcess,ntdllBase,3,PAGE_READWRITE,&n)) {
         cout<<"Parent: VirtualProtect failed!"<<endl;
         return;
      }

      if(WriteProcessMemory(pi.hProcess,ntdllBase,&sign,3,&n))
         cout<<"Parent: Write success - OK"<<endl;
      else
         cout<<"Parent: Write failed, err="<<GetLastError()<<endl;

   }
   __finally {
      TerminateProcess(pi.hProcess,0);
      CloseHandle(pi.hProcess);
   }
}