Получение забытых паролей при помощи ловушек Windows

Более ранние версии подобных программ, для получения паролей, используют дырку в безопасности Windows. Однако с появлением Windows 2000 эта лазейка была исправлена и подобные программы несмогли функционировать. После нескольких неудачных попыток был найден способ скопировать пароль из запущенного приложения на любой 32-битной Windows.

Как использовать

PasswordSpy довольно проста в использовании. Вам достаточно запустить программу, содержащую забытый пароль, а так же PasswordSpy. Затем перетащить лупу из PasswordSpy на поле "****" и PasswordSpy покажет то, что скрывается за звёздочками. PasswordSpy был протестирован на Win95/98/ME и WinNT/2K/XP.

Возможности

Плюс ко всему, PasswordSpy демонстрирует некоторые полезные возможности:

  • Единственный экземпляр приложения. Если пользователь запускает вторую копию PasswordSpy, то первая копия перемещается на передний план вместо создания второй.
  • "Всегда наверху" Одной строчкой коды Вы можете установить или удалить из Вашего приложения состояние "всегда наверху" ("always on top").
  • Межпроцессова связь. PasswordSpy использует различные формы IPC включая сообщение WM_COPYDATA, а так же отображаемые в память файлы.
  • Установка ловушек. Чтобы извречь пароль в Windows2000 и WindowsXP, необходимо установить ловушку на необходимый процесс.

Подробности кода

Самая интересная часть PasswordSpy - это методика установки ловушки Windows при помощи API функции SetWindowsHookEx. При помощи этой функции можно инсталировать ловушку (hook) на всю систему либо на отдельный процесс. Существует дюжина различных типов ловушек, и каждый тип отслеживает определённые события. Когда одно из этих событий случается, то код Вашей ловушки получает управление. PasswordSpy использует ловушку WH_GETMESSAGE, которая отслеживает вызовы функций GetMessage и PeekMessage. Если Вы хотите узнать больше о функции SetWindowsHookEx то советую почитать о ней в MSDN.

Самая трудная часть в использовании ловушек Windows - это правильно сохраненить дескриптор в ловушке. Перед тем, как установить ловушку, Вам потребуются две вещи. 1) DLL, содержащая функцию ловушки и 2) ID потока, который Вы хотите перехватывать. Теперь предложим Процессу A установить ловушку на Процесс B. После перехвата Процесса B, дескриптор ловушки возвращается в Процесс A и DLL отображается в адресное пространство Процесса B. Когда происходит одно из перехваченных событий в Процессе B, то из Процесса B вызывается код ловушки (Заметьте, что код Вашей ловушки вызывается из другого процесса! Поэтому, если в ловушке вызвать функцию GetCurrentProcessId, то Вы получите PID перехваченного процесса, а не той программы, которая установила ловушку). В ловушке Вы можете делать всё что захотите, однако не забудьте, перед тем как код ловушки завершится, вызвать CallNextHookEx. Если не сделать вызова этой функции, то остальные ловушки, которые, возможно, были установлены на это же событие не смогут получить сообщение. Проблема в том, что CallNextHookEx требует дескриптор ловушки, но этот дескриптор был возвращён в Процесс A, а мы в данный момент находимся в Процессе B. Поэтому необходимо организовать некую межпроцессовую связь для передачи дескриптора ловушки.

Большинство примеров ловушек решают эту проблему путём создания "shared" секций в DLL.

#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")

Этот код создаёт единственную переменную, которая будет доступна всем загруженным экземплярам этой DLL. Если пять процессов загрузят эту DLL, то все пять получат доступ к этой переменной. Однако данный метод порождает несколько проблем. Во первых, некоторые компиляторы могут не поддерживать данную опцию. Во вторых, что если Microsoft решит изменить принцип работы секций "shared" в будующих версиях Windows; это значит, что данная технология больше не будет работать. Так же, данный метод не имеет синхронизации потоков, а так как Вы работаете с несколькими потоками, то синхронизация потоков очень важна.

Для решения этих проблем я воспользовался для IPC отображаемыми в памяти файлами, и мутексами (mutex) для синхронизации потоков. Я заключил весь этот код в класс и назвал его CIPC. Используя отображаемые в памяти файлы, я решил проблему специальной опции компилятора, так как в ней отпала необходимость а все действия производятся обычными Win32 API функциями. Плюс MMF-ы это распространённый способ доступа к данным из нескольких процессов, поэтому Microsoft врят ли что-то изменит в этой области в будующем. А так же мутексы гарантируют, что доступ потока синхронизирован.

//***********************************************
// IPC.h
//***********************************************

#ifndef _IPC_H_
#define _IPC_H_

#define IPC_SHARED_MMF _T("{34F673E0-878F-11D5-B98A-00B0D07B8C7C}")
#define IPC_MUTEX _T("{34F673E1-878F-11D5-B98A-00B0D07B8C7C}")

// Класс для межпроцессовой связи используя
// отображаемые в память файлы

class CIPC
{
public:
CIPC();
virtual ~CIPC();

bool CreateIPCMMF(void);
bool OpenIPCMMF(void);
void CloseIPCMMF(void);

bool IsOpen(void) const {return (m_hFileMap != NULL);}

bool ReadIPCMMF(LPBYTE pBuf, DWORD &dwBufSize);
bool WriteIPCMMF(const LPBYTE pBuf,
const DWORD dwBufSize);

bool Lock(void);
void Unlock(void);

protected:
HANDLE m_hFileMap;
HANDLE m_hMutex;
};

#endif


//***********************************************
// IPC.cpp
//***********************************************

#include "IPC.h"

//***********************************************
CIPC::CIPC() : m_hFileMap(NULL), m_hMutex(NULL)
{
}

//***********************************************
CIPC::~CIPC()
{
CloseIPCMMF();
Unlock();
}

//***********************************************
bool CIPC::CreateIPCMMF(void)
{
bool bCreated = false;

try
{
if(m_hFileMap != NULL)
return false; // Уже создан

// Создаём в памяти 4 килобайтный отображаемый в память
// файл для шаринга данных

m_hFileMap = CreateFileMapping((HANDLE)0xFFFFFFFF,
NULL,
PAGE_READWRITE,
0,
4096,
IPC_SHARED_MMF);
if(m_hFileMap != NULL)
bCreated = true;
}
catch(...) {}

return bCreated;
}

//***********************************************
bool CIPC::OpenIPCMMF(void)
{
bool bOpened = false;

try
{
if(m_hFileMap != NULL)
return true; // Уже открыт

m_hFileMap =
OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
FALSE,
IPC_SHARED_MMF);
if(m_hFileMap != NULL)
bOpened = true;
}
catch(...) {}

return bOpened;
}

//***********************************************
void CIPC::CloseIPCMMF(void)
{
try
{
if(m_hFileMap != NULL)
CloseHandle(m_hFileMap), m_hFileMap = NULL;
}
catch(...) {}
}

//***********************************************
bool CIPC::ReadIPCMMF(LPBYTE pBuf, DWORD &dwBufSize)
{
_ASSERTE(pBuf);

bool bSuccess = true;

try
{
if(m_hFileMap == NULL)
return false;

DWORD dwBaseMMF = (DWORD)MapViewOfFile(m_hFileMap,
FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, 0);
_ASSERTE(dwBaseMMF);

// Первый DWORD в MMF содержит размер данных
DWORD dwSizeofInBuf = dwBufSize;
CopyMemory(&dwBufSize, (LPVOID)dwBaseMMF, sizeof(DWORD));

if(dwSizeofInBuf != 0)
{
if(dwBufSize > dwSizeofInBuf)
bSuccess = false;
else
CopyMemory(pBuf,
(LPVOID)(dwBaseMMF + sizeof(DWORD)),
dwBufSize);
}

UnmapViewOfFile((LPVOID)dwBaseMMF);
}
catch(...) {}

return bSuccess;
}

//***********************************************
bool CIPC::WriteIPCMMF(const LPBYTE pBuf, const DWORD dwBufSize)
{
_ASSERTE(pBuf);

bool bSuccess = true;

try
{
if(m_hFileMap == NULL)
return false;

DWORD dwBaseMMF = (DWORD)MapViewOfFile(m_hFileMap,
FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, 0);
_ASSERTE(dwBaseMMF);

// Первый DWORD в MMF содержит размер данных
CopyMemory((LPVOID)dwBaseMMF, &dwBufSize, sizeof(DWORD));
CopyMemory((LPVOID)(dwBaseMMF + sizeof(DWORD)),
pBuf,
dwBufSize);

UnmapViewOfFile((LPVOID)dwBaseMMF);
}
catch(...) {}

return bSuccess;
}

//***********************************************
bool CIPC::Lock(void)
{
bool bLocked = false;

try
{
// First get the handle to the mutex
m_hMutex = CreateMutex(NULL, FALSE, IPC_MUTEX);
if(m_hMutex != NULL)
{
// Wait to get the lock on the mutex
if(WaitForSingleObject(m_hMutex, INFINITE) ==
WAIT_OBJECT_0)
bLocked = true;
}
}
catch(...) {}

return bLocked;
}

//***********************************************
void CIPC::Unlock(void)
{
try
{
if(m_hMutex != NULL)
{
ReleaseMutex(m_hMutex);
CloseHandle(m_hMutex);
m_hMutex = NULL;
}
}
catch(...) {}