commit e7bfcd1a78688519569496479b9d0fcfb56ed9d3 Author: PrincessPi3 Date: Fri Jun 28 02:55:43 2024 -0600 Initial, Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2064707 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode +.micropico +PiPicoPATLFGGGGG.code-workspace +main_dev.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb423a0 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# SillyFilly Pi Pico W Power Analysis Tool +# LFGGGGGGGGGGG +# FRONG +A tool for simplifying power analysis attacks against other gadgets. +Built on the Raspberry Pi Pico W, it runs a PWM channel to do manual clock control on the nugget you're hacking, and then reads an ADC channel to measure voltage used at each clock cycle. + +## Installataion +Make sure you have Micropython installed on your Pico W. +Open up your Pico W in your favorite IDE (Thonny and VS Code are commonly used) and upload main.py. +If you're having trouble, see the official [Raspberry Pi Pico W Getting Started Page](https://projects.raspberrypi.org/en/projects/get-started-pico-w) + +## Usage +1) Optional: edit the `power_analysis_pin` and `clock_pulse_pin` variables as you see fit +2) Edit the `ssid` and `password` variables with your wifi name (ssid) and the password +3) Set up the Pico W with pins on a breadboard, connect your power_analysis_pin and clock_pulse pin to the nugget (See Theory and Method) +3) Plug the Pico W to USB of your computer +4) Open the serial terminal of the Pico W +5) Reset the Pico W +6) When the Pico W connects, it will display a message like `Open web browser and navigate to http://x.x.x.x` Open a web browser on the same network and navigate to that address +7) Configure the options on the page to your liking (see below under Options) + + +## Options +### main.py +`power_analysis_pin` + This is the pin that is reading the power usage from the device + Defaults to GPIO 28 + +`clock_pulse_pin` + This pin provides the clock signal that you will be using to manually take control of the nuggets clock + Defaults to GPIO 20 + +`ssid` + This is your wifi network name. Only bgn supported (2.4GHz) + Dont forget the 's around the name + +`password` + Your wifi password + Dont forget the 's around the password + +### On the Webpage +`Duty Cycle` + The Percentage of the time that the clock is on vs off + 50% is by far the most common, it makes a normal square wave and is the default output + for most oscilators that you will be replacing + +`Frequency` + Frequency in Hertz (Hz) + How many times per second you want your nuggets clock to pulse + Dont make this too high + +`Samples Per Manual Clock Pulse` + This is how many times per clock pulse to sample the nuggets power usage + Dont make this too high + +## Hardware Needed +1) Raspberry Pi Pico W or Raspberry Pi Pico WH +2) Pins +3) Breadboard +4) Small capacitior (470uF is fine or anywhere within that range) +5) Wires +6) Optional: spikey probes + +## Theory +Power analysis attacks are very simple in concept. +The idea is monitoring the amount of power a device uses very carefully to get some data leaked. +Two things are most important during these attacks: +1) Getting the most accurate, raw read of the power usage at a high sample rate +2) Controlling the clock of the device to slow down the processor enough to get the readings + +To accomplish #1, we will get our power reading probe as close to the action of the processor as possible, while als adding a capacitor ground, which will pull any remaining juice out of the system. + +For #2, we need to remove the builtin crystal oscilator and replace that connection with our clock pulse probe. + +For this example, I will describe a firmware decryption power analysis attack. + +Scenario: You have and ESP32-S3 based device with flash and bootloader encryption set up on it. You Can remove the RF sheield and get a raw dump of the flash chip with a SOP8 test clip and a programmer, but the data is encrypted. \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..3130395 --- /dev/null +++ b/main.py @@ -0,0 +1,200 @@ +import network +import socket +import machine +import utime +import _thread +import math +import os + + +# gpio pin to read power analysis off of (int) +# reads 16bit (0-65535) between 0.0v and 3.3v +# (16 bit is phony, real read is 12 bit) +power_analysis_pin = 28 + +# output gpio pin generating clock pulse +clock_pulse_pin = 20 + +# Wi-Fi credentials +ssid = 'your-wifi-name-here' +password = 'yuour-wifi-password-here' + +# == Clock Pulse Genertor == # +# starts a pwm +# usage do_pwm(int gpio_pin, float duty_cycle, int hertz frequency, int samples_per_clock_pulse) +def do_pwm(outpin = 20, duty_cycle = 0.5, frequency = 2000): + print('Starting Clock Pulse PWM') + output_pin = machine.Pin(outpin) + pwm_pin = machine.PWM(output_pin) + pwm_pin.freq(frequency) + + pwm_duty = math.floor(duty_cycle*65535) + percent_duty = duty_cycle*100 + pwm_pin.duty_u16(pwm_duty) + print(f"Clock Pulses Running\n\tGPIO Pin: {outpin}\n\tDuty Cycle: {percent_duty}%\n\tFrequency: {frequency}Hz") + +def us_samples(frequency, samples_per_pulse): + return math.floor(1000000/(frequency*samples_per_pulse)) + +# == Read Value off of device == # +# uses ADC, 16bit output is phony, is actualloy 12bit. +# us of sleep calculate4d by floor(1000000 / (frequency_in_Hz * samples_per_pulse)) +def do_adc(adcpin=28, duty_cycle = 0.5, frequency = 2000, samples_per_pulse = 100): + print('Starting voltage reading ADC') + semaphore_thread_adc = _thread.allocate_lock() + analog_value = machine.ADC(adcpin) + us_sample = us_samples(frequency, samples_per_pulse) + while True: + semaphore_thread_adc.acquire() + reading = analog_value.read_u16() + print(reading) + utime.sleep_us(us_sample) + semaphore_thread_adc.release() + +# == HTTP Stuff == # +# HTML template for the webpage +def default_webpage(): + template = f""" + + + + Power Analysis Tool LFGGGGg + + + +

SillyFilly Pi Pico Power Analysis Tool (LFFGGGGG)

+
+ Clock Control Config
+ 50% Duty Cycle (% of time clock is on)

+ Frequency (Hz aka number of pulses per second)

+ Samplerate Config:
+ Samples Per Manual Clock Pulse

+ +
+ + + + """ + return str(template) + +# us of sleep calculate4d by floor(1000000 / (frequency_in_Hz * samples_per_pulse)) +def running_webpage(outpin = 20, adcpin = 28, duty_cycle = 0.5, frequency = 2000, samples_per_pulse = 100): + dutyy = duty_cycle*100 + us_sample = us_samples(frequency, samples_per_pulse) + template = f""" + + + + RUNNING - Power Analysis Tool LFGGGGg + + + +

SillyFilly Pi Pico Power Analysis Tool (LFFGGGGG)

+

WE RUNNING NOWWWWW LFGGGGGGGGGGGGG

+

+ Clock Pulse Pin: {outpin}
+ Power Analysis Pin: {adcpin}

+ + Frequency: {frequency}Hz (Duty Cycle: {dutyy}%)
+ Samples Per Pulse: {samples_per_pulse}
+ Delay Between Samples: {us_sample}us

+ LFGGGGGGGG FRONG +

+ + + """ + return str(template) + +print("Board Started or Reset\n===========\n\n") + +# Connect to WLAN +wlan = network.WLAN(network.STA_IF) +wlan.active(True) +wlan.connect(ssid, password) + +# Wait for Wi-Fi connection +connection_timeout = 10 +while connection_timeout > 0: + if wlan.status() >= 3: + break + connection_timeout -= 1 + print('Waiting for Wi-Fi connection...') + utime.sleep(1) + +# Check if connection is successful +if wlan.status() != 3: + raise RuntimeError('Failed to establish a network connection') +else: + print('Connection successful!') + network_info = wlan.ifconfig() + #print('IP address:', network_info[0]) + print(f"\n\nOpen web browser and navigate to http://{network_info[0]}\n\n") + +# Set up socket and start listening +addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] +s = socket.socket() +s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +s.bind(addr) +s.listen() + +# I like to have the LED on :3 +led = machine.Pin('LED', machine.Pin.OUT) +led.value(1) + +while True: + try: + conn, addr = s.accept() + request = conn.recv(1024) + request = str(request) + + try: + fullreq = request.split() + request = fullreq[1] + except IndexError: + pass + + # Process the action page! + if request.startswith('/action?', 0): + # parse the request path to extract da GET vars + req_string = request.split('?')[1] + req_vars = req_string.split('&') + for f in req_vars: + req_split = f.split('=') + # Type strict whitelist for safety and compatibility + if req_split[0] == 'duty_cycle': + duty_cycle_in = float(req_split[1]) + elif req_split[0] == 'freq': + freq_in = int(req_split[1]) + elif req_split[0] == 'samples_per_pulse': + samples_per_pulse_in = int(req_split[1]) + print(f"power_analysis_pin: {power_analysis_pin}\nduty_cycle_in: {duty_cycle_in}\nfreq_in: {freq_in}\nsamples_per_pulse_in:{samples_per_pulse_in}") + + # run the clock pulse (PWM) task + do_pwm(clock_pulse_pin, duty_cycle_in, freq_in) + + # fork the voltage measuring ADC reading to the second core + _thread.start_new_thread(do_adc, (power_analysis_pin, duty_cycle_in, freq_in, samples_per_pulse_in)) + + response = running_webpage() + + else: + response = default_webpage() + + # Send the HTTP response and close the connection + conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') + conn.send(response) + conn.close() + + except OSError as e: + conn.close() + print('Connection closed') \ No newline at end of file