Windows2000 User Mode Memory Scan
Sprite
Brief description:
This article briefly introduces the basic theory and implementation of the memory scanning under Windows2000.
law. Memory scans are an important technology that there is a considerable application range: such as virus scanning,
Game modification, etc. Windows2000 is a fully protected system and has two working modes.
User mode and core state (User Model and Kernel Model). Memory scans can also be divided into
User-state memory scan with the core state memory scan. This article mainly tells it to work in user state
Memory scan.
One. Related theory
Memory scanning under the neutrality of DOS is a relatively simple thing. Because DOS works
In the real mode of the CPU, there is no way to provide memory technology, as long as it is real
All physical memory in the scanning is completed, and there are some anti-drug soft in the early days.
The piece is used in this way. Of course, in order to improve efficiency, we don't have to scan all memory
Area, because some space is not used, scanning these places is also wasting time. This
It can be reached by traversing the MCB chain of the DOS system.
The use area stored, thereby greatly improved the efficiency of the scan. Similar ideas under Windows2000
The memory scan is also applicable.
Windows2000 is a fully protected system that is working in the protection mode of the CPU.
Virtual memory technology. Each process has a separate 4GB address space, where low 2GB is a private space of the process, high 2GB is a mapping of system space (if used in Boot.ini files)
"/ 3GB" switch can increase the private space of the process to 3GB, system space 1GB). for
Each process is a virtual address space is continuous, in fact they are in a page.
Discrete existence in physical memory, some may be swapped into a page file on the hard disk, and
And most of the space is uncommitted. So in Windows2000
Scanning the user space of the process must be scanned in turn. One
The low 2GB distribution of the process is as follows:
range
size
effect
0x0 ~~ 0xffff
64 KB
Unsable zones, just to prevent illegal pointers from accessing, access to this range will lead to access violations.
0x10000 ~~ 0x7ffeffff
2 GB minus at least 192 kB
Process private address space
0x7ffde000 ~~ 0x7ffdefff
4 KB
The thread environment block of the first thread in the process, TEB (Thread Environment Block)
0x7ffdf000 ~~ 0x7ffdffffff
4 KB
Process environment block, PEB (Process Environment Block)
0x7ffe0000 ~~ 0x7ffe0FFF
4 KB
A shared read-only user data block, which is mapped to a block to the system space, where some system information such as system time, the number of ticks, system version numbers, etc. When such information is accessed, the system does not switch to the core mode. 0x7ffe1000 ~~ 0x7ffeffff
60 kB
Unaccessible
0x7fff0000 ~~ 0x7fffffffff
64 kB
Unsable, used to prevent threads from buffering borders across two mode spaces
Table 1
two. achieve
As can be seen from the above table, we have to scan the starting point and the end point of 0 ~~ 2GB, but only one
section. To get this starting point and end, you can use the API function getSystemInfo, the prototype of the function is as follows: void getsysteminfo
Lpsystem_info lpsysteminfo // system information
);
There are two domains in the structural system_info: LPMINIMUMAPPLICATIONADDRESS and LPMAXIMUMAPPLICATIONADDRESS (Types are LPVOID), we can get the minimum and maximum address space available for an application. In this way, we get the starting and end point of the address to be scanned. So, is it to scan all the addresses between this starting point and the end point? This is not the case, because the next process is not such a large (close to 2GB) address space. Therefore, most of the address spaces of a process are unused or reserved, which is truly used is only those submitted.
There are three states of memory pages: unused, reserved, and submit
Committed. An unused page means that the page is not retained or submitted, for one
Cheng is unused page is irreparable, and accesses such a page will result in access to violations.
The process can require the system to keep some pages for use, the system returns a reserved address to enter
Cheng, but these addresses are also irreparable, and the process wants to use this address space, use
It must be submitted first. Only one submitted page is a page that is actually accessible. But you
Submit a page, the system will not immediately allocate the physical page immediately, only the page is first
When accessing, the system is allocated and initialized. In addition, between the three states
It can be transformed with each other. Related API functions have Virtualalloc, VirtualaLalkEx,
Virtualfree, VirtualFreeex, etc.
This way our work has been greatly reduced, just scanning those pages submitted. The next thing to do
It is a submitted page range that gets a process. This is to use two other API functions VirtualQuery and
VirtualQueryex. The functionality of the two functions is similar, and the difference is VirtualQuery just queries the process.
VirtualQueryEx can query the memory space information of the specified process, the latter is what we need, the function is originally
The type is as follows:
DWORD VirtualQueryex
Handle HProcess, // Handle to Process
LPCVOID LPADDRESS, / / Address of Region
Pmemory_basic_information lpbuffer, // information buffer
SIZE_T DWLENGTH // Size of Buffer
);
The first parameter is the handle of the process; the second parameter is the memory address pointer; the third parameter is a pointer to the MEMORY_BASIC_INFORMATION structure, which is used to return information of the memory space; the fourth parameter is the length of LPBuffer. Let's take a look at the declaration of the structure Memory_Basic_information:
Typedef struct _Memory_basic_information {
Pvoid Baseaddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T Regions;
DWORD State;
DWORD protect;
DWORD TYPE;
Memory_basic_information, * pmemory_basic_information;
The first parameter is the base address of the query memory block; the second parameter refers to the base address actually allocated when the memory is allocated with VirtualAlloc.
It can be less than BaseEaddress, that is, BaseAddress must be included within the range of allocationBase assignments; the third parameter refers to
When you match this page, some properties of the page, such as Page_Readwrite, Page_execute, etc. (other properties can be referred to Platform SDK); fourth
The parameters refer to the size of the page that starts from BaseAddress, with the same attribute. The fifth parameter refers to the state of the page, there are three possible values:
MEM_COMMIT, MEM_FREE and MEM_RESERVE, this parameter is the most important thing to us, and we can know the state of the designated memory page from it.
The sixth parameter refers to the properties of the page. Its possible value is the same as allocationProtect; the last parameter indicates the type of the memory block, there are three possible values: MEM_IMAGE, MEM_MAPPED, and MEM_PRIVATE.
This way we can get the range of addresses that need to be scanned in the process. The remaining problem here is to read the contents of the designated address space specified by the specified process. It is useful here that an API function for debugging and error handling is used. In the "Platform SDK: Debugging and Error Handling", a part of the API function associated with program debugging and error handling, there are many useful, such as the ReadProcessMemory and WriteProcessMemory we used below, whose prototype is as follows:
Bool ReadprocessMemory
Handle HProcess, // Handle To The Process
Lpcvoid LPBaseAddress, // Base of Memory Area
LPVOID LPBUFFER, // Data Buffer
Size_t nsize, // Number of bytes to read
Size_t * lpnumberofbytesread // Number of Bytes Read
);
Bool writeprocessmemory
Handle HProcess, // Handle to Process
LPVOID LPBASEADDRESS, // Base of Memory Area
LPCVOID LPBUFFER, // Data Buffer
SIZE_T NSIZE, // Count of bytes to Write
SIZE_T * lpnumberofbyteswritten // count of bytes Written
);
The parameters are very simple from their names, they can guess their meaning, not much explanation here. To illustrate, you want to perform a READPROCESSMORY operation, the current process must have Process_VM_READ access to the process to read. To perform WRITEPROCESSMORY operations for a process, the current process must have Process_VM_WRITE and Process_VM_Operation access. To get a handle of a process, some of the controls for this process can use the API function OpenProcess to get it, it does not make a detailed description, only give its prototype: Handle OpenProcess
DWORD DWDESIREDACCESS, // Access Flag
Bool BinheritHandle, // Handle Inheritance Option
DWORD dwprocessid // process identifier
);
This will basically explain the process of user address space memory scan for a process.
Three related questions:
Some problems will encounter in actual operations. If we specify a write-related access (such as
Process_vm_write, process_set_information, process_all_access, etc.
OpenProcess is open to some ordinary processes, but if you open the system security process
(Such as System, Winlogon, SMSS, CSRSS, Services, LSASS, etc.) or some registration
When the process of service, it will encounter an "access refusal" error, which is the guarantee for system security.
Make a means. The current process does not have sufficient permissions to do this. In the process control structure
There is an Access Tokens, which contains permission information for this process. Some often
The permissions used are shown in Table 1 (taken from Inside Windows2000, third Edition).
Authority name
Permissions meaning
Sebackup
By the safety check when backup
Sedebug
Can debug a process
SESHUTDOWN
Turn off the local system
Setakeownership
Get ownership of an object without free access
Table 2
To specify an arbitrary process (including system security processes and service processes), the OpenProcess operation written to write-related access, as long as the current process has Sededebug permission. If a user is administrator or the corresponding permissions, it can have this permission. However, even if we use the Administrator account to perform openprocess (Process_Access, false, dwprocessid), which will also encounter an "access rejection" error with the Administrator account. What is the reason? It turns out that some access rights of the process in the default case are not enabled, so we must do these privileges first. Some of the API functions associated with this include OpenProcessToken, LookuppprivileValue, AdjustTokenPrivileges. We want to modify a process of access tokens, first get the handle of the process to access the token, which can be obtained by OpenProcessToken, the prototype of the function, such as:
Bool OpenProcesstoken
Handle ProcessHandle,
DWORD DESIREDACCESS,
Phandle tokenhandle
);
The first parameter is to modify the process handle of access rights; the third parameter is the return token pointer returned; the second parameter specifies the type of operation you want to do, if you want to modify the token, we want to specify the second parameter to beken_adjust_privileges (Other parameters can be referred to Platform SDK). Through this function we can get the handle of the token of the current process (the first parameter of the specified function is possible). Then we can call AdjustTokenPrivileges to modify this access token. The prototype of AdjustTokenPrivilege is as follows: Bool AdjustTokenPrivileges
Handle tokenhandle, // handle to token
Bool DisableAllPrivileges, // disable option
Ptoken_Privileges NewState, // Privilege Information
DWord Bufferlength, // size of buffer
Ptoken_Privileges PreviouState, // Original State Buffer
PDWord ReturnLength // Required Buffer Size
);
The first parameter is the handle of the access token; the second parameter decision is permission to modify or except (disable) all permissions; the third parameter indicates the permissions to modify, is a pointer to the Token_PrivilegeS structure, which contains An array, each item of the data group indicates the type of permissions and the operation to be performed; the fourth parameter is the length of the structural previousstate, if previousState is empty; the parameter should be null; the fifth parameter is also a point to the token_privileges structure The pointer, store information of the access permission before modification, can be exposed; the last parameter is the size returned by the actual previousState structure. Look at the structure of Token_Privileges before using this function, its declaration is as follows:
Typedef struct _token_privileges {
DWORD privilegect;
Luid_And_Attributes Privileges [];
} Token_privileges, * ptoken_privileges;
Privilegecount refers to the number of numbers, followed by an array of Luid_and_attributes type, then look at the content of Luid_and_attributes, the declaration is as follows:
TypedEf struct _luid_and_attributes {
Luid LUID;
DWORD Attributes;
} Luid_and_attributes, * pluid_and_attributes
The second parameter indicates the type of operation we have to do, and there are three options:
SE_PRIVILE_ENABED, SE_PRIVILE_ENABED_BY_DEFAULT,
SE_PRIVILEGE_USED_FOR_ACCESS. To enable a permission to specify attributes as
SE_PRIVILEGE_ENABLED. The first parameter refers to the type of permissions, is a LUID
Value, Luid means LOCALLY UNIQUE Identifier, I think everyone is more familiar.
Understanding, and Guid's requirements to ensure the only difference between the global situation, Luid only guarantees that local unique, means that it is unique during each run of the system. Also the same as GuID
One point, Luid is also a 64-bit value, I believe everyone has seen the big string of Guid, I
How do you know how much the LUID value corresponds to a permission? This is to use
An API function lookuppprivilerage, its original shape is as follows:
Bool LookuppprivilerageGevalue (
LPCTSTR LPSYSTEMNAME, / / SYSTEM NAME
LPCTSTR LPNAME, / / Privilege Name
Pluid LPluid // LOCALLY UNIQUE IDENTIFIER
);
The first parameter is the name of the system. If it is a local system, it is ok, it is ok.
The third parameter is the pointer to Luid, and the second parameter is the name of the permissions,
Such as "SedebugPrivilege". Some permission names have also been defined in Winnt.h.
Such as:
#define se_backup_name text ("Sebackupprivilege")
#define se_restore_name text ("SerestorePrivilege")
#define se_shutdown_name text ("SESHUTDOWNPRIVILEGE")
#define se_debug_name text ("SedebugPrivilege")
Through the call of these three functions, we can use OpenProcess (Process_Access, false, dwprocessid) to get the handle of any process, and
And specify all access rights.
Summary
The memory scan of user mode is still a limit, it cannot be fully scanned
All memory spaces for Windows2000. To scan the system space, under Windows2000, the user mode application cannot be implemented. To achieve scanning for system space, must
Implemented by a program-driver working on a core mode.