天天看点

关于文件读写的监控, 通过APIHOOK来实现

   有的时候,我们需要对程序读写文件的时候进行监控,尤其是文件的数据是保密的,而且不能直接存储在磁盘上。举个最简单的例子来说,当我们有个文件在磁盘上,而这个文件是加密的,这时候在程序打开文件的时候,通过输入密钥进行解密了,但是这些解密了的数据,是不能写回磁盘的,只能放在RAM中,这时候如果程序想通过正常的文件操作来访问数据的话,那么这就需要我们来对这个文件操作的API下钩子函数了。

   对于钩子函数的介绍,可以通过下面的几篇文章来了解,前提是必须了解PE格式中的输入表区块内容。

  1.  http://zhidao.baidu.com/question/56417343.html

  2. http://baike.baidu.com/view/1037259?wtp=tt-

  3. http://zhidao.baidu.com/question/5555707.html

等等,百度上一大相关文章。

在网上看了不少这方面的资料,发现很多东西写的都好散,没有封装起来,因此我就花了点功夫,把它封装了起来,还没完全测试,先把代码共享一下,如果各位有什么bug的话,请留言,我会尽快改进的。

--------------------------------------------

-------------   HooAPI.h -----------------

--------------------------------------------

#pragma once
#include<vector>
#include "Toolhelp.h"
 
struct HookAPIEntry{
 // 这个是模块的名字,比如usr32.dll
 CString  strMoudleName ;
 // 这个是需要下钩子的API函数
 CString  strFunctionName ;
 // API函数的原始地址
 PROC  pOriginalProc ;
 // API函数的新地址
 PROC  pNewProc ;
 HookAPIEntry(){
  pOriginalProc = pNewProc = NULL ;
 };
};
typedef HookAPIEntry * PHookAPIEntry ;
typedef HookAPIEntry * LPHookAPIEntry ;
typedef std::vector<PHookAPIEntry> HookAPIEntryList ;
typedef HookAPIEntryList * PHookAPIEntryList ;
typedef HookAPIEntryList * LPHookAPIEntryList ;
typedef std::vector<PHookAPIEntry>::iterator HookAPIEntryPos ;
class CHookAPI
{
public:
 CHookAPI(void);
 ~CHookAPI(void);
public:
 // 对钩子函数列表的操作
 HRESULT  AddHookAPIEntry(const HookAPIEntry & entry ) ;
 HRESULT  AddHookAPIEntry(CString strMoudleName,CString strFunctionName,PROC pNewProc) ;
 HRESULT  FindHookAPIEntry(CString strMoudleName,CString strFunctionName,LPHookAPIEntry * pResult);
 HRESULT  DeleteHookAPIEntry(CString strMoudleName,CString strFunctionName) ;
 HRESULT  ClearAllAPIEntry( void ) ;
 HRESULT  ReHookAllMoudleAPI( void ) ;
 HRESULT  UnHookAllMoudleAPI( void ) ;
 
 HRESULT  GetHookAPIEntryByNewAddress(PROC pNewAddress,LPHookAPIEntry * pResult) ;
protected:
 HRESULT  HookMoudleAPI(const HookAPIEntry & hookentry);
 HRESULT  UnHookMoudleAPI(const HookAPIEntry & hookentry);
 HRESULT  HookMoudleAPI(HMODULE hmoudle,const HookAPIEntry & hookentry);
 HRESULT  UnHookMoudleAPI(HMODULE hmoudle,const HookAPIEntry & hookentry);
 HRESULT  ReplaceMoudleAPI(HMODULE hmoudleCaller, const CString &strMoudleName,
  PROC pCurAddress,PROC pNewAddress);
 HRESULT  GetProcessAddress(const CString &strMoudleName,const CString &strFunctionName,
  PROC * pAddress ) ;
 HRESULT  UpdateOriginalProc( HookAPIEntry & hookentry);
protected:
 // 需要下钩子的函数集合
 HookAPIEntryList   m_HookAPIEntryList ;
 // 系统的信息
 SYSTEM_INFO     m_si;
};
           

--------------------------------------------

-------------   HooAPI.cpp -----------------

--------------------------------------------

#include "StdAfx.h"
#include "HookAPI.h"
#include "Dbghelp.h"
#pragma comment(lib , "imagehlp.lib")
#pragma comment(lib , "Dbghelp.lib")
CHookAPI::CHookAPI(void)
{
 GetSystemInfo(&m_si) ;
}

CHookAPI::~CHookAPI(void)
{
}

HRESULT CHookAPI::AddHookAPIEntry(const HookAPIEntry & entry ) {
 // 已经存在了,就不用添加了
 if( S_OK == FindHookAPIEntry(entry.strMoudleName,entry.strFunctionName,NULL ) )
  return S_FALSE ;
 LPHookAPIEntry pentry = new HookAPIEntry() ;
 * pentry = entry ;
 m_HookAPIEntryList.push_back(pentry) ;
 UpdateOriginalProc(*pentry) ;
 HookMoudleAPI(entry);
 return S_OK ;
}
HRESULT CHookAPI::AddHookAPIEntry(CString strMoudleName,CString strFunctionName,PROC pNewProc) {
 if( S_OK == FindHookAPIEntry(strMoudleName,strFunctionName,NULL ) )
  return S_FALSE ;
 LPHookAPIEntry pentry = new HookAPIEntry() ;
 pentry->strMoudleName = strMoudleName ;
 pentry->strFunctionName = strFunctionName ;
 pentry->pNewProc = pNewProc ;
 m_HookAPIEntryList.push_back(pentry) ;
 UpdateOriginalProc(*pentry) ;
 HookMoudleAPI(*pentry);
 return S_OK ;
}
HRESULT CHookAPI::FindHookAPIEntry(CString strMoudleName,CString strFunctionName,LPHookAPIEntry * pResult){
 HookAPIEntryPos pos ;
 for( pos = m_HookAPIEntryList.begin(); pos != m_HookAPIEntryList.end() ; pos ++ ){
  PHookAPIEntry pHookAPIEntry = * pos ;
  if( NULL != pHookAPIEntry &&
   pHookAPIEntry->strMoudleName.CompareNoCase(strMoudleName) == 0 &&
   pHookAPIEntry->strFunctionName.Compare(strFunctionName) == 0 ){
    // 已经找到了
    if( pResult == NULL )
     * pResult = pHookAPIEntry ;
    return S_OK ;
  }
 };
 return S_FALSE ;
}
HRESULT CHookAPI::DeleteHookAPIEntry(CString strMoudleName,CString strFunctionName) {
 HookAPIEntryPos pos ;
 for( pos = m_HookAPIEntryList.begin(); pos != m_HookAPIEntryList.end() ; pos ++ ){
  PHookAPIEntry pHookAPIEntry = * pos ;
  if( NULL != pHookAPIEntry &&
   pHookAPIEntry->strMoudleName.CompareNoCase(strMoudleName) == 0 &&
   pHookAPIEntry->strFunctionName.Compare(strFunctionName) == 0 ){
    // 已经找到了
    UnHookMoudleAPI(*pHookAPIEntry) ;
    m_HookAPIEntryList.erase(pos) ;
    delete pHookAPIEntry ;
    pHookAPIEntry = NULL ;
    return S_OK ;
  }
 };
 return S_OK ;
}
HRESULT CHookAPI::ClearAllAPIEntry( void ) {
 UnHookAllMoudleAPI() ;
 HookAPIEntryPos pos ;
 PHookAPIEntry pHookAPIEntry = NULL ;
 while( m_HookAPIEntryList.size() > 0 ) {
  pos = m_HookAPIEntryList.begin() ;
  pHookAPIEntry = * pos ;
  
  m_HookAPIEntryList.erase(pos) ;
  if( NULL == pHookAPIEntry ){
   delete pHookAPIEntry ;
   pHookAPIEntry = NULL ;
  }
 }
 return S_OK ;
}

HRESULT CHookAPI::GetHookAPIEntryByNewAddress(PROC pNewAddress,LPHookAPIEntry * pResult ) {
 HookAPIEntryPos pos ;
 for( pos = m_HookAPIEntryList.begin(); pos != m_HookAPIEntryList.end() ; pos ++ ){
  PHookAPIEntry pHookAPIEntry = * pos ;
  if( NULL != pHookAPIEntry &&
   pHookAPIEntry->pNewProc ==  pNewAddress){
    // 已经找到了
    if( NULL != pResult ){
     * pResult = * pos ;
     return S_OK ;
    }
  }
 };
 return S_FALSE ;
}
HRESULT CHookAPI::UpdateOriginalProc( HookAPIEntry & hookentry){
 if(S_OK != GetProcessAddress(hookentry.strMoudleName,
  hookentry.strFunctionName,&hookentry.pOriginalProc)){
   ASSERT(FALSE) ;
   return S_FALSE ;
 }
 if (hookentry.pOriginalProc > m_si.lpMaximumApplicationAddress)  {
  PBYTE pb = (PBYTE) hookentry.pOriginalProc;
  if (pb[0] == 0x86){
   PVOID pv = * (PVOID*) &pb[1];
   hookentry.pOriginalProc = (PROC) pv;
  }
 }
 return S_OK ;
}
HRESULT CHookAPI::GetProcessAddress(const CString &strMoudleName,const CString&strFunctionName,FARPROC * pAddress ){
 * pAddress = ::GetProcAddress(GetModuleHandle(strMoudleName), strFunctionName) ;
 return S_OK ;
}
HRESULT CHookAPI::ReHookAllMoudleAPI( void ) {
 CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());
 MODULEENTRY32 me = { sizeof(me) };
 for (BOOL fOk = th.ModuleFirst(&me); fOk; fOk = th.ModuleNext(&me))
 {
  //TRACE(me.szModule);
  // 监控所有的有关文件读写情况
  HookAPIEntryPos pos ;
  for( pos = m_HookAPIEntryList.begin(); pos != m_HookAPIEntryList.end() ; pos ++ ){
   PHookAPIEntry pHookAPIEntry = * pos ;
   if(pHookAPIEntry == NULL ){
    ASSERT(FALSE) ;
    continue ;
   }
   // 重新获取一下原来的地址
   if( pHookAPIEntry->pOriginalProc == NULL ){
    if(S_OK != UpdateOriginalProc(*pHookAPIEntry)){
     ASSERT(FALSE) ;
     continue ;
    }
   }
   // 对所有模块的输入表进行替换
   HookMoudleAPI(me.hModule,*pHookAPIEntry) ;
  }
 };
 return S_OK ;
}
HRESULT CHookAPI::UnHookAllMoudleAPI( void ) {
 CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());
 MODULEENTRY32 me = { sizeof(me) };
 for (BOOL fOk = th.ModuleFirst(&me); fOk; fOk = th.ModuleNext(&me))
 {
  //TRACE(me.szModule);
  // 监控所有的有关文件读写情况
  HookAPIEntryPos pos ;
  for( pos = m_HookAPIEntryList.begin(); pos != m_HookAPIEntryList.end() ; pos ++ ){
   PHookAPIEntry pHookAPIEntry = * pos ;
   if(pHookAPIEntry == NULL ){
    ASSERT(FALSE) ;
    continue ;
   }
   // 重新获取一下原来的地址
   if( pHookAPIEntry->pOriginalProc == NULL ){
    if(S_OK != UpdateOriginalProc(*pHookAPIEntry)){
     ASSERT(FALSE) ;
     continue ;
    }
   }
   // 对所有模块的输入表进行替换
   UnHookMoudleAPI(me.hModule,*pHookAPIEntry) ;
  }
 };
 return S_OK ;
}
HRESULT CHookAPI::HookMoudleAPI(const HookAPIEntry & hookentry){
 CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());
 MODULEENTRY32 me = { sizeof(me) };
 for (BOOL fOk = th.ModuleFirst(&me); fOk; fOk = th.ModuleNext(&me))
 {
  //TRACE(me.szModule);
  // 监控所有的有关文件读写情况
  HookMoudleAPI(me.hModule,hookentry) ;
 };
 return S_OK ;
}
HRESULT CHookAPI::HookMoudleAPI(HMODULE hmoudle,const HookAPIEntry & hookentry){
 return ReplaceMoudleAPI(hmoudle,hookentry.strMoudleName,hookentry.pOriginalProc,hookentry.pNewProc);
}
HRESULT CHookAPI::UnHookMoudleAPI(const HookAPIEntry & hookentry){
 CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());
 MODULEENTRY32 me = { sizeof(me) };
 for (BOOL fOk = th.ModuleFirst(&me); fOk; fOk = th.ModuleNext(&me))
 {
  //TRACE(me.szModule);
  // 监控所有的有关文件读写情况
  UnHookMoudleAPI(me.hModule,hookentry) ;
 };
 return S_OK ;
}
HRESULT CHookAPI::UnHookMoudleAPI(HMODULE hmoudle,const HookAPIEntry & hookentry){
 return ReplaceMoudleAPI(hmoudle,hookentry.strMoudleName,hookentry.pNewProc,hookentry.pOriginalProc);
}
HRESULT CHookAPI::ReplaceMoudleAPI(HMODULE hmoduleCaller, const CString &strMoudleName,
  PROC pCurAddress,PROC pNewAddress){
 // 找到的IAT表
 ULONG lsize = 0 ;
 PIMAGE_SECTION_HEADER psecHeader ;
 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
  ImageDirectoryEntryToDataEx(hmoduleCaller,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&lsize,&psecHeader);
 if( pImportDesc == NULL ){
  // ASSERT(FALSE) ;
  return S_FALSE ;
 }
 // 找输入表的相应的模块
 while( pImportDesc->Name != 0){
  // RVA
  CString strdllname = (PSTR)((PBYTE)hmoduleCaller + pImportDesc->Name);
  if( strdllname.CompareNoCase(strMoudleName) == 0 )
   break ;
  pImportDesc ++ ;
 }
 if( pImportDesc->Name == 0){
  //ASSERT(FALSE) ;
  return S_FALSE ;
 }
 // 找输入表的函数地址,FirstThunk为RVA
 PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)((PBYTE)hmoduleCaller + pImportDesc->FirstThunk);
 PROC* ppfn;
 while( pThunk->u1.Function != 0 ){
  ppfn = (PROC*)&pThunk->u1.Function;
  if( *ppfn == pCurAddress ){
   // 找到了这个输入表中的函数地址,将其修改一下
   MEMORY_BASIC_INFORMATION mbi;
   VirtualQuery(ppfn,&mbi,sizeof(MEMORY_BASIC_INFORMATION));
   VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_READWRITE,&mbi.Protect);
   *ppfn=*pNewAddress;
   DWORD dwOldProtect;
   VirtualProtect(mbi.BaseAddress,mbi.RegionSize,mbi.Protect,&dwOldProtect);
   return S_OK ;
  }
  pThunk ++ ;
 }
 return S_OK ;
   
}
 
           

--------------------------------------------

-------------   ToolHelp.h -----------------

--------------------------------------------

#pragma once
#include "stdafx.h"
///
#include <tlhelp32.h>
#include <tchar.h>
#define chINRANGE(low, Num, High) (((low) <= (Num)) && ((Num) <= (High)))
///

class CToolhelp {
private:
   HANDLE m_hSnapshot;
public:
   CToolhelp(DWORD dwFlags = 0, DWORD dwProcessID = 0);
   ~CToolhelp();
   BOOL CreateSnapshot(DWORD dwFlags, DWORD dwProcessID = 0);
  
   BOOL ProcessFirst(PPROCESSENTRY32 ppe) const;
   BOOL ProcessNext(PPROCESSENTRY32 ppe) const;
   BOOL ProcessFind(DWORD dwProcessId, PPROCESSENTRY32 ppe) const;
   BOOL ModuleFirst(PMODULEENTRY32 pme) const;
   BOOL ModuleNext(PMODULEENTRY32 pme) const;
   BOOL ModuleFind(PVOID pvBaseAddr, PMODULEENTRY32 pme) const;
   BOOL ModuleFind(PTSTR pszModName, PMODULEENTRY32 pme) const;
  
   BOOL ThreadFirst(PTHREADENTRY32 pte) const;
   BOOL ThreadNext(PTHREADENTRY32 pte) const;
  
   BOOL HeapListFirst(PHEAPLIST32 phl) const;
   BOOL HeapListNext(PHEAPLIST32 phl) const;
   int  HowManyHeaps() const;
   // Note: The heap block functions do not reference a snapshot and
   // just walk the process's heap from the beginning each time. Infinite
   // loops can occur if the target process changes its heap while the
   // functions below are enumerating the blocks in the heap.
   BOOL HeapFirst(PHEAPENTRY32 phe, DWORD dwProcessID,
      UINT_PTR dwHeapID) const;
   BOOL HeapNext(PHEAPENTRY32 phe) const;
   int  HowManyBlocksInHeap(DWORD dwProcessID, DWORD dwHeapId) const;
   BOOL IsAHeap(HANDLE hProcess, PVOID pvBlock, PDWORD pdwFlags) const;
public:
   static BOOL EnableDebugPrivilege(BOOL fEnable = TRUE);
   static BOOL ReadProcessMemory(DWORD dwProcessID, LPCVOID pvBaseAddress,
      PVOID pvBuffer, DWORD cbRead, PDWORD pdwNumberOfBytesRead = NULL);
};
           

--------------------------------------------

-------------   ToolHelp.cpp -----------------

--------------------------------------------

///

inline CToolhelp::CToolhelp(DWORD dwFlags, DWORD dwProcessID) {
   m_hSnapshot = INVALID_HANDLE_VALUE;
   CreateSnapshot(dwFlags, dwProcessID);
}

///

inline CToolhelp::~CToolhelp() {
   if (m_hSnapshot != INVALID_HANDLE_VALUE)
      CloseHandle(m_hSnapshot);
}

///

inline int CToolhelp::CreateSnapshot(DWORD dwFlags, DWORD dwProcessID) {
   if (m_hSnapshot != INVALID_HANDLE_VALUE)
      CloseHandle(m_hSnapshot);
   if (dwFlags == 0) {
      m_hSnapshot = INVALID_HANDLE_VALUE;
   } else {
      m_hSnapshot = CreateToolhelp32Snapshot(dwFlags, dwProcessID);
   }
   return(m_hSnapshot != INVALID_HANDLE_VALUE);
}

///

inline BOOL CToolhelp::EnableDebugPrivilege(BOOL fEnable) {
   // Enabling the debug privilege allows the application to see
   // information about service applications
   BOOL fOk = FALSE;    // Assume function fails
   HANDLE hToken;
   // Try to open this process's access token
   if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,
      &hToken)) {
      // Attempt to modify the "Debug" privilege
      TOKEN_PRIVILEGES tp;
      tp.PrivilegeCount = 1;
      LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
      tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
      AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
      fOk = (GetLastError() == ERROR_SUCCESS);
      CloseHandle(hToken);
   }
   return(fOk);
}

///

inline BOOL CToolhelp::ReadProcessMemory(DWORD dwProcessID,
   LPCVOID pvBaseAddress, PVOID pvBuffer, DWORD cbRead,
   PDWORD pdwNumberOfBytesRead) {
   return(Toolhelp32ReadProcessMemory(dwProcessID, pvBaseAddress, pvBuffer,
      cbRead, pdwNumberOfBytesRead));
}

///

inline BOOL CToolhelp::ProcessFirst(PPROCESSENTRY32 ppe) const {
   BOOL fOk = Process32First(m_hSnapshot, ppe);
   if (fOk && (ppe->th32ProcessID == 0))
      fOk = ProcessNext(ppe); // Remove the "[System Process]" (PID = 0)
   return(fOk);
}

inline BOOL CToolhelp::ProcessNext(PPROCESSENTRY32 ppe) const {
   BOOL fOk = Process32Next(m_hSnapshot, ppe);
   if (fOk && (ppe->th32ProcessID == 0))
      fOk = ProcessNext(ppe); // Remove the "[System Process]" (PID = 0)
   return(fOk);
}

inline BOOL CToolhelp::ProcessFind(DWORD dwProcessId, PPROCESSENTRY32 ppe)
   const {
   BOOL fFound = FALSE;
   for (BOOL fOk = ProcessFirst(ppe); fOk; fOk = ProcessNext(ppe)) {
      fFound = (ppe->th32ProcessID == dwProcessId);
      if (fFound) break;
   }
   return(fFound);
}

///

inline BOOL CToolhelp::ModuleFirst(PMODULEENTRY32 pme) const {
   return(Module32First(m_hSnapshot, pme));
}

inline BOOL CToolhelp::ModuleNext(PMODULEENTRY32 pme) const {
   return(Module32Next(m_hSnapshot, pme));
}
inline BOOL CToolhelp::ModuleFind(PVOID pvBaseAddr, PMODULEENTRY32 pme) const {
   BOOL fFound = FALSE;
   for (BOOL fOk = ModuleFirst(pme); fOk; fOk = ModuleNext(pme)) {
      fFound = (pme->modBaseAddr == pvBaseAddr);
      if (fFound) break;
   }
   return(fFound);
}
inline BOOL CToolhelp::ModuleFind(PTSTR pszModName, PMODULEENTRY32 pme) const {
   BOOL fFound = FALSE;
   for (BOOL fOk = ModuleFirst(pme); fOk; fOk = ModuleNext(pme)) {
      fFound = (lstrcmpi(pme->szModule,  pszModName) == 0) ||
               (lstrcmpi(pme->szExePath, pszModName) == 0);
      if (fFound) break;
   }
   return(fFound);
}

///

inline BOOL CToolhelp::ThreadFirst(PTHREADENTRY32 pte) const {
   return(Thread32First(m_hSnapshot, pte));
}

inline BOOL CToolhelp::ThreadNext(PTHREADENTRY32 pte) const {
   return(Thread32Next(m_hSnapshot, pte));
}

///

inline int CToolhelp::HowManyHeaps() const {
   int nHowManyHeaps = 0;
   HEAPLIST32 hl = { sizeof(hl) };
   for (BOOL fOk = HeapListFirst(&hl); fOk; fOk = HeapListNext(&hl))
      nHowManyHeaps++;
   return(nHowManyHeaps);
}

inline int CToolhelp::HowManyBlocksInHeap(DWORD dwProcessID,
   DWORD dwHeapID) const {
   int nHowManyBlocksInHeap = 0;
   HEAPENTRY32 he = { sizeof(he) };
   BOOL fOk = HeapFirst(&he, dwProcessID, dwHeapID);
   for (; fOk; fOk = HeapNext(&he))
      nHowManyBlocksInHeap++;
   return(nHowManyBlocksInHeap);
}

inline BOOL CToolhelp::HeapListFirst(PHEAPLIST32 phl) const {
   return(Heap32ListFirst(m_hSnapshot, phl));
}

inline BOOL CToolhelp::HeapListNext(PHEAPLIST32 phl) const {
   return(Heap32ListNext(m_hSnapshot, phl));
}

inline BOOL CToolhelp::HeapFirst(PHEAPENTRY32 phe, DWORD dwProcessID,
   UINT_PTR dwHeapID) const {
   return(Heap32First(phe, dwProcessID, dwHeapID));
}

inline BOOL CToolhelp::HeapNext(PHEAPENTRY32 phe) const {
   return(Heap32Next(phe));
}

inline BOOL CToolhelp::IsAHeap(HANDLE hProcess, PVOID pvBlock,
   PDWORD pdwFlags) const {
   HEAPLIST32 hl = { sizeof(hl) };
   for (BOOL fOkHL = HeapListFirst(&hl); fOkHL; fOkHL = HeapListNext(&hl)) {
      HEAPENTRY32 he = { sizeof(he) };
      BOOL fOkHE = HeapFirst(&he, hl.th32ProcessID, hl.th32HeapID);
      for (; fOkHE; fOkHE = HeapNext(&he)) {
         MEMORY_BASIC_INFORMATION mbi;
         VirtualQueryEx(hProcess, (PVOID) he.dwAddress, &mbi, sizeof(mbi));
         if (chINRANGE(mbi.AllocationBase, pvBlock,
            (PBYTE) mbi.AllocationBase + mbi.RegionSize)) {
            *pdwFlags = hl.dwFlags;
            return(TRUE);
         }
      }
   }
   return(FALSE);
}
           

End of File //

Jekkay Hu,胡杨

2010-12-18