docs update

This commit is contained in:
Eljakim Herrewijnen 2025-01-03 20:05:29 +01:00
parent 95d1701cad
commit 1734e6913b
6 changed files with 151 additions and 24 deletions

View File

@ -30,3 +30,16 @@ exclude_patterns = []
html_theme = 'sphinx_wagtail_theme'
html_static_path = ['_static']
project = "Gupje"
# These are options specifically for the Wagtail Theme.
html_theme_options = dict(
project_name = "Gupje",
# logo = "img/wagtail-logo-circle.svg",
logo_alt = "Gupje",
logo_height = 59,
logo_url = "/",
logo_width = 45,
)

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="311px" height="71px" viewBox="-0.5 -0.5 311 71" content="&lt;mxfile host=&quot;04n1rgtnob7ebrhhg57mh2mjuh68d4qe61ncs1a2e1n2no0ifp02&quot; modified=&quot;2025-01-03T17:59:05.657Z&quot; agent=&quot;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.96.0 Chrome/128.0.6613.186 Electron/32.2.6 Safari/537.36&quot; etag=&quot;KYStoJuZ4iEKOugXjLK8&quot; version=&quot;12.2.4&quot; pages=&quot;1&quot;&gt;&lt;diagram id=&quot;9qkYd14r56IiSAI5Sx1q&quot; name=&quot;Page-1&quot;&gt;tZQ9b4MwEEB/DVI6VAIcCBlbmqZLpwydDT7AqsHIOAH662vCEaCkUgc62X53549nyxYJ8+aoaJm9SwbCcm3WWOTFcl2HEM80HWl74nl+D1LFGSaN4MS/AKGN9MwZVLNELaXQvJzDWBYFxHrGqFKynqclUsxXLWkKC3CKqVjSD8501tPAs0f+BjzNhpUdGyM5HZIRVBllsp4gcrBIqKTUfS9vQhCdvMFLX/f6S/S2MQWF/kuB2xdcqDjj2XBfuh0Oq+S5YNDl2xZ5rjOu4VTSuIvW5noNy3QuzMgx3YQLEUoh1bWWJAn4cWx4pZX8hEmE7faR3U243DEe4gJKQzNBeIIjyBy0ak0KRn2Uia8pwGE9Xg0ZXk42uZYdMoqvIb1NPAozHXR23x9Z+HtiTEFVGbjZPgamiVoN1cOqWpkHAdve0xq4EfH9dbTufmj1Flqd/R2t/gpavYXWynwCm+0/6aQQJHdfqR8HECXr6PyDz5V0muH4g1xjk3+YHL4B&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="0" y="0" width="310" height="70" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"/><rect x="10" y="5" width="190" height="60" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g transform="translate(52.5,28.5)"><foreignObject style="overflow:visible;" pointer-events="all" width="105" height="12"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 106px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;">Address (4-8 bytes)</div></div></foreignObject></g><rect x="210" y="5" width="90" height="60" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/><g transform="translate(220.5,28.5)"><foreignObject style="overflow:visible;" pointer-events="all" width="68" height="12"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 69px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;">size(4 bytes)</div></div></foreignObject></g></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,6 +1,7 @@
==============
Gupje overview
==============
Gupje is a stub debugger that is designed to be minimal and requires 3 pages(0x1000) of data to be loaded on a target system.
A high level overview of how the memory mapping for this works is shown below.
@ -12,6 +13,7 @@ A high level overview of how the memory mapping for this works is shown below.
Inner workings
--------------
When the debugger is entered it will save the state of the processor into it's storage page. This is done in assembly and is architecture dependent.
When a new architecture is added the first thing that needs to be done is to implement these functionalities.
@ -24,29 +26,33 @@ When the user is done it will send the ``REST`` command to the debugger. This wi
The debugger also uses a stack that is defined in the debugger. This is done to not taint the original stack of the processor.
API
===
---
The debugger has a simple API that is used to communicate with the device. The following commands are supported:
Memory Read(Peek)
-----------------
=================
Read an address in memory to the host.
The packet that is send is always 12 bytes, but depending on the address space(32/64 bit) the address will be 4 or 8 bytes. The last 4 bytes are the amount of bytes to read.
.. figure:: images/peek_cmd.drawio.svg
Memory Write(Poke)
------------------
==================
Write a buffer to an address in memory.
Get debugger main(SELF)
-----------------------
=======================
Get the main entry point of the debugger. This is used to jump to only the debugger without setting up the stack and registers again.
This taints the device but is very usefull for debugging and showing where the debugger is in memory.
Concrete main(MAIN)
-------------------
===================
Execute the ``concrete_main`` function. This function is fully user customizable and can be used to implement custom functionality.
Like executing boot stages in a Qualcomm bootROM.
Flush Cache(FLSH)
-----------------
=================
Execute architecture specific instructions in order to flush the cache.
.. note::
@ -54,19 +60,19 @@ Execute architecture specific instructions in order to flush the cache.
Only arm64 is supported for this function. The support is there for arm and thumb but not implemented. Probably there will be a change here to support data/code cache flushes.
Jump to address(JUMP)
---------------------
=====================
Simple function that will just jump to an address without restoring the stack or registers. Usefull for testing the debugger or relocating it.
Dump special registers(SPEC)
----------------------------
============================
Will dump the special registers to the host. This is architecture dependent and will only dump the registers that are supported. For ARM64 checks for each exception level are implemented.
Synchronize State(SYNC)
-----------------------
=======================
This function can be called from the debugger and will force write the registers from the saved state into the processor.
Sync Special(SYNS)
------------------
==================
Will force write the special registers from the saved state into the processor. Usefull for overwriting the ``VBAR_ELN`` and others.
Not all registers are supported here and in the future this will probably be changed to be code pushed from the host to save space and support all special registers.
@ -75,7 +81,7 @@ Not all registers are supported here and in the future this will probably be cha
This function is currently only properly implemented for ARM64.
Restore and Jump(REST)
----------------------
======================
This function restores the state of the processor from it's internal storage and jump's to the address defined in ``DEBUGGER_JUMP``.
.. caution::
@ -83,18 +89,12 @@ This function restores the state of the processor from it's internal storage and
Currently there is always a register corrupted on the jump function(we need to branch to a register). I have not found a good method to mitigate this(maybe ldr pc in arm/thumb but wont work for arm64)
Restore and Return(RRET)
------------------------
========================
Does the same as ``Restore and Jump`` but instead of jumping it returns to the address that called the debugger(LR).
Glitching
=========
---------
A debug flag is added for adding glitching to the debugger. The command ``GLIT`` will jump to the glitch function but this is not very well implemented yet.
The goal is to add several testable glitch cases to the debugger for profiling.
Features
========
In a typical usecase the flow of the debugger would look as something like this:

View File

@ -1,5 +1,36 @@
====================
Nvidia Shield Tablet
====================
Exploitation for this target was done by `LordRafa <https://github.com/LordRafa>`_
The nvidia shield tablet, both st8 and k1 versions, have a Tegra K1 processor. This processor is based on the ARM Cortex A15 architecture.
This processor has the same bootROM bug as the Nintendo Switch, allowing us to run code very early on in the processor.
Code
----
The github code for this `is here <https://github.com/EljakimHerrewijnen/nvidia_shield>`_.
Debugger implementation
-----------------------
The bootROM has 2 endpoint functions to read/write data over USB, which we can repurpose to send and receive data from the debugger.
.. code-block:: c
#define BOOTROM_EP1_IN_WRITE_IMM 0x001065C0
#define BOOTROM_EP1_OUT_READ_IMM 0x00106612
typedef void (*ep1_x_imm_t)(void *buffer, uint32_t size, uint32_t *num_xfer);
ep1_x_imm_t usb_recv = (ep1_x_imm_t) ( BOOTROM_EP1_OUT_READ_IMM | 1 );
ep1_x_imm_t usb_send = (ep1_x_imm_t) ( BOOTROM_EP1_IN_WRITE_IMM | 1 );
void send(void *buffer, uint32_t size, uint32_t *num_xfer){
usb_send(buffer, size, num_xfer);
}
int recv(void *buffer, uint32_t size, uint32_t *num_xfer){
usb_recv(buffer, size, num_xfer);
return (int)&num_xfer;
}
See the code `here <https://github.com/EljakimHerrewijnen/nvidia_shield>`_

View File

@ -40,9 +40,10 @@ From ``devices/rpi4`` navigate to ``rpi4-baremetal-uart/`` folder and run make w
ARCH=arm64 PREFIX=aarch64-linux-gnu- make
Implementation
==============
Only send and receive need to be implemented for this target. For this target UART is used.
Debugger Implementation
-----------------------
Only send and receive need to be implemented. For this target UART is used.
The debugger expects send/recv to be handled by the user so we need to build some logic to know that the data has been send and that the amount of expected data has been received. The following C code implements the send/recv for UART.

View File

@ -1,5 +1,86 @@
==========
Samsung S7
==========
This is an example for a bit more complex target for running gupje.
The Samsung S7 contains a bootROM vulnerability, discovered and exploited by Frederick in `his blog <https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html>`_.
The USB implementation for this target is more complex because it does not expose any *easy* send/receive functions. Instead the DWC3 event handling is processed in the bootROM.
This means that we need to properly implement the event handling of the DWC3 controller in order to send and receive data, load the next stage and execute it. All in 502 bytes.
USB implementation
------------------
Here is a quick run down of how the USB controller works for this device:
When ``receiving`` data, the usb controller needs to be setup to know what to do when data is received. A callback needs to be setup and the amount of data expected to come in.
After that the transaction will be started, and a loop needs to be run in order to handle all the USB device events. One of the events that should happen at some point is the data receive function.
The previously setup callback function should then process all the incomming data and raise some sort of signal that it is happy or done in order to stop the USB handling loop.
So a simple call to receive data will look like this:
.. code-block:: c
#define recv_buffer 0x206fe00 //0x02021800 + 0x3000
#define data_received 0x206fd00
void recv_data_cb(uint32_t endpoint, uint32_t len){
char *dest_buf = (char *)recv_buffer;
volatile void *dref = (void *)data_received;
void *rbuf = get_endpoint_recv_buffer(endpoint);
for(int i= 0; i < len; i++){
dest_buf[i] = *(char *)(void *)((int)rbuf + i);
}
*(uint8_t *)dref = 1; // Mark as ready
}
void recv_data(uint32_t address, uint32_t size){
//
volatile void *dref = (void *)data_received;
*(uint8_t *)dref = 0;
maybe_usb_setup_read(2, recv_data_cb, 0x200);
uint32_t rbuf = get_endpoint_recv_buffer(2);
dwc3_ep0_start_trans(2, rbuf, 0x200);
while(1){
usb_event_handler();
volatile val = *(volatile uint8_t *)dref;
if(val == 1){
break;
}
}
// Copy to destination location
char *dest_buf = (char *)address;
for(int i= 0; i < size; i++){
dest_buf[i] = *(char *)(void *)((int)recv_buffer + i);
}
}
For ``sending`` the data the setup is somewhat the same. The data needs to be sent to the USB controller and a callback needs to be setup to know when the data has been sent.
.. code-block:: c
void send_data_cb(uint32_t endpoint, uint32_t len){
// Tell event handler that the data was received
volatile void *dref = (void *)data_received;
*(uint8_t *)dref = 1; // Mark as ready
}
void send(void *address, uint32_t size, uint32_t *error){
volatile void *dref = (void *)data_received;
*(uint8_t *)dref = 0;
maybe_usb_setup_read(0x1, send_data_cb, size);
// uint32_t rbuf = get_endpoint_recv_buffer(1);
dwc3_ep0_start_trans(1, (uint64_t)address, size);
while(1){
usb_event_handler();
if(*(uint8_t *)dref == 1){
break;
}
// exynos_sleep(1, 1000);
}
}
Stage 1
-------
Due to size constraints the first stage is a simple loader that will load the second stage from the host. The second stage being the actual debugger.
Exploitation done by Frederick in `his blog <https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html>`_.