FWCracker


…and why I decided to build it.

The idea behind FWCracker (Firmware Cracker) came to me very simply and more importantly in a very natural and practical way. I had a problem which drove me to find a solution. The solution drove me further, drawing more than pushing me to find a way on how to implement it. As it turned out, my problem was indeed an issue with my firmware although it wasn’t specifically what I had deduced earlier. After already developing my initial solution and failing to solve the problem, that’s when I took a step back, broke the problem down into simple terms, and from there, it became very clear as to what the real problem was. It was an issue with the firmware on my device, but where I had originally thought my password was incorrect, what actually happened was that the firmware was compromised. The real solution ultimately required more hardware implementation or technical documentation, and either route would have sufficed. I haven’t been able to locate the literature I’m searching for just yet and the device contained nothing worth more than the price of the tool-which was easily located-needed to remedy the problem.

What I learned was that FWCracker would make a great beginners project for someone in the beginning stages of learning Python since integrating with hardware was in part and required for this solution to work. Such is something Python was once heralded for, its ability to interface with all kinds of computer hardware is well known. So, FWCracker could be set aside as a learning device. Very introductory and simple to grasp so that one can get a taste of its power.

That’s the long and short of it, if you were wondering. I learned a lot about Python along the way and it was nice refresher and reminder of days long gone when I was once a Computer Science student.

Stay tuned for more books from me and really, you should donate if you like what you’re getting.

#   FWCracker_v3...

import os
import subprocess
import sys
import time
import serial

basedir = os.path.dirname(__file__)


#   Necessary components...


from pathlib import Path

from PyQt6.QtCore import QObject, QRunnable, QSize, Qt, QThreadPool
from PyQt6.QtGui import QIcon, QPixmap
from PyQt6.QtWidgets import (QApplication, QCheckBox, QLabel, QLineEdit,
                             QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget)

global newData
global some_word
global global_number_pattern
global hid_port
global control

some_word               = "NIL"
global_number_pattern   = "NIL"
hid_port                = "NIL"
control                 = "UNSET" 

#   From here we'll attempt to integrate a workable GUI for this app

app = QApplication([])

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
    
        logo = QLabel("FWCracker")
        logo.setPixmap(QPixmap(os.path.join(basedir, "logo.png")))
        logo.setAlignment(Qt.AlignmentFlag.AlignCenter)

        PhraseLabel = QLabel("Word/Phrase")
        NumberLabel = QLabel("Number")
        SerialLabel = QLabel("Serial Port")

        word = QLineEdit()
        pattern = QLineEdit()
        port = QLineEdit()

      
        word.setPlaceholderText("Enter the word or phrase here...")
        word.textChanged.connect(self.decouple_word)

        pattern.setPlaceholderText("Enter the number portion here...")
        pattern.textChanged.connect(self.decouple_pattern)

        port.setPlaceholderText("Enter your serial port here. Should be something like COM# or /dev/tty/USB##...")
        port.textChanged.connect(self.decouple_port)
        
        control = QCheckBox("Bios needs confirmation?")
        control.stateChanged.connect(self.decouple_control)

        launchButton = QPushButton("Launch")
        launchButton.setFixedHeight(30)
        launchButton.clicked.connect(self.starter)

        global pauseButton

        pauseButton = QPushButton()
        pauseButton.setIcon(QIcon(os.path.join(basedir, "pauseBtn.png")))
        pauseButton.setFixedSize(30, 30)

        global Output

        Output = QLabel("Output will be displayed here...")
        Output.setObjectName("nfo")
        Output.setFixedHeight(250)
        Output.setAlignment(Qt.AlignmentFlag.AlignCenter)
        Output.setWordWrap(True)
              
        findPorts = QPushButton("List Ports")
        findPorts.clicked.connect(find_ports)

        layout = QVBoxLayout()
        app_controls = QHBoxLayout()

        layout.addWidget(logo)
        layout.addWidget(PhraseLabel)
        layout.addWidget(word)
        layout.addWidget(NumberLabel)
        layout.addWidget(pattern)
        layout.addWidget(control)
        layout.addWidget(findPorts)
        layout.addWidget(SerialLabel)
        layout.addWidget(port)

        layout.addLayout(app_controls)

        app_controls.addWidget(launchButton)
        app_controls.addWidget(pauseButton)

        layout.addWidget(Output)

        app_container = QWidget()
        app_container.setLayout(layout)


#   self.setFixedSize(QSize(###, 400))

        self.setCentralWidget(app_container)


#   That's it, thanks to py(Qt)


#   Some helpers...

    def decouple_word(self, text):
        global some_word
        some_word = text
        Output.setText("Word portion is set to: " + some_word)
        if text == "":
            Output.setText("Output will be displayed here...")
    

    def decouple_pattern(self, text):
        global global_number_pattern
        if text.isdecimal() == False:
            newData = "Need a number here..."
            Output.setText(newData)
        else:
            global_number_pattern = text
            Output.setText("Number poriton is set to: " + global_number_pattern)
        if text == "":
            Output.setText("Output will be displayed here...")
    

    def decouple_control(self, state):
        if state > 0:
            control = "SET"
            newData = "Bios Confirmation: " + control
            Output.setText(newData)
        else:
            control = "UNSET"
            newData = "Bios Confirmation: " + control
            Output.setText(newData)
            

    def decouple_port(self, text):
        global hid_port
        hid_port = text
        newData = "Your serial port is set: " + text
        Output.setText(newData)


    def starter(self):
        if hid_port != "NIL" and some_word != "NIL" and global_number_pattern != "NIL":
            QThreadPool.globalInstance().start(self.build_range)
        else:
            Output.setText("Please fill out the form...")


#   FWCracker, modified

    def build_range(self):
        newData = "\nWelcome " + os.name + " user..."
        Output.setText(newData)
        time.sleep(3)
        known_factor = int(global_number_pattern) / 10
        
        if known_factor <= 1:
            o_range = 10
            newData = "\n10 different possibilities based on this info...\n"
            Output.setText(newData)
            
        else:
            if known_factor <= 10:
                o_range = 100
                newData = "\n100 different possiblities based on this info...\n"
                Output.setText(newData)
                
            else:
                if known_factor <= 100:
                    o_range = 1000
                    newData = "\n1,000 different possiblities based on this info...\n"
                    Output.setText(newData)
                    
                else:
                    if known_factor <= 1000:
                        o_range = 10000
                        newData = "\n10,000 different possiblities based on this info...\n"
                        Output.setText(newData)
                        
                    else:
                        if known_factor <= 10000:
                            o_range = 100000
                            newData = "\nBased on the number you provided this will take a very long time.\n"
                            Output.setText(newData)
                            
                        else:
                            if known_factor <= 100000:
                                o_range = 1000000
                                newData = "\nPractically impossible, theoretically....might as well continue..."
                                Output.setText(newData)
                                
                            else:
                                if known_factor <= 1000000:
                                    o_range = 1000000
                                    newData = "\nOkey dokey..."
                                    Output.setText(newData)
        global set_range
        set_range = o_range
        time.sleep(3)
        build_passcode()


def build_passcode():
    global n
    n = 1
    global to_bytes
    global passcode
    while n <= set_range:
        passcode = some_word + str(n) + "\n"
        to_bytes = passcode.encode(encoding='ascii')
        do_writer_do()
        n += 1

    newData = "Later..."
    Output.setText(newData)
    quit()

    
def do_writer_do():  
    time.sleep(1)
    space = "\n\n"

    ser = serial.Serial(hid_port)
    ser.baudrate = 9600

    space_to_bytes = space.encode(encoding='ascii')
    ser.write(to_bytes)
    
    newData = "This is attempt #" + str(n) + " of " + str(set_range) + ", using password: " + passcode
    Output.setText(newData)
    
    if str(control) == 'SET':
        ser.write(space_to_bytes)

    time.sleep(2)

    int(n)
    int(set_range)
    
    time.sleep(1)


#   Workers...


def installer():
        check_online = 'ping yahoo.com'
        online = subprocess.getstatusoutput(check_online)
        status = online[0]
        ping_output = online[1]
        newData = ping_output
        Output.setText(newData)

        if status > 0:
            install = 'pip install pyserial'
            newData = subprocess.getoutput(install)
            Output.setText(newData)
            time.sleep(2)
        else:
            newData = "FWCracker needs to be online only to get pyserial. Connect to the Internet and restart app.\n"
            Output.setText(newData)
            time.sleep(3)
            quit()      


def find_ports():
    pyserial_exists = 'pyserial-ports'
    check = subprocess.getstatusoutput(pyserial_exists)[0]
    if check > 0:
        installer()
    code = 'pyserial-ports -v'
    newData = subprocess.getoutput(code)
    Output.setText(newData) 


#   Launch pyQt app...

window = MainWindow()
window.show()

app.setStyleSheet(Path(os.path.join(basedir, 'FWCracker.qss')).read_text())
app.exec()


#   FWCracker_v3...

Get FWCracker here.