This source file includes following definitions.
- stub_function_
- Install
- Uninstall
- set_original
- original
- patched
- Perftools_HeapCreate
- Perftools_HeapDestroy
- Perftools_HeapAlloc
- Perftools_HeapFree
- Perftools_HeapReAlloc
- Perftools_VirtualAllocEx
- Perftools_VirtualFreeEx
- Perftools_MapViewOfFileEx
- Perftools_MapViewOfFile
- Perftools_UnmapViewOfFile
- Perftools_NtUnmapViewOfSection
- Perftools_GlobalAlloc
- Perftools_GlobalFree
- Perftools_GlobalReAlloc
- Perftools_LocalAlloc
- Perftools_LocalFree
- Perftools_LocalReAlloc
- heap_
- Initialize
- Unhook
- RegisterWatcher
- UnregisterWatcher
- CreateHeap
- CloseHeap
- OnTrack
- OnUntrack
#include <windows.h>
#include "memory_hook.h"
#include "memory_watcher.h"
#include "preamble_patcher.h"
#define GET_PROC_ADDRESS(hmodule, name) \
( (Type_##name)(::GetProcAddress(hmodule, #name)) )
#define DECLARE_PATCH(name) Patch<Type_##name> patch_##name
#define INSTALL_PATCH(name) do { \
patch_##name.set_original(GET_PROC_ADDRESS(hkernel32, ##name)); \
patch_##name.Install(&Perftools_##name); \
} while (0)
#define INSTALL_NTDLLPATCH(name) do { \
patch_##name.set_original(GET_PROC_ADDRESS(hntdll, ##name)); \
patch_##name.Install(&Perftools_##name); \
} while (0)
#define UNINSTALL_PATCH(name) patch_##name.Uninstall();
typedef HANDLE (WINAPI *Type_HeapCreate)(DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize);
typedef BOOL (WINAPI *Type_HeapDestroy)(HANDLE hHeap);
typedef LPVOID (WINAPI *Type_HeapAlloc)(HANDLE hHeap, DWORD dwFlags,
DWORD_PTR dwBytes);
typedef LPVOID (WINAPI *Type_HeapReAlloc)(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes);
typedef BOOL (WINAPI *Type_HeapFree)(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem);
typedef HGLOBAL (WINAPI *Type_GlobalAlloc)(UINT uFlags, SIZE_T dwBytes);
typedef HGLOBAL (WINAPI *Type_GlobalReAlloc)(HGLOBAL hMem, SIZE_T dwBytes,
UINT uFlags);
typedef HGLOBAL (WINAPI *Type_GlobalFree)(HGLOBAL hMem);
typedef HLOCAL (WINAPI *Type_LocalAlloc)(UINT uFlags, SIZE_T uBytes);
typedef HLOCAL (WINAPI *Type_LocalReAlloc)(HLOCAL hMem, SIZE_T uBytes,
UINT uFlags);
typedef HLOCAL (WINAPI *Type_LocalFree)(HLOCAL hMem);
typedef LPVOID (WINAPI *Type_VirtualAllocEx)(HANDLE process, LPVOID address,
SIZE_T size, DWORD type,
DWORD protect);
typedef BOOL (WINAPI *Type_VirtualFreeEx)(HANDLE process, LPVOID address,
SIZE_T size, DWORD type);
typedef LPVOID (WINAPI *Type_MapViewOfFile)(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);
typedef LPVOID (WINAPI *Type_MapViewOfFileEx)(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap,
LPVOID lpBaseAddress);
typedef BOOL (WINAPI *Type_UnmapViewOfFile)(LPVOID lpBaseAddress);
typedef DWORD (WINAPI *Type_NtUnmapViewOfSection)(HANDLE process,
LPVOID lpBaseAddress);
template<class T>
class Patch {
public:
Patch<T>()
: original_function_(NULL),
patch_function_(NULL),
stub_function_(NULL) {
}
~Patch<T>() {
Uninstall();
}
void Install(T func) {
patch_function_ = func;
CHECK(patch_function_ != NULL);
CHECK(original_function_ != NULL);
CHECK(stub_function_ == NULL);
CHECK(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(original_function_,
patch_function_, &stub_function_));
}
void Uninstall() {
if (stub_function_)
sidestep::PreamblePatcher::Unpatch(original_function_,
patch_function_, stub_function_);
stub_function_ = NULL;
}
void set_original(T original) { original_function_ = original; }
T original() { return original_function_; }
T patched() { return patch_function_; }
T operator()() {
DCHECK(stub_function_);
return stub_function_;
}
private:
T original_function_;
T patch_function_;
T stub_function_;
};
DECLARE_PATCH(HeapCreate);
DECLARE_PATCH(HeapDestroy);
DECLARE_PATCH(HeapAlloc);
DECLARE_PATCH(HeapReAlloc);
DECLARE_PATCH(HeapFree);
DECLARE_PATCH(VirtualAllocEx);
DECLARE_PATCH(VirtualFreeEx);
DECLARE_PATCH(MapViewOfFile);
DECLARE_PATCH(MapViewOfFileEx);
DECLARE_PATCH(UnmapViewOfFile);
DECLARE_PATCH(GlobalAlloc);
DECLARE_PATCH(GlobalReAlloc);
DECLARE_PATCH(GlobalFree);
DECLARE_PATCH(LocalAlloc);
DECLARE_PATCH(LocalReAlloc);
DECLARE_PATCH(LocalFree);
DECLARE_PATCH(NtUnmapViewOfSection);
static HANDLE WINAPI Perftools_HeapCreate(DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize) {
if (dwInitialSize > 4096)
dwInitialSize = 4096;
return patch_HeapCreate()(flOptions, dwInitialSize, dwMaximumSize);
}
static BOOL WINAPI Perftools_HeapDestroy(HANDLE hHeap) {
return patch_HeapDestroy()(hHeap);
}
static LPVOID WINAPI Perftools_HeapAlloc(HANDLE hHeap, DWORD dwFlags,
DWORD_PTR dwBytes) {
LPVOID rv = patch_HeapAlloc()(hHeap, dwFlags, dwBytes);
MemoryHook::hook()->OnTrack(hHeap, reinterpret_cast<int32>(rv), dwBytes);
return rv;
}
static BOOL WINAPI Perftools_HeapFree(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem) {
size_t size = 0;
if (lpMem != 0) {
size = HeapSize(hHeap, 0, lpMem);
}
MemoryHook::hook()->OnUntrack(hHeap, reinterpret_cast<int32>(lpMem), size);
return patch_HeapFree()(hHeap, dwFlags, lpMem);
}
static LPVOID WINAPI Perftools_HeapReAlloc(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes) {
LPVOID rv = Perftools_HeapAlloc(hHeap, dwFlags, dwBytes);
DCHECK_EQ((HEAP_REALLOC_IN_PLACE_ONLY & dwFlags), 0u);
if (lpMem != 0) {
size_t size = HeapSize(hHeap, 0, lpMem);
if (size > dwBytes)
size = dwBytes;
memcpy(rv, lpMem, size);
Perftools_HeapFree(hHeap, dwFlags, lpMem);
}
return rv;
}
static LPVOID WINAPI Perftools_VirtualAllocEx(HANDLE process, LPVOID address,
SIZE_T size, DWORD type,
DWORD protect) {
bool already_committed = false;
if (address != NULL) {
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(address, &info, sizeof(info)));
if (info.State & MEM_COMMIT) {
already_committed = true;
CHECK(size >= info.RegionSize);
}
}
bool reserving = (address == NULL) || (type & MEM_RESERVE);
bool committing = !already_committed && (type & MEM_COMMIT);
LPVOID result = patch_VirtualAllocEx()(process, address, size, type,
protect);
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(result, &info, sizeof(info)));
size = info.RegionSize;
if (committing)
MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result), size);
return result;
}
static BOOL WINAPI Perftools_VirtualFreeEx(HANDLE process, LPVOID address,
SIZE_T size, DWORD type) {
int chunk_size = size;
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(address, &info, sizeof(info)));
if (chunk_size == 0)
chunk_size = info.RegionSize;
bool decommit = (info.State & MEM_COMMIT) != 0;
if (decommit)
MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(address),
chunk_size);
return patch_VirtualFreeEx()(process, address, size, type);
}
static base::Lock known_maps_lock;
static std::map<void*, int> known_maps;
static LPVOID WINAPI Perftools_MapViewOfFileEx(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap,
LPVOID lpBaseAddress) {
LPVOID result = patch_MapViewOfFileEx()(hFileMappingObject, dwDesiredAccess,
dwFileOffsetHigh, dwFileOffsetLow,
dwNumberOfBytesToMap, lpBaseAddress);
{
base::AutoLock lock(known_maps_lock);
MEMORY_BASIC_INFORMATION info;
if (known_maps.find(result) == known_maps.end()) {
CHECK(VirtualQuery(result, &info, sizeof(info)));
known_maps[result] = 1;
MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result),
info.RegionSize);
} else {
known_maps[result] = known_maps[result] + 1;
}
}
return result;
}
static LPVOID WINAPI Perftools_MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap) {
return Perftools_MapViewOfFileEx(hFileMappingObject, dwDesiredAccess,
dwFileOffsetHigh, dwFileOffsetLow,
dwNumberOfBytesToMap, 0);
}
static BOOL WINAPI Perftools_UnmapViewOfFile(LPVOID lpBaseAddress) {
return patch_UnmapViewOfFile()(lpBaseAddress);
}
static DWORD WINAPI Perftools_NtUnmapViewOfSection(HANDLE process,
LPVOID lpBaseAddress) {
{
base::AutoLock lock(known_maps_lock);
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(lpBaseAddress, &info, sizeof(info)));
if (known_maps.find(lpBaseAddress) != known_maps.end()) {
if (known_maps[lpBaseAddress] == 1) {
MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(lpBaseAddress),
info.RegionSize);
known_maps.erase(lpBaseAddress);
} else {
known_maps[lpBaseAddress] = known_maps[lpBaseAddress] - 1;
}
}
}
return patch_NtUnmapViewOfSection()(process, lpBaseAddress);
}
static HGLOBAL WINAPI Perftools_GlobalAlloc(UINT uFlags, SIZE_T dwBytes) {
uFlags &= ~GMEM_MOVEABLE;
HGLOBAL rv = patch_GlobalAlloc()(uFlags, dwBytes);
return rv;
}
static HGLOBAL WINAPI Perftools_GlobalFree(HGLOBAL hMem) {
return patch_GlobalFree()(hMem);
}
static HGLOBAL WINAPI Perftools_GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes,
UINT uFlags) {
if (dwBytes == 0) {
return patch_GlobalReAlloc()(hMem, dwBytes, uFlags);
}
HGLOBAL rv = Perftools_GlobalAlloc(uFlags, dwBytes);
if (hMem != 0) {
size_t size = GlobalSize(hMem);
if (size > dwBytes)
size = dwBytes;
memcpy(rv, hMem, size);
Perftools_GlobalFree(hMem);
}
return rv;
}
static HLOCAL WINAPI Perftools_LocalAlloc(UINT uFlags, SIZE_T dwBytes) {
uFlags &= ~LMEM_MOVEABLE;
HLOCAL rv = patch_LocalAlloc()(uFlags, dwBytes);
return rv;
}
static HLOCAL WINAPI Perftools_LocalFree(HLOCAL hMem) {
return patch_LocalFree()(hMem);
}
static HLOCAL WINAPI Perftools_LocalReAlloc(HLOCAL hMem, SIZE_T dwBytes,
UINT uFlags) {
if (dwBytes == 0) {
return patch_LocalReAlloc()(hMem, dwBytes, uFlags);
}
HGLOBAL rv = Perftools_LocalAlloc(uFlags, dwBytes);
if (hMem != 0) {
size_t size = LocalSize(hMem);
if (size > dwBytes)
size = dwBytes;
memcpy(rv, hMem, size);
Perftools_LocalFree(hMem);
}
return rv;
}
bool MemoryHook::hooked_ = false;
MemoryHook* MemoryHook::global_hook_ = NULL;
MemoryHook::MemoryHook()
: watcher_(NULL),
heap_(NULL) {
CreateHeap();
}
MemoryHook::~MemoryHook() {
CloseHeap();
}
bool MemoryHook::Initialize() {
if (global_hook_ == NULL)
global_hook_ = new MemoryHook();
return true;
}
bool MemoryHook::Hook() {
DCHECK(!hooked_);
if (!hooked_) {
DCHECK(global_hook_);
HMODULE hkernel32 = ::GetModuleHandle(L"kernel32");
CHECK(hkernel32 != NULL);
HMODULE hntdll = ::GetModuleHandle(L"ntdll");
CHECK(hntdll != NULL);
INSTALL_PATCH(HeapCreate);
INSTALL_PATCH(HeapDestroy);
INSTALL_PATCH(HeapAlloc);
INSTALL_PATCH(HeapReAlloc);
INSTALL_PATCH(HeapFree);
INSTALL_PATCH(VirtualAllocEx);
INSTALL_PATCH(VirtualFreeEx);
INSTALL_PATCH(MapViewOfFileEx);
INSTALL_PATCH(MapViewOfFile);
INSTALL_PATCH(UnmapViewOfFile);
INSTALL_NTDLLPATCH(NtUnmapViewOfSection);
INSTALL_PATCH(GlobalAlloc);
INSTALL_PATCH(GlobalReAlloc);
INSTALL_PATCH(GlobalFree);
INSTALL_PATCH(LocalAlloc);
INSTALL_PATCH(LocalReAlloc);
INSTALL_PATCH(LocalFree);
hooked_ = true;
}
return true;
}
bool MemoryHook::Unhook() {
if (hooked_) {
UNINSTALL_PATCH(HeapCreate);
UNINSTALL_PATCH(HeapDestroy);
UNINSTALL_PATCH(HeapAlloc);
UNINSTALL_PATCH(HeapReAlloc);
UNINSTALL_PATCH(HeapFree);
UNINSTALL_PATCH(VirtualAllocEx);
UNINSTALL_PATCH(VirtualFreeEx);
UNINSTALL_PATCH(MapViewOfFile);
UNINSTALL_PATCH(MapViewOfFileEx);
UNINSTALL_PATCH(UnmapViewOfFile);
UNINSTALL_PATCH(NtUnmapViewOfSection);
UNINSTALL_PATCH(GlobalAlloc);
UNINSTALL_PATCH(GlobalReAlloc);
UNINSTALL_PATCH(GlobalFree);
UNINSTALL_PATCH(LocalAlloc);
UNINSTALL_PATCH(LocalReAlloc);
UNINSTALL_PATCH(LocalFree);
hooked_ = false;
}
return true;
}
bool MemoryHook::RegisterWatcher(MemoryObserver* watcher) {
DCHECK(global_hook_->watcher_ == NULL);
if (!hooked_)
Hook();
DCHECK(global_hook_);
global_hook_->watcher_ = watcher;
return true;
}
bool MemoryHook::UnregisterWatcher(MemoryObserver* watcher) {
DCHECK(hooked_);
DCHECK(global_hook_->watcher_ == watcher);
global_hook_->watcher_ = NULL;
return Unhook();
}
bool MemoryHook::CreateHeap() {
DCHECK(heap_ == NULL);
heap_ = HeapCreate(0, 0, 0);
DCHECK(heap_ != NULL);
return heap_ != NULL;
}
bool MemoryHook::CloseHeap() {
DCHECK(heap_ != NULL);
HeapDestroy(heap_);
heap_ = NULL;
return true;
}
void MemoryHook::OnTrack(HANDLE heap, int32 id, int32 size) {
if (heap == heap_)
return;
if (watcher_)
watcher_->OnTrack(heap, id, size);
}
void MemoryHook::OnUntrack(HANDLE heap, int32 id, int32 size) {
if (heap == heap_)
return;
if (watcher_)
watcher_->OnUntrack(heap, id, size);
}