用ZwQueryVirtualMemory枚举进程模块

转自:http://www.cppblog.com/aurain/archive/2010/07/05/119361.html

[cpp]  view plain  copy
  1. 用ZwQueryVirtualMemory枚举进程模块  
  2. 枚举进程模块通常可以使用诸如:CreateToolhelp32Snapshot,Module32First,Module32Next 等"Tool Help Functions"接口来实现, 并且这也是最通用的方法(从Win95就开始支持了), 但是今天我们要介绍的是ntdll.dll导出的未文档化接口ZwQueryVirtualMemory,。相比前面所介绍的方法,该方法可以检测出隐藏的模块(类似IceSword)。  
  3. 我们先来看下这个接口的原型:  
  4.   
  5. //-------------------------------------------------------------------------------------------------  
  6.   
  7. NTSTATUS  
  8. NTAPI  
  9. ZwQueryVirtualMemory(  
  10.             IN HANDLE ProcessHandle,  
  11.             IN PVOID BaseAddress,  
  12.             IN MEMORY_INFORMATION_CLASS MemoryInformationClass,  
  13.             OUT PVOID MemoryInformation,  
  14.             IN ULONG MemoryInformationLength,  
  15.             OUT PULONG ReturnLength OPTIONAL );  
  16.   
  17. typedef enum _MEMORY_INFORMATION_CLASS {  
  18.             MemoryBasicInformation,  
  19.             MemoryWorkingSetList,  
  20.             MemorySectionName,  
  21.             MemoryBasicVlmInformation  
  22. } MEMORY_INFORMATION_CLASS;  
  23.   
  24. 参数说明:  
  25.            ProcessHandle - 目标进程句柄  
  26.            BaseAddress    - 要查询的虚拟内存基址  
  27.            MemoryInformationClass - 要查询的内存信息类  
  28.            MemoryInformation - 用户提供的缓冲区,返回内存相关信息  
  29.            MemoryInformationLength - 缓冲区长度(字节为单位)  
  30.            ReturnLength - 实际返回的内存信息长度(字节为单位)  
  31. 返回值:  
  32.            NTSTATUS - 返回STATUS_SUCCESS或一个错误状态码  
  33.   
  34. //-------------------------------------------------------------------------------------------------  
  35.   
  36. 我们要枚举进程模块信息, 需要用到两类内存信息MemoryBasicInformation和MemorySectionName,  
  37. 前者返回内存的基本信息, 比如: 内存区的基址,大小以及页面的各种属性等等, 而后者则返回内存段的名字,  
  38. 也就是我们所要找的模块名. 利用前者我们可以过滤出类型为MEM_IMAGE的内存段并得到内存段的基址和属性, 利用后者我们可以得到模块名.  
  39. 另外,需要注意的是该方法找出来的设备名是诸如\Device\HarddiskVolume1之类的名称,所以我们需要把它转换为我们习惯的DOS设备名,如C:\,D:\等。不能自以为是的认为\Device\HarddiskVolume1对应C盘\Device\HarddiskVolume2对应D盘。该转换是通过调用QueryDosDevice来实现的。  
  40. 具体看代码:  
  41. // CheckDll.cpp : 定义控制台应用程序的入口点。  
  42. //  
  43.    
  44. #include "stdafx.h"  
  45.    
  46. #include <windows.h>  
  47. #include <winternl.h>  
  48.    
  49. #include <string>  
  50. #include <map>  
  51. using namespace std;  
  52.    
  53. #pragma warning(disable:4312)  
  54.    
  55. typedef enum _MEMORY_INFORMATION_CLASS  
  56. {  
  57.    MemoryBasicInformation,  
  58.    MemoryWorkingSetList,  
  59.    MemorySectionName  
  60. }MEMORY_INFORMATION_CLASS;  
  61.    
  62. typedef  
  63. NTSTATUS  
  64. (WINAPI *ZWQUERYVIRTUALMEMORY) (  
  65.                          IN HANDLE ProcessHandle,  
  66.                          IN PVOID BaseAddress,  
  67.                          IN MEMORY_INFORMATION_CLASS MemoryInformationClass,  
  68.                          OUT PVOID MemoryInformation,  
  69.                          IN ULONG MemoryInformationLength,  
  70.                          OUT PULONG ReturnLength OPTIONAL  
  71.                          );  
  72.    
  73. map<wstring, wstring> g_mapDevice2Path;  
  74.    
  75. void ConvertVolumePaths(  
  76.                    IN PWCHAR DeviceName,  
  77.                    IN PWCHAR VolumeName  
  78.                    )  
  79. {  
  80.    DWORD  CharCount = MAX_PATH + 1;  
  81.    PWCHAR Names     = NULL;  
  82.    PWCHAR NameIdx      = NULL;  
  83.    BOOL   Success      = FALSE;  
  84.    
  85.    for (;;)  
  86.    {  
  87.       //  
  88.       //  Allocate a buffer to hold the paths.  
  89.       Names = (PWCHARnew BYTE [CharCount * sizeof(WCHAR)];  
  90.    
  91.       if ( !Names )  
  92.       {  
  93.          //  
  94.          //  If memory can't be allocated, return.  
  95.          return;  
  96.       }  
  97.    
  98.       //  
  99.       //  Obtain all of the paths  
  100.       //  for this volume.  
  101.       Success = GetVolumePathNamesForVolumeNameW(  
  102.          VolumeName, Names, CharCount, &CharCount  
  103.          );  
  104.    
  105.       if ( Success )  
  106.       {  
  107.          break;  
  108.       }  
  109.    
  110.       if ( GetLastError() != ERROR_MORE_DATA )  
  111.       {  
  112.          break;  
  113.       }  
  114.    
  115.       //  
  116.       //  Try again with the  
  117.       //  new suggested size.  
  118.       delete [] Names;  
  119.       Names = NULL;  
  120.    }  
  121.    
  122.    if ( Success )  
  123.    {  
  124.       //  
  125.       //  Display the various paths.  
  126.       for ( NameIdx = Names;  
  127.          NameIdx[0] != L'\0';  
  128.          NameIdx += wcslen(NameIdx) + 1 )  
  129.       {  
  130.          g_mapDevice2Path[DeviceName] = NameIdx;  
  131.       }  
  132.    }  
  133.    
  134.    if ( Names != NULL )  
  135.    {  
  136.       delete [] Names;  
  137.       Names = NULL;  
  138.    }  
  139.    
  140.    return;  
  141. }  
  142.    
  143. BOOL InitDevice2Path()  
  144. {  
  145.    BOOL   bRet               = FALSE;   
  146.    DWORD  CharCount           = 0;  
  147.    WCHAR  DeviceName[MAX_PATH] = L"";  
  148.    DWORD  Error              = ERROR_SUCCESS;  
  149.    HANDLE FindHandle          = INVALID_HANDLE_VALUE;  
  150.    BOOL   Found              = FALSE;  
  151.    size_t Index              = 0;  
  152.    BOOL   Success                = FALSE;  
  153.    WCHAR  VolumeName[MAX_PATH] = L"";  
  154.    
  155.    //  
  156.    //  Enumerate all volumes in the system.  
  157.    FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));  
  158.    
  159.    if (FindHandle == INVALID_HANDLE_VALUE)  
  160.    {  
  161.       Error = GetLastError();  
  162.       wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);  
  163.       return bRet;  
  164.    }  
  165.    
  166.    for (;;)  
  167.    {  
  168.       //  
  169.       //  Skip the \\?\ prefix and remove the trailing backslash.  
  170.       Index = wcslen(VolumeName) - 1;  
  171.    
  172.       if (VolumeName[0]     != L'\\' ||  
  173.          VolumeName[1]     != L'\\' ||  
  174.          VolumeName[2]     != L'?'  ||  
  175.          VolumeName[3]     != L'\\' ||  
  176.          VolumeName[Index] != L'\\')  
  177.       {  
  178.          Error = ERROR_BAD_PATHNAME;  
  179.          wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);  
  180.          break;  
  181.       }  
  182.    
  183.       //  
  184.       //  QueryDosDeviceW doesn't allow a trailing backslash,  
  185.       //  so temporarily remove it.  
  186.       VolumeName[Index] = L'\0';  
  187.    
  188.       CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));  
  189.    
  190.       VolumeName[Index] = L'\\';  
  191.    
  192.       if ( CharCount == 0 )  
  193.       {  
  194.          Error = GetLastError();  
  195.          wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);  
  196.          break;  
  197.       }  
  198.    
  199.       ConvertVolumePaths(DeviceName, VolumeName);  
  200.    
  201.       //  
  202.       //  Move on to the next volume.  
  203.       Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));  
  204.    
  205.       if ( !Success )  
  206.       {  
  207.          Error = GetLastError();  
  208.    
  209.          if (Error != ERROR_NO_MORE_FILES)  
  210.          {  
  211.             wprintf(L"FindNextVolumeW failed with error code %d\n", Error);  
  212.             break;  
  213.          }  
  214.    
  215.          //  
  216.          //  Finished iterating  
  217.          //  through all the volumes.  
  218.          Error = ERROR_SUCCESS;  
  219.          break;  
  220.       }  
  221.    }  
  222.    
  223.    FindVolumeClose(FindHandle);  
  224.    FindHandle = INVALID_HANDLE_VALUE;  
  225.    
  226.    return bRet;  
  227. }  
  228.    
  229. void DeviceName2PathName(OUT WCHAR* szPathName, IN const WCHAR* szDeviceName)  
  230. {  
  231.    memset(szPathName, 0, MAX_PATH * 2);  
  232.    wstring strDeviceName = szDeviceName;  
  233.    size_t pos = strDeviceName.find(L'\\', 9);  
  234.    wstring strTemp1 = strDeviceName.substr(0, pos);  
  235.    wstring strTemp2 = strDeviceName.substr(pos + 1);  
  236.    wstring strDriverLetter  = g_mapDevice2Path[strTemp1];  
  237.    wstring strPathName = strDriverLetter + strTemp2;  
  238.    
  239.    wcscpy_s(szPathName, MAX_PATH, strPathName.c_str());  
  240. }  
  241.    
  242. /** 
  243. * 枚举指定进程加载的模块 
  244. * @param dwProcessId 进程Id 
  245. * @return void 
  246. */  
  247. void EnumProcessModules(IN DWORD dwProcessId)  
  248. {  
  249.    DWORD dwStartAddr = 0x00000000;  
  250.    BYTE szBuffer[MAX_PATH * 2 + 4] = {0};  
  251.    WCHAR szModuleName[MAX_PATH] = {0};  
  252.    WCHAR szPathName[MAX_PATH] = {0};  
  253.    MEMORY_BASIC_INFORMATION mbi;  
  254.    PUNICODE_STRING usSectionName;     
  255.    ZWQUERYVIRTUALMEMORY fnZwQueryVirtualMemory;  
  256.    HANDLE hProcess =NULL;  
  257.    
  258.    hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwProcessId);  
  259.    
  260.    if (hProcess == NULL)  
  261.    {  
  262.       wprintf(L"Open Process %d Error\n", dwProcessId);  
  263.       return;  
  264.    }  
  265.    
  266.    dwStartAddr = 0x00000000;  
  267.    
  268.    fnZwQueryVirtualMemory = (ZWQUERYVIRTUALMEMORY)  
  269.       ::GetProcAddress(GetModuleHandleA("ntdll.dll"),  
  270.       "ZwQueryVirtualMemory" );  
  271.    
  272.    if(fnZwQueryVirtualMemory)  
  273.    {  
  274.       do  
  275.       {  
  276.          if (fnZwQueryVirtualMemory(  
  277.             hProcess,  
  278.             (PVOID)dwStartAddr,  
  279.             MemoryBasicInformation,  
  280.             &mbi,  
  281.             sizeof(mbi),  
  282.             0) >= 0 )  
  283.          {  
  284.             if(mbi.Type == MEM_IMAGE)  
  285.             {  
  286.                 if (fnZwQueryVirtualMemory(  
  287.                    hProcess,  
  288.                    (PVOID)dwStartAddr,  
  289.                    MemorySectionName,  
  290.                    szBuffer,  
  291.                    sizeof(szBuffer),  
  292.                    0) >= 0 )  
  293.                 {  
  294.                    usSectionName = (PUNICODE_STRING)szBuffer;  
  295.                    if( _wcsnicmp(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR)) )  
  296.                    {  
  297.                       wcsncpy_s(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR) );  
  298.                       szModuleName[usSectionName->Length / sizeof(WCHAR)] = UNICODE_NULL;  
  299.                       DeviceName2PathName(szPathName, szModuleName);  
  300.                       wprintf(L"[0x%.8x]\t%s\n", dwStartAddr, szPathName);  
  301.                   }  
  302.                 }  
  303.             }  
  304.    
  305.          }  
  306.          // 递增基址,开始下一轮查询!  
  307.          dwStartAddr += 0x1000;  
  308.       }while( dwStartAddr < 0x80000000 );  
  309.    }  
  310.    
  311.    CloseHandle(hProcess);  
  312. }  
  313.    
  314. /** 
  315. * 提升当前进程权限函数("SeDebugPrivilege"读、写控制权限) 
  316. * @param void 
  317. * @return TRUE-成功;FALSE-失败 
  318. */  
  319. BOOL EnableDebugPriv()  
  320. {  
  321.    HANDLE hToken;  
  322.    TOKEN_PRIVILEGES tkp;  
  323.    LUID Luid;  
  324.    
  325.    if (!OpenProcessToken(GetCurrentProcess(),  
  326.       TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))  
  327.    {  
  328.       return FALSE;  
  329.    }  
  330.    
  331.    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid ))  
  332.    {  
  333.       CloseHandle(hToken);  
  334.       return FALSE;  
  335.    }  
  336.    
  337.    tkp.PrivilegeCount = 1;  
  338.    tkp.Privileges[0].Luid = Luid;  
  339.    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
  340.    
  341.    if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))  
  342.    {  
  343.       CloseHandle(hToken);  
  344.       return FALSE;  
  345.    }  
  346.    
  347.    return TRUE;  
  348. }  
  349.    
  350. int _tmain(int argc, _TCHAR* argv[])  
  351. {  
  352.    DWORD dwProcessId = 4;  
  353.    
  354.    if (argc != 2)  
  355.    {  
  356.       wprintf(L"usage:CheckDll ProcessId");  
  357.       return 1;  
  358.    }  
  359.    
  360.    dwProcessId = _ttoi(argv[1]);  
  361.    
  362.    InitDevice2Path();  
  363.    
  364.    // 首先提示权限  
  365.    if (EnableDebugPriv())  
  366.    {  
  367.       EnumProcessModules(dwProcessId);  
  368.    }  
  369.    
  370.    g_mapDevice2Path.clear();  
  371.    
  372.    return 0;  
  373. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值