Back to the HCU Papers
Tweaking with memory in Window95
- An API approach -
by Iceman
Intro
------
I decided to write this after I read NaTzGUL's tutorial called "How to
access memory of another process".This time want to take you deeper inside
Windows95 ,revealing some useful tricks.The basic fact is that if we want
to write good tools for reverse engineering on WIN32 hosted platforms we have
to take full control over the OS.That means system level programming
techniques such: virtual memory management,Debug API functions,Ring 0 programming
using VxD or Call gates,Image Manipulation functions.
So let's learn how this OS work!
My future projects are based on Ring 0 programing.I am especially
interested in Call gates and WIN32 debug API.Two of the most powerful
functions of WIN32 API are contained in debug API.They are GetThreadContext &
SetThreadContext.You can do amazing things using them,such as injecting
code and executing code in another process adress space.
I want to thank to Matt Pietrek for his articles about WIN32
platforms.Reading those was a pleasure and very instructive.In fact 90% of
my knowledge regarding system level programming in Windows was gained reading
his work.In my humble opinion Matt is one of the best official "reverse
engineer".Thank you,Matt,thank you for sharing knowledge with us!
Next,let's give Fravia the credit he deserves.His WEB site is a treasure
for knowledge seekers.Is the best page in reverse engineering business I ever
seen.
I know , I know my english is very bad.Please forgive me.
This essay is structured as below:
Chapter1:Short Introduction to Windows95 memory management
Chapter2:Tweaking with virtual memory functions
Chapter3.Short intro to Toolhelp32 functions
Chapter4.How do I:
4.1.Write self-modifiable code.
4.2.Modify the code pages of another process.
4.3.Modify functions inside system Dll's.
========================================================================
Short Introduction to Windows95 memory management
--------------------------------------------------
I assume that the reader is familiar with process and threads functions
and have a basic knowledge on how Windows95,using the paging mechanism on
386+ processors,manage memory.Let's remember some facts:
1.Windows 95 implements a page based virtual memory system. It
uses a 32 bit linear addressing system.Internaly,all memory is managed in
4096 bytes segments called pages.The entire memory that CPU can address
in theory is called "address space".That's 4Gb.
2. In Windows95 each 32 bit application is provided with an
independent 4 Gb address space,regardless of how match phisycal memory is
installed in your computer.This address space is structured as below:
4Gb ------------------------
| |
| |
| VxD |
3Gb ------------------------
| System DLLs |
| MMF |
| Top W16 Global Heap |
2Gb ------------------------
| |
| |
| User process |
| area |
| |
| |
4Mb ------------------------
| Base W16 Global Heap|
0 ------------------------
The portion 0 to 4Mb is shared between processes.It usually contain
16 bit system dll's.The next region begin at 4Mb and ends at 2Gb.This is
the user process area.Each process have code ,data ,resources loaded in
this region.
The region is not shared! The third region begin at 2Gb and ends at
3 Gb.This region is shared between processes.Usualy the system load here
The system dll's .This region is also used to map Memory Mapped Files.The
MMF must be in a shared region because they are utilized to share data
between processes.Finaly , the last region begin at 3Gb and ends at 4Gb
Here the system loads Ring0 components (VxD).
When a executable image is loaded,the system loader maps the file
in the user process area starting with address 0x0040000 (4Mb).
This is the base address of the most PE files.
Of course ,an executable image can be rebased,so don't relay blindly on this
address.(Microsoft provides a set of functions for image manipulation.
This functions reside inside imagehlp.dll. Some of them can be
quite useful ,so don't miss them.
============================================================================
Tweaking with virtual memory functions
--------------------------------------
Following functions are used for Virtual Memory Managemnt.They are part
of WIN32 API:
VirtualAlloc
VirtualFree
VirtualLock
VirtualUnlock
VirtualProtect
VirtualProtectEx
VirtualQuery
VirtualQueryEx
VirtualAlloc is used to allocate virtual memory in process address space.
VirtualFree is used to free virtual memory.
VirtualLock and VirtualUnlock are used for locking/unlocking pages in memory.
VirtualProtect and VirtualProtectEx are used to change protection
attributes of virtual memory pages.
VirtualQuery and VirtualQueryEx are used to query virtual memory pages.
Using this function you can retrieve all information you need about a memory
region.
VirtualProtectEx & VirtualQueryEx are extended versions of
VirtualProtect & VirtualQuery functions.They can be used to query and
modify protection attributes of memory pages in another processes address space.
Why bother with this functions? The answer is very simple.If you try to
write to a code page using WriteProcessMemory the function will fail. This
is because the OS always write-protect the code pages,protecting them from
being modified.Any attempt to write to a write protected page will generate a
Access Violation exception.
So , if I want to modify something inside a write-protected
page I must change it's protection attributes. Here is the step where
virtual memory functions can help.All you have to do is to change page
protection attributes with VirtualProtect or VirtualProtectEx.You have
to use VirtualProtect if you want to modify protection our own process
memory pages and VirtualProtectEx to work on another process.
Those function also saves old protection attribs., so then you finish
your work restore them.
You can use this method to:
1.Write self-modifiable code.
2.Modify the code pages of another process.
3.Modify functions inside system Dll's.
4.Read from read-protected areas.
5.Injecting and executing code into another process
address space.
This is not a limitative list.Just use your imagination!
Let's see now how you can modify protection attributes with
VirtualProtectEx:
BOOL VirtualProtectEx(
HANDLE hProcess,
LPVOID lpAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
We need:
1.HANDLE hProcess: A handle to target process.The handle must have
PROCESS_VM_OPERATION access.See OpenProcess for further reference.(I
usually use OpenProcess with PROCESS_ALL_ACCESS to get all possible access rights)
2. LPVOID lpAddress: A pointer to the base address of the region of
pages whose access protection attributes are to be changed.(I usually use a
DWORD with a type cast)
3.DWORD dwSize: A dword that specify the size in bytes of region whose
protection attributes are to be modificated.If dwSize excedes a page
boundary both pages will change protection attributes.
4.DWORD flNewProtect:A dword that specify new protection atributes.This
can be a one of following flags:
PAGE_READONLY Enables read access to the committed region of pages.
An attempt to write to the committed region results in
an access violation. If the system differentiates between
read-only access and execute access, an attempt to execute
code in the committed region results in an access
violation.
PAGE_READWRITE Enables both read and write access to the committed region
of pages.
PAGE_WRITECOPY Gives copy-on-write access to the committed region of pages.
PAGE_EXECUTE Enables execute access to the committed region of pages.
An attempt to read or write to the committed region results
in an access violation.
PAGE_EXECUTE_READ Enables execute and read access to the committed region
of pages.
An attempt to write to the committed region results in an access
violation.
PAGE_EXECUTE_READWRITE Enables execute, read, and write access to the
committed region of pages.
PAGE_EXECUTE_WRITECOPY Enables execute, read, and write access to the
committed region of pages. The pages are shared
read-on-write and copy-on-write.
PAGE_GUARD Pages in the region become guard pages. Any attempt to read
from or write to a guard page causes the operating system
to raise a STATUS_GUARD_PAGE exception, and turn off the guard
page status. Guard pages thus act as a one-shot access alarm.
The PAGE_GUARD flag is a page protection modifier.
An application uses it with one of the other page protection
flags, with one exception: it cannot be used with PAGE_NOACCESS.
When an access attempt leads the operating system to turn off
guard page status, the underlying page protection takes over.
If a guard page exception occurs during a system service,
the service typically returns a failure status indicator.
PAGE_NOACCESS Disables all access to the committed region of pages.
An attempt to read from, write to, or execute in the
committed region results in an access violation exception,
called a general protection (GP) fault.
PAGE_NOCACHE Allows no caching of the committed regions of pages.
The hardware attributes for the physical memory should be set
to "no cache." This is not recommended for general usage.
It is useful for device drivers; for example, mapping a video
frame buffer with no caching. This flag is a page protection
modifier, only valid when used with one of the page protections
other than PAGE_NOACCESS.
5.PDWORD lpflOldProtect: A pointer to a DWORD to save old protection
attributes.
A handle to another process is easy to obtain.You can use several methods
for this:
1.Use CreateProcess to create target process.Then retrieve the handler
from PROCESS_INFORMATION structure.(basically you use the loader method here).
2.Use of ToolHelp32 functions.This method is more elegant than previous
one. The first step here is to retrieve a PID of target process.
Use CreateToolhelp32Snapshoot,Process32First and Process32Next functions.
After you have the PID use OpenProcess to obtain a handle to target
process.(Be sure to set access flags to PROCESS_ALL_ACCESS).Warning: Toolhelp32 functions
are not portable!
3.The methods described by NaTzGUL in his essay "How to access memory of
another process".
The rest of parameters are self-explanatory.Now let's modify the
protection attributes of first code page of target process.
HANDLE hTarget; // Handle to target process
DWORD codebase = 0x0040000; // Assuming image is based at 0x0040000
DWORD oldattr; // Here we will store old prot. attribs
VirtualProtectEx( hTarget ,
LPVOID(codebase), //Change attribs of
4096, // first code page to
PAGE_EXECUTE_READWRITE, // enable read , write
&oldattr); // and execute access.
//
// Do something useful here
//
//
VirtualProtectEx( hTarget ,
LPVOID(codebase), //Restore old attr.
4096, //
oldattr, //
&oldattr);
Querying the virtual address space is another important thing.By use of
VirtualQuery and VirtualQueryEx you can get various info about memory regions in a
process address space.
Fire up Soft-Ice and type Query.Do you like the screen now? What you can
see is a map virtual address space of target process.Every memory region
is listed there with some additional info.Not only Soft-Ice can do this.
You can too!By the way: A memory region from Virtual memory API point of view
is a region whose protection attributes , type and base allocations are the same.
Let's the API:
DWORD VirtualQueryEx(
HANDLE hProcess, // handle of process
LPCVOID lpAddress, // address of region
PMEMORY_BASIC_INFORMATION lpBuffer, // address of
// information buffer
DWORD dwLength // size of buffer
);
The parameters:
1.HANDLE hProcess: Handle to target process.Must have
PROCESS_QUERY_INFORMATION
access flag set.
2.LPCVOID lpAddress: Pointer to se address of the region of pages to be
queried.
This value is rounded down to the next page boundary.
3.PMEMORY_BASIC_INFORMATION lpBuffer pointer to a
MEMORY_BASIC_INFORMATION structure to receive region info.
typedef struct _MEMORY_BASIC_INFORMATION { // mbi
PVOID BaseAddress; // base address of region
PVOID AllocationBase; // allocation base address
DWORD AllocationProtect; // initial access protection
DWORD RegionSize; // size, in bytes, of region
DWORD State; // committed, reserved, free
DWORD Protect; // current access protection
DWORD Type; // type of pages
} MEMORY_BASIC_INFORMATION;
typedef MEMORY_BASIC_INFORMATION *PMEMORY_BASIC_INFORMATION;
BaseAddress Points to the base address of the region of pages.
AllocationBase Points to the base address of a range of pages
allocated by the VirtualAlloc function. The page pointed
to by the BaseAddress member
is contained within this allocation range.
AllocationProtect Specifies the access protection given when the
region was initially
allocated. One of the following flags can be present, along with the
PAGE_GUARD and PAGE_NOCACHE protection modifier flags.
RegionSize Specifies the size, in bytes, of the region beginning at
the base
address in which all pages have identical attributes.
State Specifies the state of the pages in the region.
One of the following states is indicated:
MEM_COMMIT Indicates committed pages for which physical storage
has been allocated, either in memory or in the paging
file on disk.
MEM_FREE Indicates free pages not accessible to the calling
process and available to be allocated. For free pages,
the information in the AllocationBase, AllocationProtect,
Protect, and Type members is undefined.
MEM_RESERVE Indicates reserved pages where a range of the process's
virtual address space is reserved without allocating any
physical storage. For reserved pages, the information in
the Protect member is undefined.
Protect Specifies the access protection of the pages in the region.
One of the
flags listed for the AllocationProtect member is specified.
Type Specifies the type of pages in the region. The following types
are defined:
MEM_IMAGE Indicates that the memory pages within the region are
mapped into the view of an image section.
MEM_MAPPED Indicates that the memory pages within the region are
mapped into the view of a section.
MEM_PRIVATE Indicates that the memory pages within the region are
private (not shared by other processes).
4.DWORD dwLength: Specifies the size, in bytes, of the buffer pointed to
by the lpBuffer parameter.
Usually set to sizeof(MEMORY_BASIC_INFORMATION).
Now let's retrieve this information for the code section of target
process:
HANDLE Hare; // Handle to target process
DWORD codebase = 0x0040000; // Assuming image is based at 0x0040000
MEMORY_BASIC_INFORMATION mbi; //
VirtualQueryEx(hTarget,
LPCVOID(codebase),&mbi,sizeof(MEMORY_BASIC_INFORMATION);
After VirtualQueryEx returns you have in mbi all information you
need about code memory region of target process.
To retrieve info about all memory regions of a process address space all
you have to do is to start enumerate all memory regions from 0 to 4Gb.Initially call
VirtualProcessEx with
LPCVOID lpAddress set to 0 .After the function returns start iterate calls
to VirtualProcessEx with LPCVOID lpAddress incremented by the size of region
it queried previously.
Be sure to save away contents of MEMORY_BASIC_INFORMATION structure before
next call.Usualy I use a do - while loop.
=============================================================================
3.Short intro to Toolhelp32 functions
-------------------------------------
Nice set of functions!Unfortunately they are NOT portable to other
implementations
of WIN32 such as WIN32s or WINDOWS NT.Those functions are designed to
retrieve info about
processes and threads running in your system.They are very useful to design
debuggers for
WIN95 environment.
The keyword is "snapshot".A snapshot is an object in OS memory that lists
all
possible information about processes and thread runing.Snapshoots are
created with
CreateToolhelp32Snapshoot functions.This function returns a handle that you
can use for
future access to snapshot object.Destroying the snapshot object is very
simple.All
you have to do is to call CloseHandle function.The snapshot objects are
continue updated
by OS to reflect the real state of system,so you don't have to worry that
the information
is outdated.
They also provide an easy method to create an "Attach to process"
function.A good
tool must provide a way to dynamically attach to other processes.Using this
functions is
very easy to do it.
Those functions resides in Kernel32.dll,but they are not exported through
kernel32.lib
I think that in Visual C they are exported through tlh32.lib.You must also
locate the
appropriate header file.The linker does not link implicitly with
tlh32.lib!You have to add
the lib to your project.
The API:
CreateToolhelp32Snapshoot
Heap32First
Heap32Next
Heap32ListFirst
Heap32ListNext
Module32First
Module32Next
Process32First
Process32Next
Thread32First
Thread32Next
Toolhelp32ReadProcessMemory
After you use CreateToolhelp32Snapshoot to create system snapshot you can
use
other functions to retrieve the info.
Process32First & Process32Next are used to walk the process list in
snapshoot.They
return process information in a PROCESSENTRY32 structure.Use
Process32First to get info
about the first process,then Process32Next to retrieve info about
subsequent processes.
Use GetLastError to retrieve the error status.
This functions are the subject of one of my future essays,so I don't
present them in
details here.They are very simple to use.I put this short section here only
to provide you
an idea how to retrieve some really cool info.
============================================================================
====================
How do I....
------------
1.Write self modifiable code?
A. Call VirtualProtect on code pages you want to modify.
DWORD flNewProtect must be PAGE_WRITECOPY.
B. Write to code pages.
C.Call VirtualProtect on code pages you want to modify to
restore old attribs.(PAGE_EXECUTE)
D.Call FlushInstructionCache to empty the invalidate instruction cache.
This step is required because this portion of code may be already
in cache memory.
2.Modify the code pages of another process(single thread).
A.Get a handle to target process
B.Get handle of target process main thread.
C.Stop thread using SuspendThread.
D.Call VirtualProtectEx on code pages you want to modify.
DWORD flNewProtect must be PAGE_READWRITE.
E.Save away the code pages (not required. may you don't need
them anymore.Use ReadProcessMemory
F.Use WriteProcessMemory to write to target pages.
G.Call VirtualProtectEx on code pages you want to modify to
restore old attribs.(PAGE_EXECUTE)
H.Resume thread using ResumeThread.
3.Modify functions inside system Dll's
This is can be very dangerous.Write something wrong inside kernell32.dll
,for example,and BYE-BYE Windows.But,sometimes it may be very useful
that a
WIN API function to return without doing nothing,to return faked
values,or to
do anything but not what was supposed to do.Please take a look at
Madmax's essay
"Cracking using KERNEL32.DLL??" in Fravia's pages of reverse engineering
to get
the point. He chose to patch the file kernel32.dll itself.I don't like
this
method.I want as far as possible my system files unmodified.So I have to
tweak
with memory.Let's see how:
A. Use GetModuleHandle to get a handle to Kernel32.dll
B. Use GetProcessAdress to get the address of the function we
want to patch.
C.Call VirtualProtect on code pages you want to modify.
Use value returned by GetProcessAdress as LPVOID lpAddress
parameter.DWORD flNewProtect must be PAGE_EXECUTE_READWRITE.
D.Modify (patch the function). Write some code there.Anything
you want.I keep hoping that you know what are you doing!
E.Call VirtualProtect to restore previous protection attributes.
Enjoy your new kernel!
Let's see some code.We want to patch
HANDLE hKernel32;
DWORD oldprot;
hKernel32=GetModuleHandle("kernel32.dll");
VirtualProtect(GetProcessAdress( hKernel32,"GetDriveTypeA"),4096,
PAGE_EXECUTE_READWRITE, &oldprot);
//
//
//Write new code
//
VirtualProtect(GetProcessAdress( hKernel32,"GetDriveTypeA"),4096,
oldprot, &oldprot);
As an exercise,try to make this function to say that your HDD is a
ramdisk.
Note that the small code snippet presented above does not perform ANY
error checking.This is bad.This is very bad!I suggest an
extensive error checking and or exception handling when you
play with memory.For example if somehow the first call to
VirtualProtect fails then trying to access that memory range
to write will generate a superb AccessViolation Exception.It
will crash into pieces.I also make intensive use of
IsBadWritePtr,IsBadCodePtr,IsBadReadPtr API functions.
So please, do always !at least! a basic error checking!
============================================================================
===================
Final Notes
-----------
1.Any corrections and additions are wellcomed.Please append them at the
end of
this document and also include your name (or your nickname ).Slightly
editing minor mistakes
and typos is admitted in-place and without notice.
2.You can contact me at folowing e-mail adress ice_man81@hotmail.com
You are deep inside fravia's page of reverse engineering,
choose your way out:
Back to the HCU Papers
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?