VC to achieve screen truncation function method in detail

  • 2020-04-02 02:25:15
  • OfStack

The text on the screen in VC programming is mostly displayed by the following functions of gdi32.dll: TextOutA, TextOutW, ExtTextOutA, ExtTextOutW. The key to screen capture is to intercept the calls to these functions and get the arguments the program sends them.

The implementation method has the following three steps:

Get the current position of the mouse
Through SetWindowsHookEx.

Second, the mouse under the window to redraw the message, let it call the system function to redraw
Windows from point, ScreenToClient, InvalidateRect.

3. Intercept calls to system functions and obtain parameters (take TextOutA as an example)

1. Create MyTextOutA as a function of TextOutA, with the same parameters and return value as TextOutA, and place it in the DLL where the system hook is.


SysFunc1=(DWORD)GetProcAddress(GetModuleHandle("gdi32.dll"),"TextOutA");
BOOL WINAPI MyTextOutA(HDC hdc, int nXStart, int nYStart, LPCSTR lpszString,int cbString)
{ //Output the processing of lpszString
return ((FARPROC)SysFunc1)(hdc,nXStart,nYStart,lpszString,cbString);
}

2. Since the system mouse hook has finished injecting other GUI processes, we do not need to do any more work for injection.
If you know that all system hook functions must be in a dynamic library, you won't be surprised by "injection". When a process implicitly or explicitly calls a function in a dynamic library, the system maps the dynamic library to the process's virtual address space (" address space "). This makes the DLL part of the process, executing as the process, using the process's stack.

The DLL maps to the virtual address space

For system hooks, the system automatically maps the DLL containing the "hook callback function" into the address space of all processes affected by the hook function, into which the DLL is injected.

3. When a DLL containing hooks is injected into another process, find the base address of each module (EXE and DLL) mapped to the process's virtual memory. Where EXE and DLL are mapped to virtual memory space is determined by their base address. Their base addresses are determined by the linker at link time. When you create a new Win32 project, the VC++ linker USES the default base address 0x00400000. You can change the BASE address of a module by using the BASE option of the linker. EXE is typically mapped to 0x00400000 of virtual memory, and the DLL subsequently has a different base address, which is typically mapped to the same virtual address space for different processes.

So how do you know where EXE and DLL are mapped to?
In Win32, HMODULE and HINSTANCE are the same. They are the base address of the virtual memory space where the corresponding module is loaded into the process. Such as:


HMODULE hmodule=GetModuleHandle("gdi32.dll");

When the returned module handle is cast to a pointer, it is the base address where the gdi32.dll was loaded.

For how to find out which DLLS are mapped to the virtual memory space, we can achieve this by:


while(VirtualQuery (base, &mbi, sizeof (mbi))>0)
{
if(mbi.Type==MEM-IMAGE)
ChangeFuncEntry((DWORD)mbi.BaseAddress,1);
base=(DWORD)mbi.BaseAddress+mbi.RegionSize;
}

4. After obtaining the base address of the module, exhaust the image-import-descriptor array of the module according to the format of PE file to see whether gdi32.dll is introduced. If so, exhaust the image-thunk -DATA array to see if the TextOutA function is introduced.

5. If found, replace it with its own function.
The system maps EXE and DLL intact into the virtual memory space, and their structure in memory is the same as the static file structure on disk. PE (Portable Executable) file format.
All calls to a given API function are always passed through the same place in the executable. That's the import address table for a module (EXE or DLL). There are the function names and addresses of all the other DLLS that this module calls. Function calls to other DLLS actually just jump to the input address table, which then jumps to the DLL's actual function entry. Such as:

The call to MessageBox() jumps to the input address table, from which it jumps to the MessageBox function

Image-import-descriptor and image-thunk -data correspond to DLLS and functions, respectively. They are the format of the input address table for the PE file (see winnt.h for the data structure).


BOOL ChangeFuncEntry(HMODULE hmodule)
{
PIMAGE-DOS-HEADER pDOSHeader;
PIMAGE-NT-HEADERS pNTHeader;
PIMAGE-IMPORT-DESCRIPTOR pImportDesc;
/ get system functions and my functions ' entry /
pSysFunc1=(DWORD)GetProcAddress(GetModuleHandle("gdi32.dll"),"TextOutA");
pMyFunc1= (DWORD)GetProcAddress(GetModuleHandle("hookdll.dll"),"MyTextOutA");
pDOSHeader=(PIMAGE-DOS-HEADER)hmodule;
if (IsBadReadPtr(hmodule, sizeof(PIMAGE-NT-HEADERS)))
return FALSE;
if (pDOSHeader->e-magic != IMAGE-DOS-SIGNATURE)
return FALSE;
pNTHeader=(PIMAGE-NT-HEADERS)((DWORD)pDOSHeader+(DWORD)pDOSHeader->e-lfanew);
if (pNTHeader->Signature != IMAGE-NT-SIGNATURE)
return FALSE;
pImportDesc = (PIMAGE-IMPORT-DESCRIPTOR)((DWORD)hmodule + (DWORD)pNTHeader->OptionalHeader.DataDirectory[IMAGE-DIRECTORY-ENTRY-IMPORT].VirtualAddress);
if (pImportDesc == (PIMAGE-IMPORT-DESCRIPTOR)pNTHeader)
return FALSE;
while (pImportDesc->Name)
{
PIMAGE-THUNK-DATA pThunk;
strcpy(buffer,(char )((DWORD)hmodule+(DWORD)pImportDesc->Name));
CharLower(buffer);
if(strcmp(buffer,"gdi32.dll"))
{
pImportDesc++;
continue;
}else{
pThunk=(PIMAGE-THUNK-DATA)((DWORD)hmodule + (DWORD)pImportDesc->FirstThunk);
while (pThunk->u1.Function)
{ 
if ((pThunk->u1.Function) == pSysFunc1)
{ 
VirtualProtect((LPVOID)(&pThunk->u1.Function),
sizeof(DWORD),PAGE-EXECUTE-READWRITE,&dwProtect);
(pThunk->u1.Function)=pMyFunc1;
VirtualProtect((LPVOID)(&pThunk->u1.Function), sizeof(DWORD),dwProtect,&temp);
}
pThunk++; 
} 
return 1;
}
}
}

After replacing the entry of TextOutA in the input address table with MyTextOutA, the main part of the intercepting system function call has been completed. When an injected process calls TextOutA, it actually calls MyTextOutA. It only needs to display the passed string in MyTextOutA and then send it to TextOutA for processing.


Related articles: