Log in

View Full Version : DLL code patching at runtime ...


kappasm
February 2nd, 2011, 12:14
Hi,

I have one process that load one dll. I need to change the code of a routine, but only after the program has started. I have absolute address of code that I want to patching.

I start writing simple code with use of :

CreateToolhelp32Snapshot - for list of process/module
Process32First and Process32Next - for find primary process that load dll
Module32First and Module32Next - for find dll that i want patching
OpenProcess - to get handle of process that load dll

But now, for testing, I want read address of code that i want patch.

how do I go ?

Thanks.

Kappasm.

tazBRC
February 2nd, 2011, 12:57
After OpenProcess you need to do a ReadProcessMemory or WriteProcessMemory to patch.

kappasm
February 3rd, 2011, 03:29
Hi tazBRC,

thanks for your replay.

Quote:
[Originally Posted by tazBRC;89343]After OpenProcess you need to do a ReadProcessMemory or WriteProcessMemory to patch.


I tried these instructions but to no avail. There is definitely something that escapes me.

This is my code :

Code:

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>

// Process Name
char process_name[] = "Process.exe";

// Module Name
char module_name[] = "Module.dll";

//
// Error Message
//
void MessageError(TCHAR * msg)
{
// Local variable
DWORD eNum;
TCHAR Message[256];
TCHAR sysMsg[256];
TCHAR * p;

// Last Error
eNum = GetLastError( );

// Formatting error message
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, eNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), sysMsg, 256, NULL);

// Trim the end of the line and terminate it with a null
p = sysMsg;
while( ( *p > 31 ) || ( *p == 9 ) ) ++p;
do { *p-- = 0; } while( ( p >= sysMsg ) && ( ( *p == '.' ) || ( *p < 33 ) ) );

// Display the message
sprintf(Message, TEXT("\nWARNING: %s failed with error %d (%s)", msg, eNum, sysMsg );

// Print message
MessageBox(NULL, Message, "Error !!!", MB_OK);
}

//
// Main Program
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Local variable
DWORD Process;
DWORD ModuleAddress;
DWORD ModuleSize;
DWORD OldProtect;
PROCESSENTRY32 process;
MODULEENTRY32 module;
HANDLE snapshot;
BOOL gotime;
HANDLE phandle;
HANDLE hToken;
TOKEN_PRIVILEGES token;
PBYTE pMem;
DWORD Read;

// Get Full privilegies
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &token.Privileges[0].Luid);
token.PrivilegeCount = 1;
token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, 0, &token, sizeof(token), NULL, NULL);
}

// Init Process / Module size
process.dwSize = sizeof(process);
module.dwSize = sizeof(module);

// Init Search state
gotime = FALSE;

/*
* TH32CS_SNAPALL-> Includes all processes and threads in the system,
* plus the heaps and modules of the process specified in th32ProcessID.
*/
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);

// Search first process
Process32First(snapshot, &process);

// Search "Process.exe"
do
{
// Check found ...
if(strcmp(process.szExeFile, process_name) == 0)
{
// Save Process ID
Process = process.th32ProcessID;

// Find !!!
gotime = TRUE;

// End.
break;
}

} while (Process32Next(snapshot, &process));

// Close Search Process
CloseHandle(snapshot);

// Check found process
if (!gotime)
{
// Error
MessageError("Process not found";

// Exit
return(-1);
}

// Init Find Module
gotime = FALSE;

/*
* TH32CS_SNAPALL-> Includes all processes and threads in the system,
* plus the heaps and modules of the process specified in th32ProcessID.
*/
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, Process);

// Search first module on process
Module32First(snapshot, &module);

// Search "Module.dll"
do
{
// Check found ...
if(strcmp(module.szModule, module_name) == 0)
{
// Save Module ID and Size
ModuleAddress = (DWORD)module.modBaseAddr;
ModuleSize = (DWORD)module.modBaseSize;

// Find !!!
gotime = TRUE;

// End.
break;
}

} while (Module32Next(snapshot, &module));

// Close Search Process
CloseHandle(snapshot);

// Check found process
if (!gotime)
{
// Error
MessageError("Module not found";

// Exit
return(-1);
}

// Get handle of "Process.exe"
phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, Process);

// Check Open
if (phandle != 0)
{
// Allocate memory for Dump
pMem = (PBYTE)malloc(ModuleSize);

// Read Memory
ReadProcessMemory(phandle, (LPVOID)ModuleAddress, pMem, ModuleSize, &Read);

// Free Memory Dump
free(pMem);

// Close process
CloseHandle(phandle);
}
else
{
// Error
MessageError("Error Dump memory";

// Exit
return(-1);
}

// End
return (0);
}


During debugging the ReadProcessMemory do not work, any byte is read from "Module.dll".

I am not a professional programmer, I'm sorry if you do not like

Kappasm.

disavowed
February 3rd, 2011, 12:30
First of all, you don't need SE_DEBUG_NAME privilege.

Secondly, are both your process and the target process 32-bits? If not, you'll have problems (you'll always fail if one's 32-bit and the other's 64-bit, and if they're both 64-bits then your code will fail because you're truncating the module's base address to 32-bits).

Also, checking the return value of GetLastError() after ReadProcessMemory(...) helps with debugging.

kappasm
February 4th, 2011, 04:01
Hi disavowed,

Quote:
[Originally Posted by disavowed;89351]
First of all, you don't need SE_DEBUG_NAME privilege.


I thought it was necessary ... I try to remove it.


Quote:
[Originally Posted by disavowed;89351]
Secondly, are both your process and the target process 32-bits? If not, you'll have problems (you'll always fail if one's 32-bit and the other's 64-bit, and if they're both 64-bits then your code will fail because you're truncating the module's base address to 32-bits).


Both process are 32-bit.

Quote:
[Originally Posted by disavowed;89351]
Also, checking the return value of GetLastError() after ReadProcessMemory(...) helps with debugging.


GetLastError() return :

"ReadProcessMemory failed with error 299"

I am still confused .

Maybe someone has a code that already works ?

Thanks.

Kappasm.

Darkelf
February 4th, 2011, 06:49
error 299 returned from ReadProcessMemory means you still have parts of memory that are not readable. Did you have a look at your memory regions while debugging? Maybe you can identify the affected part this way. Or try to read-in your memory in pieces, to determine which part is not readable.

Regards

disavowed
February 4th, 2011, 11:59
I'd recommend using VirtualQueryEx(...) to find each region of memory in the loaded module, and use ReadProcessMemory(...) once per region.

kappasm
February 5th, 2011, 04:00
Hi disavowed,

thanks for suggestions ...

Quote:
[Originally Posted by disavowed;89360]I'd recommend using VirtualQueryEx(...) to find each region of memory in the loaded module


What do you mean by region of memory ? The loaded module is not fully accessible ? The module is divided into blocks ? How to find the block with absolute address ?

Many questions to which I have no answer ...

An example perhaps would be appreciated ...

Kappasm.

disavowed
February 5th, 2011, 04:02
Yes, the memory of the loaded module is divided into multiple regions. Read the documentation on VirtualQueryEx(...) to learn how to query these regions (start at the base of the module, then find the next region based on the size of the previous region).

kappasm
February 5th, 2011, 04:13
I prefer the examples, I'm still reading. One last thing ...

Quote:
[Originally Posted by disavowed;89362]start at the base of the module, then find the next region based on the size of the previous region


I call first time VirtualQueryEx with destination this type structure and start address previously found with research of "module.dll" :

Code:

typedef struct _MEMORY_BASIC_INFORMATION
{
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;


and i obtain "RegionSize". First region is PE header, I want code, I call again VirtualQueryEx with start address "BaseAddress + RegionSize". And so for the next region to obtain "RegionSize = 0" ... right ?

I understand you correctly ?

Thanks very much.

Kappasm.

Darkelf
February 5th, 2011, 07:21
since you prefer examples...

Code:


#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr) + (DWORD)(addValue))

BOOL __stdcall DumpProcessRange( IN DWORD dwProcessId, IN void* pStartAddr, IN DWORD dwcBytes, OUT void* pDumpedBytes, OUT char* szErrorStr)
{
BOOL bRet;
HANDLE hProc;
DWORD cb, cbFailure = 0, cb2Do, dwBlockSize;
MEMORY_BASIC_INFORMATION minfo;
char cBuff[100];

// get process handle
hProc = OpenProcess( PROCESS_VM_READ, FALSE, dwProcessId );
if ( !hProc )
{
lstrcpy( szErrorStr, "Error while querying process handle !" );
return FALSE; // ERR
}

//
// first let's try 2 dump the whole block directly
//
bRet = ReadProcessMemory( hProc, pStartAddr, pDumpedBytes, dwcBytes, &cb );
CloseHandle( hProc );
if ( bRet )
goto TidyUp;

//
// scan the memory information
//
hProc = OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwProcessId );
if ( !hProc )
{
lstrcpy( szErrorStr, "Error while querying process handle !" );
return FALSE; // ERR
}

cb2Do = dwcBytes;
minfo.BaseAddress = pStartAddr;
while ( cb2Do )
{
// get memory info
bRet = VirtualQueryEx(
hProc,
minfo.BaseAddress,
&minfo, sizeof(minfo));
if ( !bRet )
{
lstrcpy( szErrorStr, "VirtualQueryEx() failed !" );
goto TidyUp; // ERR
}

#define f minfo.Protect

dwBlockSize = min( minfo.RegionSize, cb2Do );
if ( pStartAddr ) // first region dump ?
dwBlockSize -= (DWORD)pStartAddr - (DWORD)minfo.BaseAddress;

if ( ((f & PAGE_GUARD) != 0) || // do we have access there ?
((f & PAGE_NOACCESS) != 0) )
{
//
// don't try 2 dump this memory part
//

// zero not dumped part in output buffer
memset(
MakePtr( PVOID, pDumpedBytes, dwcBytes - cb2Do ),
0,
dwBlockSize);
cbFailure += dwBlockSize;
}
else
{
//
// dump normally
//

bRet = ReadProcessMemory(
hProc,
pStartAddr ? pStartAddr : minfo.BaseAddress,
MakePtr( PVOID, pDumpedBytes, dwcBytes - cb2Do ),
dwBlockSize,
&cb);
if ( pStartAddr ) // only non-zero on first region dump
pStartAddr = NULL;
if ( !bRet )
{
// OK,no...zero not dumped part in output buffer
memset(
MakePtr( PVOID, pDumpedBytes, dwcBytes - cb2Do ),
0,
dwBlockSize);
cbFailure += dwBlockSize;
}
}

// adjust vars
minfo.BaseAddress = MakePtr( PVOID, minfo.BaseAddress, minfo.RegionSize );
cb2Do -= dwBlockSize;
}

bRet = TRUE; // ret an OK

if ( cbFailure ) // were any bytes not dumpable ?
{
wsprintf(
cBuff,
"0x%X of 0x%X bytes could not be dumped\nand were padded with zeros.",
cbFailure, dwcBytes);
MessageBox( GetActiveWindow(), cBuff, "IntelliDump", MB_ICONWARNING );
}

//
// tidy up
//
TidyUp:
CloseHandle( hProc );

return bRet;
}


taken from yoda's intellidump.

kappasm
February 6th, 2011, 06:13
With your very useful information I managed to do the patching of the DLL.

Thanks very much Darkelf and disavowed.

Best Regards.

Kappasm.