rmangino
asked on
Page protection in another process
I am working on an NT 5 (2128) machine and cannot seem to get VirtualProtectEx/WriteProc essMemory 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
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
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
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Use the following (call to VirtualQueryEx is not necessary):
VirtualProtectEx( ppi->hProcess,
pvLoc,
sizeof(DWORD),
PAGE_READWRITE,
&oldProtect );
WriteProcessMemory ...
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_SU SPENDED,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(DW ORD),PAGE_ READWRITE,
&written)<<endl;
// This call fails w/o previous one
cout<<WriteProcessMemory(p i.hProcess ,p,&buf,4, &written)< <endl;
}
}
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"
char buf[16];
LPVOID p=LPVOID(0x400001); // I know that 0x400000 is the base addr of process a.exe
DWORD written;
cout<<VirtualProtectEx(pi.
&written)<<endl;
// This call fails w/o previous one
cout<<WriteProcessMemory(p
}
}
ASKER
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!
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.
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.
ASKER
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!
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(GetModuleH andle("NTD LL"));
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(ntdl lBase);
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,1 0);
// Pass address of NTDLL to the parent
PostThreadMessage(parent,W M_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,P M_NOREMOVE );
// Run child process, pass as parameter our thread id.
char cmdLine[120];
wsprintf(cmdLine,"child.ex e %u",GetCurrentThreadId());
STARTUPINFO si;
memset(&si,0,sizeof(si));
si.cb=sizeof(si);
PROCESS_INFORMATION pi;
if(!CreateProcess(0,cmdLin e,0,0,FALS E,0,0,0,&s i,&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.wPara m);
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.h Process,nt dllBase,&s ign,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.h Process,nt dllBase,&s ign,3,&n))
cout<<"Parent: Test write success - VERY STRANGE!"<<endl;
else
cout<<"Parent: Test write failed - OK"<<endl;
// Now change protection
if(!VirtualProtectEx(pi.hP rocess,ntd llBase,3,P AGE_READWR ITE,&n)) {
cout<<"Parent: VirtualProtect failed!"<<endl;
return;
}
if(WriteProcessMemory(pi.h Process,nt dllBase,&s ign,3,&n))
cout<<"Parent: Write success - OK"<<endl;
else
cout<<"Parent: Write failed, err="<<GetLastError()<<end l;
}
__finally {
TerminateProcess(pi.hProce ss,0);
CloseHandle(pi.hProcess);
}
}
>>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(GetModuleH
if(!ntdllBase) {
cout<<"Child: NTDLL is not loaded!"<<endl;
return;
}
cout<<"Child: NTDLL base="<<LPVOID(ntdllBase)<
// Just to make sure, following lines must print 'MZ' signature
char sign[3];
*LPWORD(sign)=*LPWORD(ntdl
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,1
// Pass address of NTDLL to the parent
PostThreadMessage(parent,W
// 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
// Run child process, pass as parameter our thread id.
char cmdLine[120];
wsprintf(cmdLine,"child.ex
STARTUPINFO si;
memset(&si,0,sizeof(si));
si.cb=sizeof(si);
PROCESS_INFORMATION pi;
if(!CreateProcess(0,cmdLin
cout<<"Parent: unable to run child.exe!"<<endl;
return;
}
CloseHandle(pi.hThread);
// Wait for message from the child
GetMessage(&msg,0,WM_USER+
LPVOID ntdllBase=LPVOID(msg.wPara
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.h
cout<<"Parent: unable to read memory!"<<endl;
return;
}
sign[2]=0;
cout<<"Parent: signature="<<sign<<endl;
// Test - following call must fail
if(WriteProcessMemory(pi.h
cout<<"Parent: Test write success - VERY STRANGE!"<<endl;
else
cout<<"Parent: Test write failed - OK"<<endl;
// Now change protection
if(!VirtualProtectEx(pi.hP
cout<<"Parent: VirtualProtect failed!"<<endl;
return;
}
if(WriteProcessMemory(pi.h
cout<<"Parent: Write success - OK"<<endl;
else
cout<<"Parent: Write failed, err="<<GetLastError()<<end
}
__finally {
TerminateProcess(pi.hProce
CloseHandle(pi.hProcess);
}
}
ASKER