搞出来很久了,看到看雪上不少同学都很期待,干脆发出来。
本文大部分研究成果归功于mentaldease,欲读此文,请遵守以下约定:
1. 绝不可利用本文所揭示的技术编写或者辅助编写盗号木马,危害中国网民。
2. 也不可将本文所揭示的技术直接或间接传授给写盗号木马的败类。
3. 违背以上两点约定者,天打雷劈,断子绝孙...(以下省略2000字)。
**********************************************************
不能遵守以上三点约定者,请立即离开!
**********************************************************
在分析某著名IM软件之前,尝试直接用低级键盘钩子截获密码。
笔者发现可以获取正确的某著名IM软件号,但是截获的某著名IM软件密码里面却有很多虚假按键:
事实上,只要将鼠标焦点放在密码输入框里,某著名IM软件就自动产生虚假按键。所以,靠低级键盘钩子不能截获正确的某著名IM软件密码。
用XueTr查看系统中的消息钩子。笔者发现只要用户双击打开某著名IM软件2009登录窗口,某著名IM软件就会创建一个WH_KEYBOARD_LL低级键盘钩子,如图2
我们知道WH_KEYBOARD_LL钩子的优先级高于WH_KEYBOARD和WH_GETMESSAGE钩子。显然某著名IM软件2009的低级键盘钩子会比其他类型的钩子先执行,某著名IM软件获取用户的真实按键后再伪造虚假按键。
笔者在成功截获某著名IM软件2009密码之后,挂钩了SetWindowHookEx观察某著名IM软件创建WH_KEYBOARD_LL钩子的过程:
由上图可见某著名IM软件2009每隔不到0.2秒就创建一个低级键盘钩子,这样确保自己的钩子始终在钩子队列的最顶层。
我们对SetWindowHookEx函数下断,但是断不下来,显然是某著名IM软件在这里有反调试。删除刚才的断点,下一个硬件断点,程序断下,执行到返回:
来到042437C5处,tssafeed模块中看到如下指令:
042437BB 56 push esi
042437BC 50 push eax
042437BD FF75 0C push dword ptr [ebp+C]
042437C0 E8 AB550000 call 04248D70 ; jmp 到 MSVCRT.memcmp
042437C5 8B3D 28A02404 mov edi, dword ptr [424A028] ; KERNEL32.GetCurrentProcess
042437CB 83C4 0C add esp, 0C
042437CE 85C0 test eax, eax
042437D0 0F84 8F000000 je 04243865
猜测这里很可能就是用memcmp 函数校验SetWindowsHookExA(可能还有其他函数)的代码。在042437BB 处F2,再F9让程序运行,把鼠标焦点移到密码输入框时,断下来了:
单步执行到042437C0,看堆栈信息:
果然是对SetWindowsHookExA初的内存进行校验。memcmp返回0则认为该处内存没有改变。
在77D311D1处下硬件执行断点,成功断下后,堆栈信息如图:
HookType为13,即是WH_KEYBOARD_LL钩子,其HookProc为 04260F1B,转到该地址看看某著名IM软件的钩子回调例程:
04260F1B 55 push ebp
04260F1C 8BEC mov ebp, esp
04260F1E 83EC 30 sub esp, 30
04260F21 56 push esi
04260F22 57 push edi
04260F23 C745 FC F00C260>mov dword ptr [ebp-4], 4260CF0
04260F2A 8D7D D0 lea edi, dword ptr [ebp-30]
04260F2D B9 07000000 mov ecx, 7
04260F32 33C0 xor eax, eax
04260F34 FC cld
04260F35 F3:AB rep stos dword ptr es:[edi]
04260F37 8B45 10 mov eax, dword ptr [ebp+10]
04260F3A 8945 F8 mov dword ptr [ebp-8], eax
04260F3D C745 F4 0000000>mov dword ptr [ebp-C], 0
04260F44 E8 20000000 call 04260F69 ; 跟进
… …
04260F69 处的call:
04260F69 5F pop edi
04260F6A 8B4D F4 mov ecx, dword ptr [ebp-C]
04260F6D 833C8F 00 cmp dword ptr [edi+ecx*4], 0
04260F71 74 1D je short 04260F90
04260F73 8B55 F4 mov edx, dword ptr [ebp-C]
04260F76 8B0497 mov eax, dword ptr [edi+edx*4]
04260F79 50 push eax
04260F7A 8B4D FC mov ecx, dword ptr [ebp-4]
04260F7D FF51 50 call dword ptr [ecx+50] ; USER32.GetAsyncKeyState
04260F80 0FBFD0 movsx edx, ax
04260F83 81E2 00800000 and edx, 8000
04260F89 75 05 jnz short 04260F90
04260F8B FF45 F4 inc dword ptr [ebp-C]
04260F8E ^ EB DA jmp short 04260F6A
04260F90 8B55 F8 mov edx, dword ptr [ebp-8]
04260F93 8B02 mov eax, dword ptr [edx]
04260F95 C745 F0 0000000>mov dword ptr [ebp-10], 0
04260F9C E8 3C000000 call 04260FDD ;跟进
… …
这个函数中有很多花指令,这里就不仔细分析了,对GetForegroundWindow下断,只要将鼠标焦点移到某著名IM软件登录窗口,就会断下,再对CallNextHookEx下断观察。它的大概功能猜也猜得出来:某著名IM软件自己获取按键之后,发送虚假按键,然后直接返回。现在就是找出某著名IM软件2009到底是用什么函数发送虚假按键。对keybd_event下硬件执行断点,没有断下,对sendInput下硬件断点,断下了:
Sendinput函数原型:
UINT SendInput(
UINT nInputs, // count of input events
LPINPUT pInputs, // array of input events
int cbSize // size of structure
);
typedef struct tagINPUT {
DWORD type;
union {
MOUSEINPUT mi;
KEYBDINPUT ki;
HARDWAREINPUT hi;
};
} INPUT, *PINPUT;
观察堆栈:
数据窗口跟随到0012FA94:
如图所示 01表示的是输入类型INPUT_KEYBOARD,pInouts的结构如下:
typedef struct tagKEYBDINPUT {
WORD wVk;
WORD wScan;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KEYBDINPUT, *PKEYBDINPUT;
如图所示0x38对应的虚拟按键是数字8,用我们刚才写的那个低级键盘钩子可以看到,所接收到的虚假按键正是8.
现在我们能够写出某著名IM软件的低级键盘钩子处理例程:
HWND hwnd = GetForegroundWindow();
If( hWnd == hPassEditWnd)
{
MapVirtualKey();
SendInput();
return 1;
}
else
{
return CallNextHookEx(ncode,…);
}
现在2009登录窗口保护的原理已经分析清楚,获取某著名IM软件密码的方法也就有了。其关键就是绕过某著名IM软件对SetWindowsHookExA等函数的内存校验,然后hook SetWindowsHookExA,判断如果是某著名IM软件的WH_KEYBOARD_LL钩子,就替换其回调函数,然后在my_KeyboardllProc中截获密码,下图是截获密码的效果图:
PS:某著名IM软件2009的软键盘也被我和mentaldease兄突破,并成功截获密码:
演示程序:GetXXPass.rar