Windows 95 System Programming Secrents Learning Notes --- Chapter 3 (2)

xiaoxiao2021-03-06  44

API function related to module

Related function list:

l getModuleusage

l getProcaddress

l getModuleHandle

l getModuleFileName

l loadingLibrary

l freeelibrary

GetModuleusage

This is an old function for 16 windows, it is no longer available.

GetProcaddress and IgetProcaddress

GetProcaddress is a key function in Win32 programming because it is a way to dynamically load DLL (the opposite method is called implicit load, Implicitly Link). As long as the specified module (HMODULE) and functions (name or serial number), getProcAddress returns the function entry address. In order to complete the task, getProcaddress first must find the module Database of a particular module, and then access its output function table to get the function entry address.

GetProcAddress actually only performs parameter verification. It first confirms that the LPSZProc parameter is a string or serial number. If the high word is 0, the low Word is the serial number. If the high position is not 0, LPSZProc is considered a PSTR, so getProcAddress scans the string and looks for NULL end. If the PSTR does not meet the requirements, the scanning process will exceed the abnormal situation, and the structured exception handler is captured, and then 0 (indicates failure). If everything goes well, the control will be passed to IgetProcadDress, where the function entry address is really obtained.

IgetProcAddress puts the action management of the search function entry address on a high-level surface, leaving trivial work to two lower level functions. IGetProcaddress first makes some thread synchronous control actions to ensure that the current thread will not be discontinued by inappropriate. Next, it calls mrfromhlib to get a pointer and point to Modref. (MRFROMHLIB is the internal function of the kernel32) and then starting the Modref linked list of the current process to find the HModule that meets the requirements. After found, IgetProcaddress is used to use the index value of the module in the Modref structure, find the corresponding IMTE.

The second action of IgetProcaddress is to find the function address. Since IgetProcAddress may be a function name or serial number as a parameter, it must first decide which parameter can be called to call the underlying function. If the serial number came, X_FindAddressFormatexportordORDINAL is called; if it is incoming a string, call x_findaddressformateXportName. If both cases, the function addresses are not found, IgetProcAddress returns an error diagnostic information and pass back 0.

If you look for the kernel32 function, IgetProcAddress does not allow you to find functions in serial numbers. This is because Kernel32 has a pile of unputued functions and is only derived from the serial number. Since these function names are not in the kernel32.dll, they do not exist among the IMPORT library of Kernel32, so the application cannot call these Microsoft's reserved reservations.

Does this mean that if you know the serial number of the unapproved function, can you call? No, IGETPROCADDRESS blocks a terrible code to stop this, it does not allow GetProcAddress to use the kernel32 function serial number. Let us go back to the topic. When the function address is successfully found, IgetProcAddress will not end this. For some strange reason, when a process is loaded by Debugger, any System DLLS (located in 2GB address space), which is called by a special code (by the loader dynamically). The purpose of these codes is to prevent Debugger from entering the Ring3 System DLLS. For implicit links, the loader handles each necessary action: however, if you use the display link (that is, using getProcAddress), the program calls the function pointer returned by getProcAddress, so skip the special code. Therefore, getProcAddress must check if the program is in debug status: If the address obtained by IgetProcadDress is in 2GB, IgetProcAddress will first look for the corresponding special code and pass back the start address of the code.

If the function is not found, IgetProcadDress sets the error code, let GetLastError return ERROR_PROC_NOT_FOND. Finally, IgetProcAddress leaving Critical Section (which is the synchronization control mechanism used in the beginning of the function).

X_FindAddressFromExportordordinal

X_findaddressFromExportordordinal (not Microsoft's official name, the author's own itself) is a core function of kernel32. He is not only called by getProcadDress, and is also called by the PE loader (when the call to the implicit link is called).

X_FindAddressFromExportordORDinal relies on the content of Image_nt_headers and .edata in the PE document. They are all mapped to memory to build modules. So, studying the format of the PE document becomes more important - even if you don't intend to do something in the PE format.

Although there is quite a lot of code among x_findaddressFromExportordinal, its principle is actually relatively simple. In the module's export table, you can get an RVA (Relative Virtual Address array to describe each export function in the module. This array is called Export Address Table. The first element in the array contains the RVA of the function of the output number 1, and the second element contains the RVA of the output number 2 having a function of 2, and so on. X_FindAddressFromExportordORDinal The only thing to do is to enter the array to get the corresponding RVA, then add the RVA to the base address of the module so that it is a linear address available. However, there are two points to pay attention.

The first point is that x_findaddressFromExportordORDinal needs to calculate the serial number base address. In the PE document, the lowest output serial number is the base address. This allows the table that places the output function address to be smaller. For example, we assume that the output number of a DLL is from 100 to 109. Intuitive ideas require an array of 110 elements, and only 10 is used. In order to save space, the linker sets the serial number base address is 100, so the array is sufficient to be in 10 elements. After finding an export function, x_findaddressFromExportordORDinal is to remember to add the serial base address to an index value to become a true index value. The second point is that x_findaddressFromExportordORDinal must process the forward function. There will be detailed explanation after the transfer function. Currently, you just know, the so-called translation function is an alias for [Export Functions in another DLL]. For example, the HeapAlloc function of Windows NT Kernel32.dll is transferred to NTLAllocateHeap functions of NTDLL.DLL. The address of the transfer function in the export function address array is always placed in .edata section. It is not the address of the export function, but points to a string like NTDLL.rtLallocateHeap. If X_FINDDRESSFROMEXPORTORTORDINAL sees such a thing, it takes out the module name and function name, and then calls GetProcaddress again as the parameter. Yes, if you look for a transfer function with getProcAddress, it is possible to fall into Recursive.

X_FindAddressFromExportName

This is the companion of X_FindAddressFromExportName. The difference between these two functions is that this function is a lookup object. The first part is the same as the previous function.

The true meaning of the x_findaddressfromExportName function is where it looks for function names to match the lpszProc parameter. If you find an anastomotive string, this function converts the string array index to the index of the output function address table according to the addressofnameordinals array. Then, X_FindAddressFromExportName can find the exported RVA and pass back to its caller. Therefore, this function is actually handed over to X_FindAddressFromExportordORDINAL, making it what it should do.

Simply say once, the function address can be obtained according to the name or serial number. However, in the underlying action, the address is always looking for sequence numbers. When you give a function name to getProcAddress, or enter a function with a name, Kernel32 is only injecting an extra step, and the string is first converted to the serial number.

GetModuleFileName and IgetModuleFileName

GetModuleFileName Accepts an HMODule parameter to return the full path to the corresponding EXE or DLL. GetModuleFileNamea itself is very simple, but only the confirmation action of the parameter. After confirming that the LPSZPATH parameter (ready to store the file name) is the legitimate value, getModuleFileName jumps to the IgetModuleFileName function.

Note:

The function name finally belts "a", indicating that this is an ANSI string. If "W" means this is a Unicode string.

In addition to the subject of file name, IgetModuleFileName is very simple. All what it needs to do is to put the full file name from the correct IMTE copy output buffer. However, because each process thinks it has its own module array, IgetModuleFileName cannot find PModuletableArray directly. IgetModuleFileName uses mrfromhlib to find the Modref of this module. With MODREF, IgetModuleFileName uses MTEINDEX to enter PModuletableArray and get its IMTE pointer. Once there is an Imte pointer, the rest is to copy the string in the IMTE's PSZFileName to the buffer - That is a parameter for getModuleFileName. GetModuleHandle and IgetModuleHandle

The GetModuleHandle function performs the opposite work of getModuleFileName. Given a module name, this function returns its corresponding hmodule. Unfortunately, the interpretation of the module name is a bit blurred in the Microsoft document. However, in general, the module name can be an essential name of the EXE or DLL, or a full path. If the extension is a DLL, it can be omitted. Therefore, the following four cases can be said to be a module name of C: /Windows/system/User32.dll

N USER32

N user32.dll

N c: / windows / system / user32

N c: /windows/system/User32.dll

The real getModuleHandle function code is short, it just confirms that the lpszModule parameter is a legitimate string pointer. If this is true, getModuleHandle jumps to iGetModuleHandle. Just like IgetModuleFileName, the core of IgetModuleHandle also has a part of the conversion between ANSI and OEM strings (if needed). This part of the code first changes the module name to uppercase so that the contrast will be quickly. Next, check if there is an extension in the end. If you do not have an extension, you will automatically add a .dll.

The next function core code calls two auxiliary functions: x_getmodreffromfilename and x_gethmodulefrommodref. First, x_getmodreffromFileName scans the Modrefs linked list of the current process until you find an anastomotive file name, then return to the Modref's pointer. Next, X_GethModuleFromModRef gets a PMODREF parameter and returns the corresponding hmodule.

X_GETMODREFFROMFILENAME

This function scans the Modrefs linked list of the current process and compares the file names and function parameters lpszModName. If you agree, return PMODREF, otherwise it will return NULL.

Interestingly, the string of X_GetModReffromFileName is not just once, nor is it two, but four. The first comparison base name (for example, kernel32.dll), if it fails, compare the complete path. If it fails, the second copy of the base name is compared, and the second copy of the full path. As long as there is a successful success, return the corresponding Modref pointer.

In order to speed up comparison, X_GETMODREFFROMFILENAME first calculates the length of the string parameter. Due to the string stored in the Modref structure, the length has been recorded in the structure, so X_GETMODREFFROMFILENAME first complies with the length. If the length does not match, it is not enough. X_GethModuleFromModRef

This function requires a PMODREF as a parameter, returns the corresponding hmodule (that is, the base address). The function will take out the pointer to MODULE DATABASE (an Image_NT_Headers structure) from Modref, then remove the module base address from the structure, that is, a hmodule.

Kernel32 object

K32 object is a key system data structure, stored in Kernel32 HEAP. There are a variety of K32 objects, all start with the same head. Deciding whether it is a K32 object method is to ask questions: Is it in the application to represent this object with Handle? For example, an application can have File Handles or Event Handles, so File and Event are K32 objects.

supplement:

The K32 object mentioned here is actually a kernel object, and the kernel object can only be accessed by kernel, and the application can access these kernel objects through the handle of the kernel object, but cannot directly change the contents of these kernel objects. And these handles are related to the process, passing a handle of a process to another process, but can share a kernel object handle through some special ways to cross the process boundary (see: "Windows Core Programming" third chapter).

The kernel object (ie k32 object) is owned by the system kernel and is not owned by the process. The system uses a reference counter to record how many processes are using a kernel object. When a kernel object is created, its reference count is 1, when another process uses the kernel object, its reference count is incremented 1, when the process terminates, the system automatically decrements the counter of all kernel objects used by the process (each Secondary decreases), when a kernel object is 0, the system automatically revoke the kernel object.

In Windows NT / 2000 and its subsequent systems, kernel objects can be protected by security descriptors.

It should be noted that windows, menus, fonts, and GDI objects are not kernel objects.

When a process is initialized, the system assigns a handle table, which is the module chain list mentioned in this article. It is different for Windows 98/2000 / CEs, and Microsoft does not provide the corresponding documentation.

Each K32 object begins in a common head. This header has the following structural members:

00h dword

Object Type, this value determines how the subsequent structure is explained

04h dword

This is the reference count of the kernel object, which represents the number of times the object is used.

In the subsequent part, we will focus on process objects and thread objects. A Process Database is actually a k32_proces object, and a Thread Database is actually a K32_thread object. A process handle table is actually an array of pointers. Each pointer points to a wide variety of K32 objects.

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

New Post(0)