|
HOW TO ACCESS THE MEMORY OF A PROCESS (i.e. Game Trainer,
Process Patcher etc.)
Deep into Windows
|
Our Tools
|
17 February 1998
|
by
NaTzGUL
|
|
|
Courtesy of Fravia's page of
reverse engineering
|
|
fra_00xx 980217 NaTzGUL 0110 OT AD
|
NaTzGUL: a great cracker (as Quine pointed out long ago). This essay-tutorial is a valuable
contribution to the "Our tools" section, since what you'll learn here will be of
paramount importance when working in the 'guts' of this awful operating system the
world is compelled to live with. Yes, dear reader, you'll slowly learn how to
program (in the most REAL sense of the word) in windows... in order to bend this
overbloated operating system to your twisted purposes :-)
Enjoy! It's fundamental 'required' reading... read it twice (at least) and then
work on this... you'll be a much more powerful wizard when you are finished with
this...
|
Advanced
|
|
There is a crack, a crack in everything
That's how the light gets in
| |
Rating
|
( )Beginner (x)Intermediate (x)Advanced ( )Expert
|
|
A fundamental essay for intermediate and advanced crackers. This tutorial's
subjects are
of paramount importance for any serious probe-building.
HOW TO ACCESS THE MEMORY OF A PROCESS
(i.e. Game Trainer,Process Patcher etc.)
Deep into window
Written by
NaTzGUL
INTRO: Yup ! iam back from lazyness =)
Hello and Welcome to my new tut.
This time it concerns, how to access the memory of a Process, i.e. write
and read access.
This is actually nothing new and the Game Trainer Scene proves it to us daily.
The problem under win95 is actually that a Process has no rights to access
the memory of another Process (above all not the code).
In this tut I will show you methods and the functions to bypass this problem.
Lotta people has spoken about these functions, but no one did somewhat
useful with them until now (except the Trainer Scene),
however this is my contribution to fill this gap and of course a few examples
how to use them for cracking purposes were included.
TARGET: ANY EXE (FLAT MODEL !)
Author : NaTzGUL
Email : natzgul@hotmail.com
HOW TO ACCESS THE MEMORY OF A PROCESS (i.e. Game Trainer,Process Patcher etc.)
TUT: At first I will list you the mentioned functions we wanna use :
(These functions are actually meant for debugging)
KERNEL32!ReadProcessMemory
KERNEL32!WriteProcessMemory
Now the Descriptions from win32.hlp:
The ReadProcessMemory function reads memory in a specified process.
The entire area to be read must be accessible, or the operation fails.
BOOL ReadProcessMemory(
HANDLE hProcess, // handle of the process whose memory is read
LPCVOID lpBaseAddress, // address to start reading
LPVOID lpBuffer, // address of buffer to place read data
DWORD cbRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead // address of number of bytes read
);
Parameters
hProcess
Identifies an open handle of a process whose memory is read.
The handle must have PROCESS_VM_READ access to the process.
lpBaseAddress
Points to the base address in the specified process to be read.
Before any data transfer occurs, the system verifies that all data in the
base address and memory of the specified size is accessible for read access.
If this is the case, the function proceeds; otherwise, the function fails.
lpBuffer
Points to a buffer that receives the contents from the address space of
the specified process.
cbRead
Specifies the requested number of bytes to read from the specified process.
lpNumberOfBytesRead
Points to the actual number of bytes transferred into the specified buffer.
If lpNumberOfBytesRead is NULL, the parameter is ignored.
Return Value
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE.
To get extended error information, call GetLastError.
The function fails if the requested read operation crosses into an area of
the process that is inaccessible.
Remarks
ReadProcessMemory copies the data in the specified address range from the
address space of the specified process into the specified buffer of the
current process.
Any process that has a handle with PROCESS_VM_READ access can call the function.
The process whose address space is read is typically, but not necessarily,
being debugged.
The entire area to be read must be accessible.
If it is not, the function fails as noted previously.
The WriteProcessMemory function writes memory in a specified process.
The entire area to be written to must be accessible, or the operation fails.
BOOL WriteProcessMemory(
HANDLE hProcess, // handle of process whose memory is written to
LPVOID lpBaseAddress, // address to start writing to
LPVOID lpBuffer, // address of buffer to write data to
DWORD cbWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten // actual number of bytes written
);
Parameters
hProcess
Identifies an open handle of a process whose memory is to be written to.
The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access to the process.
lpBaseAddress
Points to the base address in the specified process to be written to.
Before any data transfer occurs, the system verifies that all data in the base address
and memory of the specified size is accessible for write access.
If this is the case, the function proceeds; otherwise, the function fails.
lpBuffer
Points to the buffer that supplies data to be written into the address space of the
specified process.
cbWrite
Specifies the requested number of bytes to write into the specified process.
lpNumberOfBytesWritten
Points to the actual number of bytes transferred into the specified process.
This parameter is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored.
Return Value
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE.
To get extended error information, call GetLastError.
The function will fail if the requested write operation crosses into an area
of the process that is inaccessible.
Remarks
WriteProcessMemory copies the data from the specified buffer in the current process
to the address range of the specified process.
Any process that has a handle with PROCESS_VM_WRITE and PROCESS_VM_OPERATION access
to the process to be written to can call the function.
The process whose address space is being written to is typically, but not necessarily,
being debugged.
The entire area to be written to must be accessible.
If it is not, the function fails as noted previously.
TUT: To call both functions we need :
HANDLE hProcess = ? // handle of process
LPVOID lpBaseAddress, = your choice // address to start reading/writing
LPVOID lpBuffer, = your choice // address of buffer to read/write data
DWORD cbWrite, = your choice // number of bytes to read/write
LPDWORD lpNumberOfBytesWritten = NULL // actual number of bytes read/written
Now, looks nevertheless already quite promising heh ;)
So the only thing we need is the Handle of the Process we want to access.
A Process can call "KERNEL32!GetCurrentProcess" to get a pseudo-handle of itself.
This pseudo-handle has the maximum possible access to the process object (memory).
If we want to address however another Process we must proceed differently.
I know two methods to get a Process Handle of another Process :
1. with USER32!FindWindowA,USER32!GetWindowThreadProcessId and KERNEL32!OpenProcess
2. with KERNEL32!CreateProcess
METHOD1:
----------------
A Game Trainer use this method by the way.
A Process is like everything else in win95 also only an Object,
this means we can open it with "KERNEL32!OpenProcess" to get a Handle.
And you betterdon't forget to close it with
"BOOL KERNEL32!CloseHandle (hProcess)" later on.
Otherwise the Process image will remain in the memory and waste space
when the Process terminates.
Here is the Description of it from win32.hlp:
The OpenProcess function returns a handle of an existing process object.
HANDLE OpenProcess(
DWORD fdwAccess, // access flag
BOOL fInherit, // handle inheritance flag
DWORD IDProcess // process identifier
);
Parameters
fdwAccess
Specifies the access to the process object. For operating systems that support security
checking, this access is checked against any security descriptor for the target process.
Any combination of the following access flags can be specified in addition to
the STANDARD_RIGHTS_REQUIRED access flags:
Access Description
PROCESS_ALL_ACCESS Specifies all possible access flags for the process object.
PROCESS_CREATE_PROCESS Used internally.
PROCESS_CREATE_THREAD Enables using the process handle in the
CreateRemoteThread function to create a thread in the process.
PROCESS_DUP_HANDLE Enables using the process handle as either the source
or target process in the DuplicateHandle function to
duplicate a handle.
PROCESS_QUERY_INFORMATION Enables using the process handle in the
GetExitCodeProcess and GetPriorityClass functions
to read information from the process object.
PROCESS_SET_INFORMATION Enables using the process handle in the SetPriorityClass
function to set the priority class of the process.
PROCESS_TERMINATE Enables using the process handle in the TerminateProcess
function to terminate the process.
PROCESS_VM_OPERATION Enables using the process handle in the VirtualProtectEx
and WriteProcessMemory functions to modify the virtual
memory of the process.
PROCESS_VM_READ Enables using the process handle in the ReadProcessMemory
function to read from the virtual memory of the process.
PROCESS_VM_WRITE Enables using the process handle in the
WriteProcessMemory function to write to the virtual
memory of the process.
SYNCHRONIZE Windows NT: Enables using the process handle in any of
the wait functions to wait for the process to terminate.
fInherit
Specifies whether the returned handle can be inherited by a new process created by
the current process. If TRUE, the handle is inheritable.
IDProcess
Specifies the process identifier of the process to open.
Return Value
If the function succeeds, the return value is an open handle of the specified process.
If the function fails, the return value is NULL. To get extended error information,
call GetLastError.
Remarks
The handle returned by the OpenProcess function can be used in any function
that requires a handle to a process, provided the appropriate access rights
were requested.
TUT: To call this function we need :
DWORD fdwAccess = PROCESS_ALL_ACCESS (Thx to Micro$oft ;) // access flag
BOOL fInherit = FALSE // handle inheritance flag
DWORD IDProcess = ? // process identifier
Use "KERNEL32!GetCurrentProcessId" to get an ID of the current Process.
To get the ID of another Process we must proceed differntly again.
To find out if a certain Process allready exist we call USER32!FindWindowA
which will look for a Window with a certain title and then return its handle.
With this window handle we then finally call GetWindowThreadProcessId to get
the process identifier.
Here is the Description of both function from win32.hlp:
The FindWindowA function retrieves the handle of the top-level window whose class name
and window name match the specified strings. This function does not search child windows.
HWND FindWindowA(
LPCTSTR lpClassName, // address of class name
LPCTSTR lpWindowName // address of window name
);
Parameters
lpClassName
Points to a null-terminated string that specifies the class name or is an atom that
identifies the class-name string. If this parameter is an atom, it must be a global
atom created by a previous call to the GlobalAddAtom function.
The atom, a 16-bit value, must be placed in the low-order word of lpClassName;
the high-order word must be zero.
lpWindowName
Points to a null-terminated string that specifies the window name (the window's title).
If this parameter is NULL, all window names match.
Return Value
If the function succeeds, the return value is the handle of the window that has the
specified class name and window name.
If the function fails, the return value is NULL. To get extended error information,
call GetLastError.
TUT: To call this function we need :
LPCTSTR lpClassName = NIL // address of class name
LPCTSTR lpWindowName = your choice // address of window name
The GetWindowThreadProcessId function retrieves the identifier of the thread that
created the specified window and, optionally, the identifier of the process that
created the window. This function supersedes the GetWindowTask function.
DWORD GetWindowThreadProcessId(
HWND hWnd, // handle of window
LPDWORD lpdwProcessId // address of variable for process identifier
);
Parameters
hWnd
Identifies the window.
lpdwProcessId
Points to a 32-bit value that receives the process identifier.
If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the
process to the 32-bit value; otherwise, it does not.
Return Value
The return value is the identifier of the thread that created the window.
TUT: To call this function we need :
HWND hWnd = result from FindWindowA // handle of window
LPDWORD lpdwProcessId = your choice // address of variable for
process identifier
We are now able to write a function which will relieve the access to a Process for us.
I wrote this function in Delphi3. If someone asks for the assembler or c version,
then I will write one for you too, but at this time iam to lazy ;)
Anyway, it should not be that hard to figure it out yourself !
Here thus the Delphi version of the mentioned function:
(This function will in most cases find its use in a trainer i think)
type access_info = record
error :integer;
hwindow :integer;
thread_id :integer;
process_id :integer;
hprocess :integer;
end;
function AccessProcess ( access_type :integer;
wtitle :string;
address :integer;
buffer :PByteArray;
b_count :integer
):access_info;
var temp :integer;
begin result.error:=0;
if wtitle<>'' then
begin result.hwindow:=FindWindowA (nil,pchar(wtitle));
if result.hwindow<>0 then
begin result.thread_id:=GetWindowThreadProcessId (result.hwindow,@result.process_id);
result.hprocess:=OpenProcess (PROCESS_ALL_ACCESS,false,result.process_id);
if result.hprocess<>0 then
begin temp:=0;
case access_type of
0 : ;
1 : if not(ReadProcessMemory (result.hprocess,pointer(address),buffer,b_count,temp)) then
result.error:=4;
2 : if not(WriteProcessMemory (result.hprocess,pointer(address),buffer,b_count,temp)) then
result.error:=5;
else result.error:=6;
end;
CloseHandle (result.hprocess);
end
else result.error:=3;
end
else result.error:=2;
end
else result.error:=1;
end;
The possible access-types of the function :
0 = Only get Info
1 = Read
2 = Write
Possible Error Codes :
0 = No Error.
1 = Window-Title is emty.
2 = Can't find the Window with the specified title.
3 = Can't open the Process.
4 = Read Error.
5 = Write Error.
6 = Not supported access-type.
METHOD2:
----------------
In this chapter we are going to write a Process Patcher.
A Process Patcher will do the following tasks :
- Getting commandline
- Create new process and handle commandline to it
- Wait until the process was fully initialized
- Patch the process
- Finally terminate and leave the new process alone
A Process Patcher will be usefull if ...
1. the exe was compressed (i.e. Shrinker)
2. the exe was encrypted (i.e. PE-Crypt)
3. the app performs a CRC-Check on the exe
4. the exe was modified in any other way
With a Process Patcher you dont have to care about the exe anymore, because it will
patch the Process like in the first method of this tut.
The difference is that we dont have to specify a window title, because we will
use KERNEL32!CreateProcess and therefore we will automatically get the
Process handle (with PROCESS_ALL_ACCESS) returned in a structure.
Let us go through each task of the patcher :
Getting commandline :
-----------------------------
This may also be usefull if you have renamed the patcher.exe to app.exe .
This way the app will receive the commandline from the patcher and
thus the patcher will be fully transparency to the child process.
Remark : do not change the name if the app performs a CRC-Check on the exe !!!,
otherwise it will do the check on the patcher and thats even logical ;)
Create new process and handle commandline to it :
------------------------------------------------------------------
Win95 does it the same way and again we will be fully transparency to the child process.
Wait until the process was fully initialized :
-------------------------------------------------------
This is necessary, because the memory of the child processes wasnt commited yet
which means that there is garbage at this point.
I used WaitForInputIdle to make sure the memory was commited.
This function waits until the given process is waiting for user input.
Remark : If this is too late for your app you may do compare constantly !!!
Patch the process :
-------------------------
After the process memory was commited and the byte check was ok we now can simply
patch the process.
Finally terminate and leave the new process alone :
-------------------------------------------------------------------
This is optional , but dont forget to close the process and thread handles.
Ok , before i give you the source here are the Descriptions of the functions
from win32.hlp:
The CreateProcess function creates a new process and its primary thread.
The new process executes the specified executable file.
BOOL CreateProcess(
LPCTSTR lpApplicationName, // pointer to name of executable module
LPTSTR lpCommandLine, // pointer to command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // pointer to process security attributes
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to thread security attributes
BOOL bInheritHandles, // handle inheritance flag
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // pointer to new environment block
LPCTSTR lpCurrentDirectory, // pointer to current directory name
LPSTARTUPINFO lpStartupInfo, // pointer to STARTUPINFO
LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION
);
Parameters
lpApplicationName
Pointer to a null-terminated string that specifies the module to execute.
The string can specify the full path and filename of the module to execute.
The string can specify a partial name. In that case, the function uses the
current drive and current directory to complete the specification.
The lpApplicationName parameter can be NULL. In that case, the module name
must be the first white space-delimited token in the lpCommandLine string.
The specified module can be a Win32-based application.
It can be some other type of module (for example, MS-DOS or OS/2) if the
appropriate subsystem is available on the local computer.
Windows NT : If the executable module is a 16-bit application,
lpApplicationName should be NULL, and the string pointed to by
lpCommandLine should specify the executable module.
A 16-bit application is one that executes as a VDM or WOW process.
lpCommandLine
Pointer to a null-terminated string that specifies the command line to execute.
The lpCommandLine parameter can be NULL. In that case, the function uses the
string pointed to by lpApplicationName as the command line.
If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName
specifies the module to execute, and *lpCommandLine specifies the command line.
The new process can use GetCommandLine to retrieve the entire command line.
C runtime processes can use the argc and argv arguments.
If lpApplicationName is NULL, the first white space-delimited token of the command line
specifies the module name. If the filename does not contain an extension,
.EXE is assumed. If the filename ends in a period (.) with no extension, or the filename
contains a path, .EXE is not appended. If the filename does not contain a directory path,
Windows searches for the executable file in the following sequence:
1. The directory from which the application loaded.
2. The current directory for the parent process.
3. Windows 95: The Windows system directory. Use the GetSystemDirectory function
to get the path of this directory.
Windows NT: The 32-bit Windows system directory. Use the GetSystemDirectory function
to get the path of this directory. The name of this directory is SYSTEM32.
4. Windows NT only: The 16-bit Windows system directory. There is no Win32 function
that obtains the path of this directory, but it is searched.
The name of this directory is SYSTEM.
5. The Windows directory. Use the GetWindowsDirectory function to get the path of
this directory.
6. The directories that are listed in the PATH environment variable.
If the process to be created is an MS-DOS - based or Windows-based application,
lpCommandLine should be a full command line in which the first element is the
application name. Because this also works well for Win32-based applications, it is the
most robust way to set lpCommandLine.
lpProcessAttributes
Points to a SECURITY_ATTRIBUTES structure that specifies the security attributes for
the created process.
If lpProcessAttributes is NULL, the process is created with a default
security descriptor, and the resulting handle is not inherited.
lpThreadAttributes
Points to a SECURITY_ATTRIBUTES structure that specifies the security attributes for
the primary thread of the new process. If lpThreadAttributes is NULL, the process is
created with a default security descriptor, and the resulting handle is not inherited.
bInheritHandles
Indicates whether the new process inherits handles from the calling process.
If TRUE, each inheritable open handle in the calling process is inherited by the new
process. Inherited handles have the same value and access privileges as the original
handles.
dwCreationFlags
Specifies additional flags that control the priority class and the creation of the
process. The following creation flags can be specified in any combination, except as
noted:
Value Meaning
CREATE_DEFAULT_ERROR_MODE The new process does not inherit the error mode of the
calling process. Instead, CreateProcess gives the new
process the current default error mode. An application
sets the current default error mode by calling
SetErrorMode.This flag is particularly useful for
multi-threaded shell applications that run with hard
errors disabled. The default behavior for CreateProcess
is for the new process to inherit the error mode of the
caller. Setting this flag changes that default behavior.
CREATE_NEW_CONSOLE The new process has a new console, instead of inheriting
the parent's console. This flag cannot be used with the
DETACHED_PROCESS flag.
CREATE_NEW_PROCESS_GROUP The new process is the root process of a new process
group. The process group includes all processes that are
descendants of this root process. The process ID of the
new process group is the same as the process ID, which
is returned in the lpProcessInformation parameter.
Process groups are used by the GenerateConsoleCtrlEvent
function to enable sending a CTRL+C or CTRL+BREAK signal
to a group of console processes.
CREATE_SEPARATE_WOW_VDM This flag is only valid only launching a 16-bit Windows
program. If set, the new process is run in a private
Virtual DOS Machine (VDM). By default, all 16-bit
Windows programs are run in a single, shared VDM.
The advantage of running separately is that a crash
only kills the single VDM; any other programs running
in distinct VDMs continue to function normally.
Also, 16-bit Windows applications which are run in
separate VDMs have separate input queues. That means that
if one application hangs momentarily, applications in
separate VDMs continue to receive input.
CREATE_SHARED_WOW_VDM Windows NT: The flag is valid only when launching a
16-bit Windows program. If the DefaultSeparateVDM switch
in the Windows section of WIN.INI is TRUE, this flag
causes the CreateProcess function to override the switch
and run the new process in the shared Virtual DOS Machine.
CREATE_SUSPENDED The primary thread of the new process is created in a
suspended state, and does not run until the ResumeThread
function is called.
CREATE_UNICODE_ENVIRONMENT If set, the environment block pointed to by lpEnvironment
uses Unicode characters. If clear, the environment block
uses ANSI characters.
DEBUG_PROCESS If this flag is set, the calling process is treated as a
debugger, and the new process is a process being debugged.
The system notifies the debugger of all debug events
that occur in the process being debugged.
If you create a process with this flag set, only the
calling thread (the thread that called CreateProcess)
can call the WaitForDebugEvent function.
DEBUG_ONLY_THIS_PROCESS if not set and the calling process is being debugged,
the new process becomes another process being debugged
by the calling process's debugger. If the calling process
is not a process being debugged, no debugging-related
actions occur.
DETACHED_PROCESS For console processes, the new process does not have
access to the console of the parent process.
The new process can call the AllocConsole function at
a later time to create a new console. This flag cannot
be used with the CREATE_NEW_CONSOLE flag.
The dwCreationFlags parameter also controls the new
process's priority class, which is used in determining
the scheduling priorities of the process's threads.
If none of the following priority class flags is
specified, the priority class defaults to
NORMAL_PRIORITY_CLASS unless the priority class of the
creating process is IDLE_PRIORITY_CLASS. In this case
the default priority class of the child process is
IDLE_PRIORITY_CLASS. One of the following flags can be
specified:
Priority Meaning
HIGH_PRIORITY_CLASS Indicates a process that performs time-critical tasks that must
be executed immediately for it to run correctly.
The threads of a high-priority class process preempt the threads
of normal-priority or idle-priority class processes.
An example is Windows Task List, which must respond quickly when
called by the user, regardless of the load on the operating
system. Use extreme care when using the high-priority class,
because a high-priority class CPU-bound application can use
nearly all available cycles.
IDLE_PRIORITY_CLASS Indicates a process whose threads run only when the system is
idle and are preempted by the threads of any process running in
a higher priority class. An example is a screen saver.
The idle priority class is inherited by child processes.
NORMAL_PRIORITY_CLASS Indicates a normal process with no special scheduling needs.
REALTIME_PRIORITY_CLASS Indicates a process that has the highest possible priority.
The threads of a real-time priority class process preempt the
threads of all other processes, including operating system
processes performing important tasks. For example, a real-time
process that executes for more than a very brief interval can
cause disk caches not to flush or cause the mouse to be
unresponsive.
lpEnvironment
Points to an environment block for the new process. If this parameter is NULL,
the new process uses the environment of the calling process.
An environment block consists of a null-terminated block of null-terminated strings.
Each string is in the form:
name=value
Because the equal sign is used as a separator, it must not be used in the name of an
environment variable.
If an application provides an environment block, rather than passing NULL for this
parameter, the current directory information of the system drives is not automatically
propagated to the new process. For a discussion of this situation and how to handle it,
see the following Remarks section.
An environment block can contain Unicode or ANSI characters. If the environment block
pointed to by lpEnvironment contains Unicode characters, the dwCreationFlags field's
CREATE_UNICODE_ENVIRONMENT flag will be set. If the block contains ANSI characters,
that flag will be clear.
Note that an ANSI environment block is terminated by two zero bytes: one for the last
string, one more to terminate the block. A Unicode environment block is terminated by
four zero bytes: two for the last string, two more to terminate the block.
lpCurrentDirectory
Points to a null-terminated string that specifies the current drive and directory for the
child process. The string must be a full path and filename that includes a drive letter.
If this parameter is NULL, the new process is created with the same current drive and
directory as the calling process. This option is provided primarily for shells that need
to start an application and specify its initial drive and working directory.
lpStartupInfo
Points to a STARTUPINFO structure that specifies how the main window for the new process
should appear.
lpProcessInformation
Points to a PROCESS_INFORMATION structure that receives identification information about
the new process.
Return Value
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error information,
call GetLastError.
TUT: wow ! this function has lotta paramaters ;) , but dont worry most of em can be NULL.
lpApplicationName = your choice // pointer to name of executable module
lpCommandLine = result of getcommandine // pointer to command line string
lpProcessAttributes = NULL // pointer to process security attributes
lpThreadAttributes = NULL // pointer to thread security attributes
bInheritHandles = FALSE // handle inheritance flag
dwCreationFlags = NORMAL_PRIORITY_CLASS // creation flags
lpEnvironment = NULL // pointer to new environment block
lpCurrentDirectory = NULL // pointer to current directory name
lpStartupInfo = ? // pointer to STARTUPINFO
lpProcessInformation = ? // pointer to PROCESS_INFORMATION
Now we have a look at the STARTUPINFO structure :
typedef struct _STARTUPINFO { // si
DWORD cb;
LPTSTR lpReserved;
LPTSTR lpDesktop;
LPTSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO
Its eneugh to initialize all values to zero and cb to sizeof(si).
Now we have a look at the PROCESS_INFORMATION structure :
typedef struct _PROCESS_INFORMATION { // pi
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
The PROCESS_INFORMATION structure is filled in by the CreateProcess
function with information about a newly created process and its
primary thread.
Members
hProcess
Returns a handle to the newly created process. The handle is used to
specify the process in all functions that perform operations on the
process object.
hThread
Returns a handle to the primary thread of the newly created process.
The handle is used to specify the thread in all functions that perform
operations on the thread object.
dwProcessId
Returns a global process identifier that can be used to identify a process.
The value is valid from the time the process is created until the time
the process is terminated.
dwThreadId
Returns a global thread identifiers that can be used to identify a thread.
The value is valid from the time the thread is created until the time the
thread is terminated.
TUT:
This structure will be returned by CreateProcess and it includes the process handle.
Now we got all parameters to create the process, but after it was created we have
to wait until it was initialized. Therefore we must call WaitForInputIdle (hProcess,
INFINITE) to make sure the whole virtual memory of the new process was commited,
before we can patch it.
Before we terminate we also have to close the process and thread handle.
SOURCE:
I wrote a simple Process-Patcher PPATCH.EXE which will patch COUNTERS.EXE.
COUNTERS.EXE was shrinked and it consists only of a counter and a button.
If you press the button it will decrement the counter by one and a MessageBox with
"GO ON !!!" will pop up. If you have reach 0 the MessageBox will show you "GAME OVER !!!".
Put both exe into the same dir and execute PPATCH.EXE.
Sorry that counters.exe is over 100kb, but Shrinker does not accept small files ;)
Here is the source of PPATCH
/*------------------------------------------------------------
PPATCH.C -- Process Patcher Demo
(c) NaTzGUL, 1998
Description : This is a Process Patcher for counters.exe
------------------------------------------------------------*/
#include
void messbox (char* mess)
{
static char caption[]="Process Patcher Error";
MessageBox (NULL,mess,&caption,MB_OK|MB_ICONERROR);
}
void main ()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
char* cl;
int x;
static long BaseAddress=0x4255C1;
static char original[6]={0xFF,0x0D,0x14,0x68,0x42,0x00};
static char new_bytes[6]={0x90,0x90,0x90,0x90,0x90,0x90};
static char Buffer[6];
static char fname[]="counters.exe";
static char err1[]="Can't CreateProcess ";
static char err2[]="Can't ReadProcessMemory";
static char err3[]="Bytes don't match";
static char err4[]="Can't WriteProcessMemory";
ZeroMemory (&si,sizeof (si));
si.cb=sizeof (si);
cl=GetCommandLine ();
if (CreateProcess (&fname,cl,NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,
NULL,NULL,&si,&pi))
{
WaitForInputIdle (pi.hProcess,INFINITE);
if (ReadProcessMemory (pi.hProcess,BaseAddress,&Buffer,6,NULL))
{
for (x=0;(Buffer[x]==original[x])&&(x<5);x++);
if (x==5)
{
if (!WriteProcessMemory (pi.hProcess,BaseAddress,&new_bytes,6,NULL))
messbox(&err4);
}
else messbox(&err3);
}
else messbox(&err2);
CloseHandle (pi.hProcess);
CloseHandle (pi.hThread);
}
else messbox(&err1);
}
>
I) GREETINGS
Groups:
REVOLT, #CRACKING, UCF, PC97, HERITAGE,CRC32
#CRACKING4NEWBIES, CORE, RZR, PWA, XF, DEV etc.
PERSONAL:
Quine, CoPhiber, Spanky, Doc-Man, Korak, lgb, DDensity,
Krazy_N, Sirax, Norway, delusion, riches, Laamaah,
Darkrat, wiesel, DirHauge, GnoStiC, JosephCo, niabi,Voxel,
TeRaPhY, NiTR8, Marlman, OWL, razzia, K_LeCTeR, FaNt0m,
zz187, HP, Johnastig, StarFury, Hero, +ORC, +Crackers,
Fravia+, LordCaligo, BASSMATIC, j0b ,xoanon, EDISON etc.
Ob duh does not apply here, quite the contrary: many will use the material
we are explaining here to build and sell their own applications without even referring
to Natzgul's work... poor sods... by the time they will have made some slimy
money we will already be three light years ahead with our tools
You are deep inside fravia's page of reverse engineering,
choose your way out:
Back to our tools
homepage
links
search_forms
+ORC
students' essays
academy database
reality cracking
how to search
javascript wars
tools
anonymity academy
cocktails
antismut CGI-scripts
mail_fravia+
Is reverse engineering legal?