_strupr_s 和 _strlwr_s两个函数可以将字符串改为大写或者小写形式的字符串。
重要思路:
在注入进程中,可以开辟线程,然后调用宿主进程的Dll函数,进行执行某些操作,比如调用设置日志的级别。
也可以获取宿主进程的默写变量地址,然后更改这个地址的值,达到修改运行结果的效果,比如增加图像的算法结果。
抓取指定进程的快照,可以是被这个进程使用的堆,模块或者线程。
语法
``` C++
HANDLE WINAPI CreateToolhelp32Snapshot
(
_In_ DWORD dwFlags,
_In_ DWORD th32ProcessID
)
```
参数
dwFlags (in)
表示快照中包含的系统的一部分。这个参数可以是如下的一个或多个的组合。
TH32CS_INHERIT
指示这个快照句柄是可以被继承的。
TH32CS_SNAPALL
包含系统中的所有的进程和线程,以及th32ProcessID指定的堆和模块。
相当于使用TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD
TH32CS_SNAPHEAPLIST
包含在快照中th32ProcessID指定的所有的堆。要枚举这些堆,参见Heap32ListFirst。
TH32CS_SNAPMODULE
包含在快照中th32ProcessID指定的所有的模块。要枚举这些模块,参见Module32First。
如果函数返回错误码 ERROR_BAD_LENGTH,重试函数,直至成功。
对于64位windows:这个标志在32位进程中可以获取所有的32位模块,在64位进程中可以获取所有的64位模块。
要想在64进程中获取所有32位模块,请使用TH32CS_SNAPMODULE32标志。
TH32CS_SNAPMODULE32
此标志可以与TH32CS_SNAPMODULE或者TH32CS_SNAPALL一起使用。可以在64位进程中也可以获得32位的目标。
如果函数返回错误码 ERROR_BAD_LENGTH,重试函数,直至成功。
TH32CS_SNAPPROCESS
包括快照中所有的系统进程。要枚举这些进程,参见Process32First。
TH32CS_SNAPTHREAD
包括快照中所有的系统线程。要枚举这些线程,参见Thread32First。
为了确定属于特定进程的线程,当枚举线程的时候,
将进程的ID号与THREADENTRY32结构体的th32OwnerProcessID成员进行比较。
th32ProcessID (in)
指定快照中的进程的ID号。这个参数可以为0,此时表示当前进程。
当TH32CS_SNAPHEAPLIST,TH32CS_SNAPMODuLE,TH32CS_SNAPMODULE32或者TH32CS_SNAPALL被指定时生效。
否则,该参数被忽略,将自动表示使用所有进程标志。
如何指定的进程时Idle进程,或者是CSRSS系列进程中的一个,这个函数将会失败,
错误码为ERROR_ACCESS_DENIED,因为这些函数的访问限制阻止了用户级别的代码打开它们的操作。
如果指定的进程是一个64位的进程,并且调用者是32位的进程,那么函数将会失败,错误码为ERROR_PARTIAL_COPY。
返回值
如果函数成功,它返回打开的指定快照的句柄。
如果函数失败,返回INVALID_HANDLE_VALUE。
调用GetLastError函数可以获取错误信息。可能该错误码为ERROR_BAD_LENGTH。
备注
这个函数返回的快照将会被其他tool help函数使用以获取更详细的结果。
该快照的访问权限是只读。
该快照句柄作为一个目标句柄,在有效的进程和线程中必须遵守相同的规则。
要枚举所有进程的堆和模块状态,指定TH32CS_SNAPALL并且将th32ProcessID置为0。
然后,对于快照中每个附加的进程,再次调用CreateToolhelp32Snapshot,并且
指定新的进程ID以及指定TH32CS_SNAPHEAPLIST和TH32_SNAPMODULE的值。
如果获取非当前进程的包含堆和模块的快照信息的话,可能会失败,原因很多。
如果返回错误码是ERROR_BAD_LENGTH,那么再次调用。
释放该快照句柄,使用CloseHandle。
注意你可以在32或64位进程中使用QueryFullProcessImageName函数获取一个32位的可执行镜像。
示例
下面的简单的控制台程序或得了运行进程列表。
首先,GetProcessList函数使用CreateToolhelp32snapshot函数获取了当前系统中正在执行的进程的快照。
然后,它使用Process32First和Process32Next去遍历快照中的记录表中的进程。
对于每一个进程,GetProcessList函数调用ListProcessModules函数,以及ListProcessThreads函数。
printError是一个简单的错误报告函数,打印了任何失败的原因,这些原因通常来源于安全限制。
例如, OpenProcess函数会失败,对于Idle和CSRSS进程而言,因为他们的访问限制阻止了用户级别的代码去打开。
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
// Forward declarations:
BOOL GetProcessList( );
BOOL ListProcessModules( DWORD dwPID );
BOOL ListProcessThreads( DWORD dwOwnerPID );
void printError( TCHAR* msg );
int main( void )
{
GetProcessList( );
return 0;
}
BOOL GetProcessList( )
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( hProcessSnap == INVALID_HANDLE_VALUE )
{
printError( TEXT("CreateToolhelp32Snapshot (of processes)") );
return( FALSE );
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof( PROCESSENTRY32 );
// Retrieve information about the first process,
// and exit if unsuccessful
if( !Process32First( hProcessSnap, &pe32 ) )
{
printError( TEXT("Process32First") ); // show cause of failure
CloseHandle( hProcessSnap ); // clean the snapshot object
return( FALSE );
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
_tprintf( TEXT("\n\n=====================================================" ));
_tprintf( TEXT("\nPROCESS NAME: %s"), pe32.szExeFile );
_tprintf( TEXT("\n-------------------------------------------------------" ));
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
if( hProcess == NULL )
printError( TEXT("OpenProcess") );
else
{
dwPriorityClass = GetPriorityClass( hProcess );
if( !dwPriorityClass )
printError( TEXT("GetPriorityClass") );
CloseHandle( hProcess );
}
_tprintf( TEXT("\n Process ID = 0x%08X"), pe32.th32ProcessID );
_tprintf( TEXT("\n Thread count = %d"), pe32.cntThreads );
_tprintf( TEXT("\n Parent process ID = 0x%08X"), pe32.th32ParentProcessID );
_tprintf( TEXT("\n Priority base = %d"), pe32.pcPriClassBase );
if( dwPriorityClass )
_tprintf( TEXT("\n Priority class = %d"), dwPriorityClass );
// List the modules and threads associated with this process
ListProcessModules( pe32.th32ProcessID );
ListProcessThreads( pe32.th32ProcessID );
} while( Process32Next( hProcessSnap, &pe32 ) );
CloseHandle( hProcessSnap );
return( TRUE );
}
BOOL ListProcessModules( DWORD dwPID )
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
// Take a snapshot of all modules in the specified process.
hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
if( hModuleSnap == INVALID_HANDLE_VALUE )
{
printError( TEXT("CreateToolhelp32Snapshot (of modules)") );
return( FALSE );
}
// Set the size of the structure before using it.
me32.dwSize = sizeof( MODULEENTRY32 );
// Retrieve information about the first module,
// and exit if unsuccessful
if( !Module32First( hModuleSnap, &me32 ) )
{
printError( TEXT("Module32First") ); // show cause of failure
CloseHandle( hModuleSnap ); // clean the snapshot object
return( FALSE );
}
// Now walk the module list of the process,
// and display information about each module
do
{
_tprintf( TEXT("\n\n MODULE NAME: %s"), me32.szModule );
_tprintf( TEXT("\n Executable = %s"), me32.szExePath );
_tprintf( TEXT("\n Process ID = 0x%08X"), me32.th32ProcessID );
_tprintf( TEXT("\n Ref count (g) = 0x%04X"), me32.GlblcntUsage );
_tprintf( TEXT("\n Ref count (p) = 0x%04X"), me32.ProccntUsage );
_tprintf( TEXT("\n Base address = 0x%08X"), (DWORD) me32.modBaseAddr );
_tprintf( TEXT("\n Base size = %d"), me32.modBaseSize );
} while( Module32Next( hModuleSnap, &me32 ) );
CloseHandle( hModuleSnap );
return( TRUE );
}
BOOL ListProcessThreads( DWORD dwOwnerPID )
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( hThreadSnap == INVALID_HANDLE_VALUE )
return( FALSE );
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread,
// and exit if unsuccessful
if( !Thread32First( hThreadSnap, &te32 ) )
{
printError( TEXT("Thread32First") ); // show cause of failure
CloseHandle( hThreadSnap ); // clean the snapshot object
return( FALSE );
}
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
do
{
if( te32.th32OwnerProcessID == dwOwnerPID )
{
_tprintf( TEXT("\n\n THREAD ID = 0x%08X"), te32.th32ThreadID );
_tprintf( TEXT("\n Base priority = %d"), te32.tpBasePri );
_tprintf( TEXT("\n Delta priority = %d"), te32.tpDeltaPri );
_tprintf( TEXT("\n"));
}
} while( Thread32Next(hThreadSnap, &te32 ) );
CloseHandle( hThreadSnap );
return( TRUE );
}
void printError( TCHAR* msg )
{
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError( );
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, eNum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
sysMsg, 256, NULL );
// Trim the end of the line and terminate it with a null
p = sysMsg;
while( ( *p > 31 ) || ( *p == 9 ) )
++p;
do { *p-- = 0; } while( ( p >= sysMsg ) &&
( ( *p == '.' ) || ( *p < 33 ) ) );
// Display the message
_tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg );
}打开一个已经存在的本地进程对象。
语法
``` C++
HANDLE WINAPI OpenProcess
(
_In_ dWORD dwDesireAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwProcessId
);
```
参数
dwDesiredAccess (in)
对进程对象的访问级别。这个访问权限由进程的安全描述符检查。
这个参数可以是process access rights中的一个或多个。
如果调用者开启了SeDebugPrivilege特权,那么不管安全描述符的内容是什么,请求权限都将授予。
bInheritHandle (in)
如果这个值为真,那么此进程创建的进程将会继承这个句柄。否则子进程无法继承。
dwProcessId (in)
将要被打开的进程的ID号。
如果指定的进程ID号为System进程(0x00000000),该函数会失败,并且设置错误码ERROR_INVALID_PARAMETER。
如果指定的进程是Idle进程,或者CSRSS系列进程中的一个或多个,这个函数会失败,错误码ERROR_ACCESS_DENIED,
因为它们的访问权限不允许用户打开它们。
如果你使用GetCurrentProcessId作为这个函数的参数,考虑使用GetCurrentProcess代替OpenProcess,效率更高。
返回值
如果函数成功,返回值是该进程的句柄。
如果函数失败,返回值是NULL。调用GetLastError获取更多信息。
备注
为了打开一个本地其他进程的句柄,并且获取所有权限,你必须开启SeDebugPrivilege权限。
获取更多信息,参见Changing Privileges in a Token。
该函数返回的进程句柄和平时的进程句柄一模一样。
CloseHandle释放句柄。
保留,提交或者改变指定进程的虚拟地址的内存区域的状态。
该函数会自动将分配的内存置0
语法
```C++
LPVOID WINAPI VirtualAllocEx
(
_In_ HANDLE hProcess,
_In_opt LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
```
参数
hProcess (in)
进程的句柄。该函数分配该进程的虚拟地址空间的内存。
句柄必须有PrOcESS_VM_OPERATION的访问权限。获取更多信息,参见ProcessSecurityandAccessRights。
lpAddress (in, optinal)
指定一个你想要分配的页面区域的预期的起始地址。
如果你要保留内存,该函数将会降低舍入这个地址到分配力度的倍数。
如果你要提交已经保留的内存,该函数将降低舍入最近的页边缘。
为了决定一个页面的大小合集宿主的分配力度,使用GetSystemInfo函数。
如果lpAddress是NULL,该函数自动决定去哪里分配该区域。
dwSize (in)
要分配的内存区域的字节大小。
如果lpAddress是NULL,该函数将会将dwSize上升舍入到下一个页面边缘。
如果lpAddress不是NULL,该函数将会分配在lpAddress到lpAddress+dwSize的区间内的
所有的页面。
flAllocationType (in)
内存分配的类型。这个参数必须包含如下值中的一个。
MEM_COMMIT
为指定的保留内存页分配内存费用(从总体的内存以及磁盘页文件)。
该函数也保证,当调用者延后第一次访问这个内存,这个内存将会是0。
实际的物理页面将不会分配除非虚拟地址已经真正地被访问了。
为了一次性地保留和提交页面,调用VirtualAllocEx函数,使用MEM_COMMIT | MEM_RESERVE参数。
通过指定MEM_COMMIT而没有MEM_RESERVE,加上一个非NULL的lpAddress,然后尝试提交一个特定地址范围,
将会失败,除非整个范围已经被保留。错误码为ERROR_INVALID_ADDRESS。
尝试提交一个已经提交的页面不会导致函数失败。
这意味着你可以不用判断每个页面的当前提交状态就可以去提交页面了。
如果llpAddress指定一个带有enclave的地址,flAllocationType必须为MEM_COMMIT。
MEM_RESERVE
保留进程的虚拟地址空间的一个范围,而没有分配任何实际的物理存储在内存中或者在磁盘的页面文件中。
你通过再次调用带MeM_COMMIT的VirtualAllocEx函数提交保留页面。
为了一次性的保留和提交,你可以调用带有MEM_COMMIT | MEM_RESERVE的VirtualAllocEx。
其他的内存分配函数,比如malloc和LocalAlloc函数,不能使用保留内存,直到它已经释放。
MEM_RESET
指示着在lpAddress和dwSize所指定的内存范围的数据不再感兴趣。
该页面不应该被读或者被写入。
然而,内存块以后将会被再次使用,所以它不应该被解提交。这个值不能与其他任何值一起使用。
使用这个值不保证MEM_RESET的范围操作会包含0.
如果你希望这个范围包含0,解提交这个内存,然后重新提交它。
当你使用MEM_RESET的时候,VirtualAllocEx函数忽略fProtect。
然而,你必须设置fProtect为一个有效的保护权限值,比如PAGE_NOACCESS。
VirtualAllocEx返回错误,如果你使用MEM_RESET并且内存范围被映射到一个文件。
一个共享的视角仅仅当映射到页文件时才能被接收。
// 其他的后续再加
flProtect (in)
将要分配的页面区域的内存保护。如果页面正在提交,你可以指定memory protection constants的任何一个。
返回值
如果函数成功,返回值是分配的页面区域的起始地址。
如果番薯失败,返回值是NULL。GetLastError可以获取错误信息。
备注
每个页面都有一个页面状况。VirtualAllocEx函数可以执行如下操作:
- 提交保留页面的的区域
- 保留空闲页面区域
- 空闲页面区域的错综复杂地保留和提交
VirtualAllocEx不能保留已经保留的页面,但是可以提交已经提交过的区域。
这意味着你可以提交一个页面区域,而不用管它是否已经被提交过了,并且函数将不会失败。
你可以使用VirtualAllocex函数去保留一个页面区域块,并且从这个保留块中调用多次VirtualAlllocEx去提交
各自的页面。这样可以使得进程保留一个虚拟地址空间的范围,而不用消费物理存储除非需要的时候。
如果lpAddress参数不是NULL,该函数使用lpAddress和dwSize参数去计算分配的页面区域。
整个页面返回的当前状态必须与flAllocationType参数兼容。
否则,函数失败并且没有页面被分配。
这个兼容需求不需要阻止提交已经提交过的页面。参见前面的列表。
为了动态地执行产生的代码,使用VirtualAllocEx去分配内存并且用VirtualProtectEx函数去赋予PAGE_EXECUTE权限。
VirtualAllocEx函数可以被用来保留一个AddressWindowingExtensions内存区域,使用特定进程的虚拟地址空间。
这块内存可以根据应用程序的需求将物理页面映射到(出)虚拟内存中。MEM_PHYSICAL和MEM_RESERVE的值必须被
设置到AllocationType中,MEM_COMMIT一定不能被设置。
页面保护权限一定要被设置为PAGE_READWRITE。
VirtualFreeEx函数可以解提交一个已经提交的页面,释放页面的内存,或者
它可以同时解提交并且释放页面。
它也可以释放一个保留的页面,将其置为一个自由页面。
当创建一个可执行的区域时,一旦代码就位,
调用程序通过适当地调用FlushInstructionCache函数背负着保证缓存一致性的责任。
否则在新的执行区域之外的代码被执行的时候可能产生不可预测的结果。
在特定进程的内存区域中写入数据。
被写入的整个数据必须是可访问的或者操作失败。
语法
BOOL WINAPI WriteProcessMemory
(
_In_ HANDLE hProcess,
_In_ LPVOID lpBaseAddress,
_In_ LPCVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_ SIZE_T *lpNumberOfBytesWritten
)参数
hProcess (in)
将要修改的内存所属的进程的句柄。
这个句柄必须有对该进程的PROCESS_VM_WRITE和PROCESS_VM_OPERATION的访问权限。
lpBaseAddress (in)
要写入数据的特定进程的首地址的指针。
在数据转移动作发生之前,系统会检验所有在该基址的数据以及指定大小的内存都是有效的。
如果这些是不可访问的,函数返回失败。
lpBuffer (in)
包含数据的指针,这个数据将会被写入特定进程的地址空间中。
nSize (in)
要写入特定进程的数据的字节数目。
lpNumberOfBytesWritten (out)
指向一个变量,这个变量接收转移到特定进程的数据的字节数。
这个参数是可选的。如果这个参数是NULL,这个参数被忽略掉。
返回值
如果函数成功,返回值是非零。
如果函数失败,返回值是0。调用GetLastError获取更多信息。
如果请求的写入操作穿越到不可访问的进程的一个区域,那么将会失败。
备注
WriteProcessMemory函数将数据从当前的进程的特定的缓存中拷贝到特定进程的内存返回中去。
任何进程都可以调用这个函数,只要这个进程有一个带有PROCESS_VM_WRITE和PROCESS_VM_OPERATION
去访问这个进行的写入权限的句柄。
典型地但是不总是,带有将要写入的地址空间的进程正在被调试。
整个被写入的区域必须可以访问,如果不能访问,则函数失败。
创建一个线程,这个线程运行在另外一个进程的虚拟地址空间中,可以指定一个附加属性。
语法
HANDLE CreateRemoteThreadEx(
_In_ HANDLE hProcess,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_In_opt_ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
_Out_opt_ LPDWORD lpThreadId
);参数
hProcess (in)
线程将会被创建的宿主进程的句柄。
这个句柄必须有PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_VM_READ访问权限。
llpThreadAttributes(in, optional)
指向SECURITY_ATTRIBUTES结构体的指针,这个指针指示着新的线程的安全描述符,
并且决定着那个是否子进程可以继承这个返回句柄。
如果lpThreadAttributes是NULL,线程获取一个默认安全描述符,并且句柄不能被继承。
线程的访问控制列表在默认安全描述符中来自于创建者最初的记号。
dwStackSize (in)
栈空间大小,以字节为准。系统舍入这个值到最近的页面。
如果该值为0,新的线程使用默认的大小去执行。
获取更多信息,参见Thread Stack Size。
lpStartAddress (in)
指向用户定义的LPTHREAD_START_ROUTINE类型的函数的指针,它将被线程执行,并且代表
远程进程的线程的起始地址。
这个函数必须存在于远程进程中。
获取更多信息,参见ThreadProc。
lpParameter (in, opt)
指向一个变量,这个变量传递给指向lpStartAddress的线程函数。这个参数可以为NULL。
dwCreationFlags (in)
控制着线程创建的一些标志。
0
线程在创建之后立即执行
CREATE_SUSPENDED
线程创建之后不会运行,知道调用ResumeThread函数。
STACK_SIZE_PARAM_IS_A_RESERVATION
dwStackSize参数指定栈空间的初始化大小。
如果没有指定这个标志,dwStackSize指定为提交大小。
lpThreadId
指向一个变量,这个变量接收线程标识符
如果这个参数为NULL,线程标识符不返回。
返回值
如果函数成功,返回值是新创建的线程的句柄。
如果函数失败,返回NULL。
GetLastError可以获取更多信息。
备注
CreateRemoteThreadEx可以产生一个特定进程的一个地址空间的一个新线程。
这个线程可以访问这个进程打开的所有目标。
lpAttribute参数可以用来指定扩展属性,比如为新线程指定处理器群亲和性。
如果lpAttribute是NULL,该线程的行为如同CreateRemoteThread。
***其他备注太多,后续添加***