Windows2000 User Mode Memory Scan

zhaozj2021-02-16  51

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.

转载请注明原文地址:https://www.9cbs.com/read-28169.html

New Post(0)