读书笔记--[计算机病毒解密与对抗]
- 遍历进程&线程程序
- 终止进程
- 获取进程信息
- 获取进程内模块信息
- 获取进程命令行参数
代码运行环境:Win7 x64
VS2012 Update3
遍历系统中所有进程
- #include <stdio.h>
- #include <windows.h>
- #include <TlHelp32.h>
- int main()
- {
- // 为进程的所有线程拍个快照
- HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if( hSnapshort==INVALID_HANDLE_VALUE )
- {
- printf("CreateToolhelp32Snapshot调用失败!\n");
- return -1;
- }
- // 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
- PROCESSENTRY32 stcProcessInfo;
- stcProcessInfo.dwSize = sizeof(stcProcessInfo);
- BOOL bRet = Process32First(hSnapshort, &stcProcessInfo);
- printf("进程名\t\t\t 进程ID\t 线程数\t 父进程ID\n");
- while (bRet)
- {
- printf("%ls\t\t %d\t %d\t %d\n", stcProcessInfo.szExeFile, stcProcessInfo.th32ProcessID, stcProcessInfo.cntThreads, stcProcessInfo.th32ParentProcessID);
- bRet = Process32Next(hSnapshort, &stcProcessInfo);
- }
- CloseHandle(hSnapshort);
- system("pause");
- return 0;
- }
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
int main()
{
// 为进程的所有线程拍个快照
HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if( hSnapshort==INVALID_HANDLE_VALUE )
{
printf("CreateToolhelp32Snapshot调用失败!\n");
return -1;
}
// 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
PROCESSENTRY32 stcProcessInfo;
stcProcessInfo.dwSize = sizeof(stcProcessInfo);
BOOL bRet = Process32First(hSnapshort, &stcProcessInfo);
printf("进程名\t\t\t 进程ID\t 线程数\t 父进程ID\n");
while (bRet)
{
printf("%ls\t\t %d\t %d\t %d\n", stcProcessInfo.szExeFile, stcProcessInfo.th32ProcessID, stcProcessInfo.cntThreads, stcProcessInfo.th32ParentProcessID);
bRet = Process32Next(hSnapshort, &stcProcessInfo);
}
CloseHandle(hSnapshort);
system("pause");
return 0;
}
遍历系统中所有线程
- #include <stdio.h>
- #include <windows.h>
- #include <TlHelp32.h>
- int main()
- {
- // 为进程的所有线程拍个快照
- HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
- if( hSnapshort==INVALID_HANDLE_VALUE )
- {
- printf("CreateToolhelp32Snapshot调用失败!\n");
- return -1;
- }
- // 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
- THREADENTRY32 stcThreadInfo;
- stcThreadInfo.dwSize = sizeof(stcThreadInfo);
- BOOL bRet = Thread32First(hSnapshort, &stcThreadInfo);
- DWORD dwProId = -1; // 保存上一个线程的进程ID
- unsigned unCount=0;
- while (bRet)
- {
- if( dwProId!=stcThreadInfo.th32OwnerProcessID )
- {
- // 记录PID与所属PID不同,遍历至不属于同一进程的线程
- if( dwProId!=-1 )
- {
- // 不是第一次遍历
- printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
- unCount = 0;
- printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
- }
- dwProId = stcThreadInfo.th32OwnerProcessID;
- printf("进程%d:\n\n ", dwProId);
- printf("线程TID\t\t线程所属进程PID\t\t线程优先级\n");
- }
- printf(" %d\t\t\t%d\t\t\t %d\n",stcThreadInfo.th32ThreadID, stcThreadInfo.th32OwnerProcessID, stcThreadInfo.tpBasePri);
- unCount++;
- bRet = Thread32Next(hSnapshort, &stcThreadInfo);
- }
- printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
- unCount = 0;
- printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
- CloseHandle(hSnapshort);
- system("pause");
- return 0;
- }
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
int main()
{
// 为进程的所有线程拍个快照
HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if( hSnapshort==INVALID_HANDLE_VALUE )
{
printf("CreateToolhelp32Snapshot调用失败!\n");
return -1;
}
// 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
THREADENTRY32 stcThreadInfo;
stcThreadInfo.dwSize = sizeof(stcThreadInfo);
BOOL bRet = Thread32First(hSnapshort, &stcThreadInfo);
DWORD dwProId = -1; // 保存上一个线程的进程ID
unsigned unCount=0;
while (bRet)
{
if( dwProId!=stcThreadInfo.th32OwnerProcessID )
{
// 记录PID与所属PID不同,遍历至不属于同一进程的线程
if( dwProId!=-1 )
{
// 不是第一次遍历
printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
unCount = 0;
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
}
dwProId = stcThreadInfo.th32OwnerProcessID;
printf("进程%d:\n\n ", dwProId);
printf("线程TID\t\t线程所属进程PID\t\t线程优先级\n");
}
printf(" %d\t\t\t%d\t\t\t %d\n",stcThreadInfo.th32ThreadID, stcThreadInfo.th32OwnerProcessID, stcThreadInfo.tpBasePri);
unCount++;
bRet = Thread32Next(hSnapshort, &stcThreadInfo);
}
printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
unCount = 0;
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
CloseHandle(hSnapshort);
system("pause");
return 0;
}
在win程序设计中除了使用ToolHelp函数可以完成进程的遍历操作之外(程序需要包含tlhelp32.h),还可以使用Psapi函数EnumProcess函数,但是它只能获取进程的ID。
进程相关操作--终止进程
void WINAPI ExitProcess(
UINT uExitCode; // 进程退出码
);
在程序的任意一个地方调用这个函数都会立即终止自身进程的运行。在C/C++中应避免直接调用这个函数,因为C/C++运行期库得不到通知,∴没有机会去调用全局或静态C/C++对象的析构函数。- BOOL WINAPI TerminateProcess(
- HANDLE hProcess, // 要结束的进程句柄
- UINT uExitCode // 指定目标进程的退出码
- );
BOOL WINAPI TerminateProcess(
HANDLE hProcess, // 要结束的进程句柄
UINT uExitCode // 指定目标进程的退出码
);
- HANDLE WINAPI OpenProcess(
- DWORD dwDesiredAccess, // 指定得到的句柄具有的访问权限
- BOOL bInheritHandle, // 指定返回的句柄是否可被继承
- DWORD dwProcessId // 指定要打开的进程ID
- );
HANDLE WINAPI OpenProcess(
DWORD dwDesiredAccess, // 指定得到的句柄具有的访问权限
BOOL bInheritHandle, // 指定返回的句柄是否可被继承
DWORD dwProcessId // 指定要打开的进程ID
);
这个函数打开一个存在的进程,返回具有指定权限的句柄。获取进程的其他信息
- 获取进程对应的可执行程序路径
- DWORD WINAPI GetProcessImageFileNameA (
- _In_ HANDLE hProcess, // 要获得文件路径的进程句柄
- _Out_writes_(nSize) LPSTR lpImageFileName, // 保存文件路径的内存首地址
- _In_ DWORD nSize<span style="white-space:pre"> </span>// 保存文件路径的内存大小
- );
DWORD WINAPI GetProcessImageFileNameA (
_In_ HANDLE hProcess, // 要获得文件路径的进程句柄
_Out_writes_(nSize) LPSTR lpImageFileName, // 保存文件路径的内存首地址
_In_ DWORD nSize// 保存文件路径的内存大小
);
注意使用上述函数获取的路径并不是磁盘驱动器的路径,而是内核路径,形如: " \Device\HarddiskVolume1\WINDOWS\System32\notepad.exe ",通常此路径中的Device表示硬盘驱动器,HarddiskVolume表示分区数字表示具体分区号(1表C 2表D。。。),所以对路径需要进行转换。- #include <stdio.h>
- #include <windows.h>
- #include <tchar.h>
- #include <TlHelp32.h>
- #include <Psapi.h>
- #pragma comment(lib, "psapi.lib")
- void TransPath(PTCHAR pPath)
- {
- // 获取驱动器字符串位置
- PTCHAR pFind = _tcsstr(pPath, _T("HarddiskVolume"));
- if( pFind==NULL )
- return;
- TCHAR tcDriver[5] = {'C', 'D', 'E', 'F', 'G'}; // 可写满24个字符,这里只是一个demo
- int nNum = pFind[_tcslen(_T("HarddiskVolume"))] - 0x30;
- PTCHAR pTemp = _tcsstr(pFind, _T("\\"));
- // 拼接字符串的缓冲区
- TCHAR tcBuffer[MAX_PATH];
- memset(tcBuffer, 0, MAX_PATH*sizeof(TCHAR));
- _stprintf_s(tcBuffer, _T("%c:%s"), tcDriver[nNum-1], pTemp);
- memset(pPath, 0, _tcslen(pPath));
- _tcscpy_s(pPath, 256, tcBuffer);
- }
- int main()
- {
- // 传入\Device\HarddiskVolume1\WINDOWS\System32\notepad.exe
- DWORD dwNeed;
- PDWORD pdwMem = new DWORD[4000];
- BOOL bRet = EnumProcesses(pdwMem, 4000, &dwNeed);
- if( !bRet )
- {
- if(pdwMem!=NULL)
- {
- delete[] pdwMem;
- pdwMem = nullptr;
- return -1;
- }
- }
- int nNumProc = dwNeed/4;
- HANDLE hProcess = NULL;
- DWORD dwPathLength = 0;
- TCHAR tcBuffer[256] = {0};
- for( int i=0; i<nNumProc; i++ )
- {
- hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pdwMem[i]);
- if( hProcess==NULL )
- continue;
- memset(tcBuffer, 0, 256*sizeof(TCHAR));
- dwPathLength = GetProcessImageFileName(hProcess, tcBuffer, 256);
- if( dwPathLength!=0 )
- {
- TransPath(tcBuffer);
- printf("%08d %ls\n",pdwMem[i], tcBuffer);
- }
- CloseHandle(hProcess);
- }
- if( pdwMem!=NULL )
- {
- delete[] pdwMem;
- pdwMem = NULL;
- }
- system("pause");
- return 0;
- }
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <TlHelp32.h>
#include <Psapi.h>
#pragma comment(lib, "psapi.lib")
void TransPath(PTCHAR pPath)
{
// 获取驱动器字符串位置
PTCHAR pFind = _tcsstr(pPath, _T("HarddiskVolume"));
if( pFind==NULL )
return;
TCHAR tcDriver[5] = {'C', 'D', 'E', 'F', 'G'}; // 可写满24个字符,这里只是一个demo
int nNum = pFind[_tcslen(_T("HarddiskVolume"))] - 0x30;
PTCHAR pTemp = _tcsstr(pFind, _T("\\"));
// 拼接字符串的缓冲区
TCHAR tcBuffer[MAX_PATH];
memset(tcBuffer, 0, MAX_PATH*sizeof(TCHAR));
_stprintf_s(tcBuffer, _T("%c:%s"), tcDriver[nNum-1], pTemp);
memset(pPath, 0, _tcslen(pPath));
_tcscpy_s(pPath, 256, tcBuffer);
}
int main()
{
// 传入\Device\HarddiskVolume1\WINDOWS\System32\notepad.exe
DWORD dwNeed;
PDWORD pdwMem = new DWORD[4000];
BOOL bRet = EnumProcesses(pdwMem, 4000, &dwNeed);
if( !bRet )
{
if(pdwMem!=NULL)
{
delete[] pdwMem;
pdwMem = nullptr;
return -1;
}
}
int nNumProc = dwNeed/4;
HANDLE hProcess = NULL;
DWORD dwPathLength = 0;
TCHAR tcBuffer[256] = {0};
for( int i=0; i<nNumProc; i++ )
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pdwMem[i]);
if( hProcess==NULL )
continue;
memset(tcBuffer, 0, 256*sizeof(TCHAR));
dwPathLength = GetProcessImageFileName(hProcess, tcBuffer, 256);
if( dwPathLength!=0 )
{
TransPath(tcBuffer);
printf("%08d %ls\n",pdwMem[i], tcBuffer);
}
CloseHandle(hProcess);
}
if( pdwMem!=NULL )
{
delete[] pdwMem;
pdwMem = NULL;
}
system("pause");
return 0;
}
- 获取进程内模块信息
- 获取进程命令行参数
// 返回命令行参数字符串的首地址
LPTSTR GetCommandLine(VOID);
mov ax, dword ptr [7C8835F4]
// 个人做测试的时候地址好像是7C8855F4
也就是说进程的命令行参数被保存在内存中的固定位置,只要定位到GetCommandLineA的入口地址,跳过一个字节后取出一个dword值,这个值是一个指针,指向的内容即命令行参数的地址。现在的问题即得到远程进程中GetCommandLineA函数的入口地址即可得到该进程的命令行参数。获取指定模块中某个Win32API函数地址的函数是GetProcAddress。but这个函数仅可获取当前进程中API函数的地址,要获得远程进程中某个函数的地址就必须在远程进程中执行这个函数,需要使用远线程方法,本文暂时不涉及这个内容。换个思路:GetCommandLineA使用Kernel32.dll提供,这个库是系统基本库,几乎所有程序都会加载这个模块。而且对于NT架构所有进程加载Kernel32.dll的模块基址基本都是一致的,而GetCommandLineA在其库中的地址也是固定的。所以我们在本进程中得到的此函数地址和远程进程中该函数的地址是一致的(这个方法在我的电脑Win7x64中实验失败,应该因为程序的加载基址不固定了)。获取远程进程中数据使用ReadProcessMemory函数。- BOOL WINAPI ReadProcessMemory(
- _In_ HANDLE hProcess, <span style="white-space:pre"> </span> // 远程进程句柄
- _In_ LPCVOID lpBaseAddress,<span style="white-space:pre"> </span> // 目标进程内存空间首地址
- _Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer, // 本进程内存空间首地址,存储读出的数据
- _In_ SIZE_T nSize,<span style="white-space:pre"> </span> // 要读取的字节数
- _Out_opt_ SIZE_T * lpNumberOfBytesRead<span style="white-space:pre"> </span> // 实际读取的字节数
- );
BOOL WINAPI ReadProcessMemory(
_In_ HANDLE hProcess, // 远程进程句柄
_In_ LPCVOID lpBaseAddress, // 目标进程内存空间首地址
_Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer, // 本进程内存空间首地址,存储读出的数据
_In_ SIZE_T nSize, // 要读取的字节数
_Out_opt_ SIZE_T * lpNumberOfBytesRead // 实际读取的字节数
);
为保证此方法成功,应判断当前系统是否是NT系统,使用GetVersion函数获取当前系统的版本。返回值表示系统的版本信息,若是NT/2k/xp则返回值小于0x80000000,否则大于该值。虚拟机中XP实验成功。