现在的位置: 首页 > 综合 > 正文

用vc++穷举windows应用程序密码

2013年01月12日 ⁄ 综合 ⁄ 共 8313字 ⁄ 字号 评论关闭

一、引言

  随着计算机信息技术的发展,人们越来越重视信息的安全性,信息数据的安全保密已经成为影响计算机发展的一个重要课题。机密文件、商业情报、银行账号、网络密码、科技成果、包括私人信件等等,都成了用户为难以存放发愁的心病。密码可以说是他们的唯一的精神寄托,通过密码,他们可以对这些信息进行加密,或者通过密码对用户存取信息进行授权,非法用户禁止存取有关信息。

  但是有了密码,用户也不能高枕无忧,因为密码都是人工键入的,都是由键盘的可见字符组成(包括汉字),如果一个非法用户不幸猜中了你的密码,哪怕只有千万分之一的几率,也会给你的数据安全带来潜在威胁,而不光会污染你的数据,丧心病狂的人甚至会在瞬间摧毁你苦心经营多年的成果。更何况现在有了电脑作为工具,它可以在一分钟之间穷举成千上万个密码,利用局域网分布式计算,一个小时内穷举十位以下的所有密码,更不幸的是我们的用户所用的密码都是出奇的易记(击),他们偏好单用数字和字母,这为他们数据安全埋下了危机。

  本文正是通过引用一个穷举密码的例子来提醒用户,在密码问题上不要大意,密码要求尽可能长,而且不要鄙视非数字和非字母字符,密码要定期更换。动态密码,相对更为安全。

  二、实现原理:

  我们的用户一般都过输入密码的经历,一般情况下系统都会显示一个对话框,提醒用户输入密码,密码编辑框一般具有ES_PASSWORD 风格,用户输入完成后,要求按《确定》按钮确认,如果密码正确,系统就会开始工作,否则系统会提示你密码错误,要求按《确定》按钮重新输入。无论我们运用鼠标的技能有多高,如果我们想在短时间内穷举所有可能密码,根本不现实。但是高速运行的计算机可以做到。

  我们可以通过编程,利用Windows API 函数EnumWindows 和EnumChildWindows对当前运行的所有程序的所有窗口(包括子窗口即控件)进行遍历,通过窗口标题查找密码输入和出错确认重新输入窗口,通过按钮标题查找我们应该单击的按钮,通过ES_PASSWORD 查找我们需要键入的密码窗口。

  我们可以通过向密码输入窗口发送WM_SETTEXT消息模拟输入密码,通过向按钮窗口发送WM_COMMAND消息模拟单击。所有这一切我们可以把它放在一个线程内运行,我们用户可以随时暂停、随时中断退出。我们可以在枚举密码的过程中,把密码保存在一个文件中,以便下次接着下一个序列的密码再次穷举。直到找到密码为止。找到密码后,由于不再出现密码输入窗口,程序虽然仍在继续枚举窗口,但由于找不到对应窗口,不会发送任何消息。我们打开记录文件推算前一个序列的密码即可找到对应的密码。

  枚举密码的方法有多种,这跟密码的组成有关,如果你忘记了你的wps 2000 的文件密码,而且你确切知道密码由数字组成,你就可以采用数字穷举,当然程序还提供了其它穷举方法,包括大写字母、小写字母、大小写混合、字母数字、标点符号字母数字组合等等。每一种穷举方法都有进度记录保存,下次穷举不用从头开始,字符个数从一位到多位自动增长。你可以通过编辑配置文件setup.ini控制穷举进度,尤其是当你知道密码的某一位确切为某一个字符时,或者确切知道密码有几位时,通过修改当前密码(退出程序情况下),可以大大加快穷举速度,以便短时间内找回那根遗忘的神经,唤醒那存储密码的神经元细胞。

  三、具体细节:

  ◆进程和线程

  Windows应用程序有一个或多个进程组成。所谓进程,用最简单的术语说就是装入内存并准备运行的可执行的程序。进程是资源分配的独立单位。进程具有动态、并发、独立等特点。进程具有就绪、执行、堵塞三种基本状态,win 32中的每个进程都有自己的私有虚拟地址空间,进程有代码、数据和进程中的线程可用的其他系统资源组成,每个进程都由单线程开始,并可创建新的线程和其他进程。

  在一个进程中运行着一个或多个线程,线程是操作系统分配处理器时间片的最小单位,一个线程可以执行进程中的任何一部分代码,包括当前被其他线程执行的部分。线程能独立执行程序代码的任何部分,共享虚拟地址空间并能访问全局变量和进程系统资源。各个线程根据其调度优先级分配CPU,线程具有进程的许多特征,又称为轻量级的进程。由于线程基本上不拥有系统资源,仅占有一点在运行中不可缺少的资源(机器寄存器、内核堆栈、线程环境块和用户堆栈等),由于应用程序由进程组成,进程由线程组成。同一进程线程的切换不会引起进程的切换,因此,线程的调度开销要远远小于进程的调度开销。

  在MFC类库中,每个CWinThread对象表示程序的一个执行线程,MFC 将线程分为两种类型:用户界面线程(user-interface thread)和工作者线程(worker thread),前者用于消息循环或消息泵,用于消息处理。后者没有消息循环用于无需用户响应的后台任务。

  工作者线程分两步创建:

  1.创建线程函数

  DWORD WINAPI ThreadFunc( LPVOID );

  参数值是在创建线程对象时传递给构造函数的值,它既可为标量值,也可以为指向多个参数结构的指针,还可以省略。

  2.调用AfxBeginThread启动工作者线程。此时,全局函数AfxBeginThread采用以下原型:

CWinThread * AfxBeginThread(
AFX_THREADPROC pfnThreadProc ,

LPVOID pParam,

int nPriority=THREAD_PRIORITY_NORMAL,

UINT nStackSize=0,

DWORD dwCreateFlags=0,

LPSECURITY_ATTRIBUTES lpsecurityAttrs=NULL);
 

  其中:pfnThreadProc 即为工作者线程的线程函数。

  pParam 为传递给工作者线程函数的入口参数。

  nPriority 创建优先级别可以使用SetThreadPriority设置,默认值为THREAD_PRIORITY_NORMAL

  dwCreateFlags 创建标志0为创建后立即运行,若为CREDTE_SUSPEND,创建后处于挂起状态。

  在工作者线程函数中,执行return 语句或者执行AfxEndThread函数将导致线程运行终止。

  我们可以通过设置CWinThread 对象的成员变量和成员函数来对线程进行控制。

  m_bAutoDelete为true ,表示线程终止后自动销毁。

  m_hThread 表示当前线程句柄。

  ResumeThread 使挂起线程恢复运行

  SuspendThread挂起运行线程

  SetThreadPriority设置线程的运行优先级

  其他成员请参考MFC 类库和MSDN帮助文档。

  ◆同步对象

  由于系统为了提高穷举效率,采用了多线程编程,以便加快模拟输入响应速度,在尽可能短的时间内得到真正的密码。

  由于线程之间共享进程资源,这样就会带来同步的问题。譬如,由于多个线程并发地存在于系统之中同时运行(时间片轮转),可能会存在这样的情况,两个线程都在修改窗口密码编辑框文本,前一个线程修改后还未来得及按《确定》按钮,另一个线程又修改了窗口的文本,造成前一个密码文本丢失,导致穷举密码不全,可能穷举失败。一个线程正在向密码编辑框填写文本,而另一个线程又在改写该文本,这样会导致前一个线程无法辨识究竟用的是哪一个版本的密码文本。会导致重复穷举或者残缺穷举,甚至系统死锁。

  为了保证进程或线程能够按照一定的顺序推进向前执行,windows 提供了一组等待函数和同步对象用于控制同步。包括信号灯、互斥量、事件、临界区。同步的思想很简单,如果一个线程遇到一个信号变量无信号时,它能使自己处于睡眠状态一旦有了信号系统会唤醒线程,使线程接着执行。

  本文仅仅简要叙述以下互斥信号量,其他信号量不在本文谈论之列,请用户自行参考《win 32 编程指南》或 MSDN .

  互斥信号量用于串行某一资源的使用,即任一时刻仅允许至多一个线程访问某一资源,一个互斥信号灯只能为一个线程所拥有,任何试图要求互斥标志的其他线程都将被锁住,直到互斥标志被释放为止。

  在程序采用了多个线程对窗口进行枚举,一旦某线程发现密码窗口或出错要求重试窗口,该线程便首先获得互斥信号量,该线程具有枚举子窗口的权利,能够向密码编辑框输入文本,发送键盘鼠标消息,其他线程因无法获得互斥标志处于睡眠状态。这正如列车上的卫生间,一次只能进一个人,进去后先关门,其他人被挡在大门之外,直到里面的人出来,别人方可进去。

  CreateMutex 函数用于创建一个命名的或无命名的互斥量对象。

HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,

  // 安全属性指针

    BOOL bInitialOwner, // 最初的拥有标志

    LPCTSTR lpName // 指向互斥量的对象名

   );
 

参数:

    lpMutexAttributes

    指向一个安全属性结构,决定返回句柄能否被子进程继承并拥有,如果此值为NULL,那么句柄无法继承拥有。

    bInitialOwner

    指示互斥对象的最初拥有者,此值为真,请求互斥量的线程可以直接获得互斥量的拥有权。否则互斥量不被拥有。
lpName

    指向一个以0结尾的字符串, 来表示互斥对象的名字,名字限于MAX_PATH 个字符串,可以包含除 / 以外的任何字符,名字大小写敏感。

    若此值为NULL ,互斥对象没有名字。

    返回值:

    函数执行成功,返回指向互斥对象的句柄。

    WaitForSingleObject函数只有在下列情况发生时才返回,否则处于睡眠状态。

    1. 指定的对象处于信号状态,

    2. 等待事件超时。

 DWORD WaitForSingleObject(
    HANDLE hHandle, // 等待对象的句柄

    DWORD dwMilliseconds    // 超时的毫秒数

    );
 

  参数:

  hHandle

  等待对象标识,可以采用上面CreateMutex返回的句柄

  dwMilliseconds

  指定的超时的毫秒数,若为INFINITE,超时不限

  返回值:

  执行成功,返回值预示着导致函数返回的事件发生。

  注释:

  WaitForSingleObject函数首先检查指定的对象当前状态,如果当前对象处于无信号状态,调用函数的线程将进入有效等待状态,在信号来临或超时发生之前,在等待信号的过程中线程消耗非常有限的处理器时间。

  在返回之前,等待函数会修改某些同步对象的状态。修改仅适用于那些会导致函数返回的对象。对互斥量对象,互斥量有信号时,它不为任何线程所拥有,用线程的等待函数会获得互斥量的拥有权,一旦得到互斥量的拥有权,它就会修改互斥量为无信号状态。

  枚举操作完成后,线程要调用ReleaseMutex函数以便释放互斥量的拥有权,从而使其他没有得到响应的线程唤醒。

  ◆窗口枚举

  EnumWindows 函数通过借助于应用程序定义的回调函数传递每个窗口句柄枚举所有顶层的屏幕窗口。直到最后一个顶层窗口被枚举或者回调函数返回false ,EnumWindows 函数才会退出停止枚举过程。

  函数原型:

BOOL EnumWindows(
    WNDENUMPROC lpEnumFunc, // 指向回调函数

LPARAM lParam     // 应用程序定义的参数值

   );
 

  参数:

  lpEnumFunc

  指向一个应用程序定义的回调函数。

  lParam

  指定一个32位的应用程序定义的参数值传递给回调函数。

  返回值:

  函数执行成功返回非零,否则返回零。

  注释:

  EnumWindows 函数不会枚举子窗口,这个函数相比而言比循环调用GetWindows函数可靠。调用GetWindow函数的应用程序枚举窗口时可能会陷入一个死循环,或者引用的窗口句柄已经被破坏。

  EnumWindowsProc 函数是一个用户定义的回调函数,它能够接受顶层窗口句柄,并把返回的函数值传递给EnumWindows 函数或 EnumDesktopWindows 函数。

  函数原型:

BOOL CALLBACK EnumWindowsProc(
    HWND hwnd,  // 父窗口句柄

    LPARAM lParam   // 应用程序定义的参数值

   );
 

参数:

    hwnd

    标是一个顶层窗口

    lParam

    指定一个传递给EnumWindows或EnumDesktopWindows的应用程序定义参数值。

    返回值:

    若应用程序想持续枚举窗口,必须返回true.返回false停止枚举。

    注释:

    这个回调函数可以执行任何渴望的任务,应用程序必须通过传递函数地址给  EnumWindows或EnumDesktopWindows注册回调函数。

    EnumWindowsProc 是一个应用程序定义的函数名,该函数声明为 WNDENUMPROC 类型。

    ◆子窗口枚举

    EnumChildWindows 函数通过借助于应用程序定义的回调函数传递每一个子窗口的窗口句柄枚举所有隶属于指定父窗口的子窗口,直到最后一个子窗口被枚举或者回调函数返回false,

    EnumChildWindows 才会停止枚举子窗口。

    函数原型:

BOOL EnumChildWindows(
    HWND hWndParent,    // 父窗口句柄

    WNDENUMPROC lpEnumFunc, // 回调函数指针

    LPARAM lParam   // 应用程序定义的参数值

   );
 
参数:

  hWndParent

  标识一个其子窗口将被枚举的子窗口。

  lpEnumFunc

  指向一个应用程序定义的回调函数。

  lParam

  标识一个传递给回调函数的应用程序定义的32位参数值

  返回值:

  执行成功返回非零,否则返回零。

  注释:

  EnumChildWindows 函数既不会枚举为指定窗口拥有的顶层窗口,也不会枚举其它拥有的窗口。如果一个子窗口已经创建它自己的子窗口,这个函数同样也会枚举这些子窗口。

  在枚举的过程中,子窗口按照Z顺序被移动或被改变位置,不会影响枚举结果。函数不会枚举一个在枚举之前被破坏的子窗口,也不会枚举在枚举过程中创建的子窗口。

  同EnumWindows 函数一样,这个函数比调用GetWindow函数可靠,因为后者会可能陷入死循环,或者参考到一个已经破坏的句柄。

  EnumChildProc函数是一个应用程序定义的回调函数,这个函数能够接受子窗口句柄,作为调用EnumChildWindows 的结果。

  函数原型:

BOOL CALLBACK EnumChildProc(
    HWND hwnd,  // 子窗口句柄

    LPARAM lParam   // 应用程序定义的参数值

   );
 

  参数:

  hwnd

  标识一个隶属于由EnumChildWindows指定的父窗口的子窗口。

  lParam

  指定一个在EnumChildWindows 给定的应用程序定义的参数值。

  返回值:

  持续枚举子窗口,程序必须返回true.返回false 停止枚举。

  注释:

  回调函数可以执行任何渴望的任务。应用程序必须通过传递函数地址给EnumChildWindows来注册回调函数。

  ·密码枚举

  我们知道组成密码的字符由许多种,有人喜欢单用数字,有人喜欢单用字母(包括大小写),也有的是用一些诸如!。#“等符号,我们可以分情况区别对待。密码的长短也是一个制约穷举的一个重要因素,密码长度越长,密码的排列组合种类越多,穷举时间越长。

  我们把所有的密码字符都看成字符串,把字符组成集合视为密码字符集。如{0,1,2,3,4,5,6,7,8,9}、{A、B、C、D、E、F、G、H、I、J、K、L、M、N、O、P、Q、R、S、T、U、V、W、X、Y、Z}等都称之为密码字符集。每一个密码每一位都由这些字符集的任意一个字符组成。若字符集中有N个元素,则长度为M的密码根据排列组合共有N的M次方种排法。我们只要将密码字符集的各个要素逐次地放入密码的各个位置,就能穷举长度为M的密码。而长度M可以从1开始自动增长。就像密码开始从 0穷举到9,长度变化为 2,密码在从00穷举到99一样,我们只需变换密码字符集即可变换密码的组成元素。给出密码的算法都一样。每得出一个密码,我们都可以将其存入文件,下次枚举时可以通过这个密码推算下一个密码,如上一个密码为ZZ,则下一个密码应为AAA,再下一个为AAB … AAZ…ABA……ABZ…AZZ…BAA…依次类推。一直推算到ZZZ,接着又是AAAA.有点类似于十进制、二十六进制、五十二进制,只不过这里是各位数不是数字而是字符,字符来自我们自己定义的密码字符集。组成的密码不是数字,而是字符串。

  程序还采用了字典穷举法,密码字典采用纯文本文件(。txt),该文件可以用edit 、notepad、ultraedit建立,每一个密码之间采用回车符和换行符间隔。程序穷举时会自动保存文件读写指针和当前穷举密码。

  穷举密码时程序会自动保存穷举进度,以便下次穷举时不用从头开始。通过修改配置文件setup.ini, 我们很容易实现控制穷举进度,也可以实现多人穷举时的任务分工。

  下面是一个穷举电子图板文件密码的setup.ini例子

  [设置]

  口令输入窗口标题=输入文件密码

  口令输入窗口确认按钮标题=确定

  提示口令错要求重新输入窗口标题=Eb

  提示口令错要求重新输入窗口确认按钮标题=确定

  穷举方法=大写字母

  线程计数=5

  穷举文件=C:/TOOLS/部门。txt

  [C:/TOOLS/部门。txt]

  文件指针=171

  当前穷举密码=AB

  [大写字母]

  当前密码=AC =〉将密码改成AAAA开始穷举四位密码,当然你也可以改为BAAA、CAAA、DAAA等等。

  程序对密码限制不超过 30 位。

  四、程序代码

  1.资源文件 GETCODE.RC

//Microsoft Developer Studio generated resource script.
//

#include "resource.h"

 

#define APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////

//

// Generated from the TEXTINCLUDE 2 resource.

//

#include "afxres.h"

 

/////////////////////////////////////////////////////////////////////////////

#undef APSTUDIO_READONLY_SYMBOLS

 

/////////////////////////////////////////////////////////////////////////////

// Chinese (P.R.C.) resources

 

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)

#ifdef _WIN32

LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#pragma code_page(936)

#endif //_WIN32

 

#ifdef APSTUDIO_INVOKED

/////////////////////////////////////////////////////////////////////////////

//

// TEXTINCLUDE

//

 

1 TEXTINCLUDE DISCARDABLE

BEGIN

    "resource.h/0"

END

 

2 TEXTINCLUDE DISCARDABLE

BEGIN

    "#include ""afxres.h""/r/n"

    "/0"

END

 

本篇文章来源于 黑客基地-全球最大的中文黑客站 原文链接:http://www.hackbase.com/tech/2009-03-23/51788_1.html

抱歉!评论已关闭.