Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF
Article

WiFi Password Recovery and Management Tool

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
27 Sep 2019CPOL6 min read 14.8K   20   1
In this article, I will discuss a WiFi password recovery and management tool that I created in WPF using Visual Studio 2019.

Table of Contents

Introduction

In this article, I will discuss a WiFi password recovery and management application that I created in WPF using Visual Studio 2019. For the creation of this application, I used MVVM architecture using Caliburn.Micro and dependency injection with Autofac. The main purpose of this application is to manage WiFi profiles, recover passwords from WiFi profiles and match different passwords in order to connect to your own secure WiFi (in case you want to connect a new device to your WiFi but don't remember your own WiFi password and want to make an educated guess). The WiFi profile manager functionality allows you to view and manage all your WiFi profiles on your Windows 10 PC, such as deleting profiles, which can be useful because over time, if you connect to different WiFi networks, the WiFi profile list can increase and at times, you may wish to remove unnecessary WiFi profiles. The WiFi password recovery and management application is intended to recover your own WiFi password and is not intended to hack anyone's WiFi. Brute force hacking of secure WiFi networks takes a very different approach than the one used by this application as can be read here. The most recent code of this application can be found in my Github repository here, the ClickOnce deployment can be launched here.

Background

The WiFi password recovery and management tool uses the ManagedNativeWifi library to retrieve WiFi profiles and their corresponding passwords from your Windows 10 PC. Furthermore, this library is also used to manipulate the WiFi profiles present on your PC such as increasing or decreasing their priorities. For the WiFi connection functionality, the application generates WiFi connection profiles using code from the simplewifi library. The application user can supply potential passwords in three different ways, manually by right clicking the network row in the home view, using dictionaries and regular expressions. Several print screens of the application are shown below. You can click the pictures to enlarge them.

Figure 1: Home view of WiFi password recovery and management tool.

Figure 2: Profile manager view.

Using the Code

The main user interface consists of four controls arranged in a DockPanel:

  • The left side control contains a navigation bar which allows you to navigate through all the available views.
  • The top side control contains a header panel.
  • The bottom control contains a status bar which displays application messages and busy status.
  • The middle control contains the view that is selected in the navigation bar.
XAML
<Window x:Class="EasyPasswordRecoveryWiFi.Views.ShellView">
    <DockPanel>
        <ContentControl DockPanel.Dock="Bottom" 
         cal:View.Model="{Binding StatusBarBottom}"></ContentControl>
        <ContentControl DockPanel.Dock="Top" 
         cal:View.Model="{Binding HeaderMenu}"></ContentControl>
        <ContentControl DockPanel.Dock="Left" 
         cal:View.Model="{Binding LeftMenu}"></ContentControl>
        <ContentControl x:Name="ActiveItem"></ContentControl>
    </DockPanel>
</Window>

Code snippet 1: The main user interface (ShellView.xaml) consists of four controls arranged in a DockPanel.

As mentioned above, the status bar control is docked on the bottom of the main user interface, when there are no messages and the application is not busy, the status bar control is hidden using data triggers. The status bar is updated by sending messages to it from different viewmodels using Caliburn.Micro Event Aggregator. For those unfamiliar, an Event Aggregator is a service that provides the ability to publish an object from one entity to another in a loosely based fashion as can be read here.

To reduce the applications dependencies, I used dependency injection with Autofac. One major advantage of using dependency injection is that it increased the applications testability by allowing to inject a mocked implementation of the IWiFiService interface into the MainController. During startup of the application, I can easily switch between the mocked and the actual implementation of the IWiFiService interface as shown in the code snippet below:

C#
#if MOCK_DATA
    builder.RegisterType<MockWiFiAdapter>().As<IWiFiService>();
#else
    builder.RegisterType<NativeWiFiAdapter>().As<IWiFiService>();
#endif

//Constructor Dependency Injection
public class MainController
{
    private readonly IWiFiService _wiFiService;
    public MainController(IWiFiService wifiService)
    {
        _wiFiService = wifiService;
    }
}

Code snippet 2: Switch between the mocked and the actual implementation of the IWiFiService interface.

The coupling between the application and the ManagedNativeWifi library is accomplished using the adapter pattern. This approach allows to easily replace the used ManagedNativeWifi library with other alternatives.

C#
public class NativeWiFiAdapter : NativeWifiPlayer, IWiFiService { }

Code snippet 3: NativeWiFiAdapter inherits from NativeWifiPlayer (ManagedNativeWifi) and implements the IWiFiService interface.

For the handling of async tasks, I used a fire and forget approach that can be found here. This approach basically wraps tasks into a try catch block. If an error occurs, it sends the exception to an error handler, in my case the error handler updates the status bar with the new error message. When a task is active, the busy flag is true, resulting in disabling of the main user interface and thereby prohibiting the start of new tasks.

C#
public static class TaskUtilities
{
#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
    public static async void FireAndForgetSafeAsync(this Task task, IErrorHandler handler = null)
#pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void
    {
        try
        {
            await task;
        }
        catch (Exception ex)
        {
            handler?.HandleError(ex);
        }
    }
}

Code snippet 4: Task extension method used for fire and forget approach.

C#
public interface IErrorHandler
{
    void HandleError(Exception ex);
}

Code snippet 5: IErrorHandler interface used by task extension method.

As mentioned earlier, the WiFi connection functionality generates WiFi connection profiles using the ProfileFactory class. The ProfileFactory class contains the CreateProfileXml method which can be called with an Accesspoint object (containing the authentication type, encryption type and Bss type of the targeted WiFi network) and a password as input arguments, after which it returns the connection profile. The resulting connection profile is then added to the PCs list of connection profiles, after which a connection is attempted to the targeted WiFi network. Prior to calling the CreateProfileXml method, the password is validated using the PasswordHelper class. Enterprise networks are not supported because they need an additional configuration step of the username and domain as can be read here.

Popup windows are displayed using the WindowManager class of Caliburn.Micro. Caliburn.Micro takes care of initializing the window, setting its data context and displaying the appropriate view.

C#
bool dialogResult = _windowManager.ShowDialog(_passwordViewModel) ?? false;
if (dialogResult)
{
    password = _passwordViewModel.Password;
}

Code snippet 6: Popup windows are displayed using the WindowManager class of Caliburn.Micro.

The WiFi manager view of the application allows you to import and manipulate all the WiFi connection profiles present on your Windows 10 PC. By right clicking on a profile row, you can manipulate the WiFi connection profile (remove, increase or decrease its priority, export the profile or show its properties). When you click on the properties option, the properties window pops up showing profile properties including the password used by the profile to connect to its corresponding WiFi network. In order to retrieve the unencrypted password, I modified the GetProfile method of the ManagedNativeWifi library.

C#
uint flags = WLAN_PROFILE_GET_PLAINTEXT_KEY;
var result = WlanGetProfile(
    clientHandle,
    interfaceId,
    profileName,
    IntPtr.Zero,
    out string profileXml,
    ref flags,
    out uint grantedAccess);

Code snippet 7: WlanGetProfile method retrieves a WiFi profile with its corresponding plain text password.

BAT
C:\Users>netsh wlan show profiles
C:\Users>netsh wlan show profile name="network-profile-name" key=clear
#Replace network-profile-name with your copied network name.

Code snippet 8: You can use the command prompt to display the plain text password of a WiFi profile.

As mentioned at the beginning of this article, the passwords used in the WiFi connection process can be provided using dictionaries and regular expressions. Details on how to generate password strings using regular expressions can be found in this article.

Last but not least, testing of the application was done using NUnit. I generated connection profiles for all the supported WiFi networks and validated them with reference profiles generated by a Windows 10 PC. Furthermore, I wrote test cases to validate the PasswordHelper class (which checks if a string meets the password rules for the targeted WiFi network).

Points of Interest

Inversion of control (IoC) can improve testability of your application, for example, it allows you to easy mock application functionality. The use of Caliburn.Micro framework can make programming easier, for example, the Event Aggregator allows you to easily interchange messages between different parts of your application.

History

  • 27th September, 2019: Version 1.0.0.0 - Published the article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Netherlands Netherlands
Mohamed Kalmoua is a Microsoft Certified Solutions Developer (MCSD) with over a decade of programming experience. He creates software for the Windows platform using C#, WPF, ASP.NET Core, SQL and C++. Mohamed also loves to build websites using Wordpress and Google analytics.

Comments and Discussions

 
Question"Recovery"... Pin
dandy7227-Sep-19 7:25
dandy7227-Sep-19 7:25 
If all one wants to do is retrieve the list of encrypted passwords you've used to connect to the various wifi endpoints you already have on your own machine, the following PowerShell script is all you need. I have no idea how well I can directly paste PS code into CP's editor, but hopefully a reader is going to be smart enough to figure it out. It's rather trivial if you have any PS knowledge.
$output = netsh wlan show profiles
$profiles = @()

foreach ( $line in $output )
{
    $index = $line.LastIndexOf( ": " )
    if ( $index -eq -1 ) { continue }
    $afterColon = $line.Substring( $index + 2 )
    if ( $afterColon.Trim() -ne "" ) { $profiles += $afterColon }
}

$profilesWithPasswords = @()
foreach ( $profile in $profiles | Sort-Object )
{
    $output = netsh wlan show profiles "$profile" key=clear
    $pwd = ""
    $authentication = ""

<pre>
foreach ( $line in $output )
{
    $index = $line.LastIndexOf( "Authentication" )
    if ( $index -ne -1 )
    {
        $index = $line.LastIndexOf( ": " )
        if ( $index -eq -1 ) { $authentication = "<unknown>" }
        else { $authentication = $line.Substring( $index + 2 ) }
        continue
    }

    $index = $line.LastIndexOf( "Key Content" )
    if ( $index -ne -1 )
    {
        $index = $line.LastIndexOf( ": " )
        if ( $index -eq -1 ) { continue }

        $pwd = $line.Substring( $index + 2 ).Trim()
        continue
    }
}
if ( $pwd -eq "" )

{
if ( $authentication -ne "Open" ) { $pwd = "<unknown>" }
#else { $pwd = "<unknown>" }
}
$profilesWithPasswords += New-Object -TypeName PSObject -Prop ( @{ 'Profile'=$profile; 'Password'=$pwd } )
}
Write-Host "Found $($profilesWithPasswords.Count) wifi profiles:"
$profilesWithPasswords | Format-Table Profile, Password

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.