├── .gitignore ├── payload.ld ├── Makefile ├── LICENSE.md ├── hello-world.c ├── README.md └── stage2.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | *.bin 4 | *.swo 5 | *.h 6 | -------------------------------------------------------------------------------- /payload.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("binary"); 2 | 3 | ENTRY(_entry); 4 | 5 | _PAYLOAD_START_ = 0x0; 6 | 7 | SECTIONS 8 | { 9 | . = _PAYLOAD_START_; 10 | .start : { 11 | *(_start) 12 | } 13 | .text : { 14 | *(.text) 15 | } 16 | .ro : { 17 | *(.ro) 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for lv0ldr payloads 3 | # (c) 2022 - MikeM64 4 | # 5 | # 6 | 7 | # 8 | # Useful commands: 9 | # - spu-objdump -b binary -D -m spu .bin 10 | 11 | .PHONY: clean, all 12 | 13 | .SECONDARY: 14 | 15 | all: stage2.h hello-world.h 16 | 17 | %.bin: %.c 18 | spu-gcc -ffreestanding -fno-builtin -nostdlib -fpic $< -Os -e _entry -o $@ -T payload.ld 19 | 20 | %.h: %.bin 21 | xxd -i $< > $@ 22 | 23 | clean: 24 | rm *.bin *.o *.h || true 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | lv0ldr payloads - Copyright (C) 2022 MikeM64 2 | 3 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. 4 | 5 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 6 | 7 | You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. 8 | -------------------------------------------------------------------------------- /hello-world.c: -------------------------------------------------------------------------------- 1 | /** 2 | * lv0ldr-spi: Hello-World Test 3 | * 4 | * (c) MikeM64 - 2022 5 | */ 6 | 7 | /* 8 | * This will print out "Hello from lv0ldr!" on the SB UART. 9 | * It will ONLY work on 3.4.0 lv0ldrs (CECH-2500) as the address 10 | * to printf changes between versions. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | void (*sb_printf)(const char *fmt, ...); 20 | void _entry(void) __attribute__((section("_start"))); 21 | 22 | void 23 | _entry (void) 24 | { 25 | sb_printf = (void *)0x19bf8; 26 | sb_printf("Hello from lv0ldr!\n"); 27 | while(1) {} 28 | } 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lv0ldr-payloads 2 | (c) 2022 - MikeM64 3 | 4 | This is a collection of payloads to run with your lv0ldr hardware exploit. 5 | 6 | # Requirements 7 | - SPU GCC Toolchain 8 | 9 | # Writing a new payload 10 | 1) Use the following template for the .c file: 11 | 12 | ```c 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /* Forward declarations */ 20 | void _entry(void) __attribute__((section("_start"))); 21 | 22 | void 23 | _entry (void) 24 | { 25 | /* Your code goes here */ 26 | while (1) {} 27 | } 28 | 29 | ``` 30 | 31 | 2) Edit the makefile to add .h as a target 32 | 3) `make` 33 | 34 | # Limitations 35 | - Payloads start execution from 0x3e120 36 | - The maximum payload size at the moment is 0xEE0 in order to not clobber syscon message counters. 37 | 38 | # Payloads 39 | ## hello-world 40 | This prints a message to the SB UART port. It only works on 3.4.0 lv0ldrs due to the fixed offset of the printf function. 41 | 42 | ## stage2 43 | This dumps the entire contents of the isolated LS over SB UART. This should work on any lv0ldr as it does not rely on any pre-existing code. 44 | 45 | -------------------------------------------------------------------------------- /stage2.c: -------------------------------------------------------------------------------- 1 | /** 2 | * lv0ldr-spi: Stage 2 - Dumper Code 3 | * 4 | * (c) MikeM64 - 2022 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define SPU_DMA_TAG 0 14 | #define SPU_DMA_TAG_MASK (1 << SPU_DMA_TAG) 15 | 16 | /* Forward declarations */ 17 | void _entry(void) __attribute__((section("_start"))); 18 | 19 | /* 20 | * SPU DMA transfers must share the same low 4-bits on both 21 | * source and destination addresses otherwise an alignment 22 | * error will be raised. 23 | * 24 | * Safe addresses available are 0x3e000 to 0x3e120 before 25 | * overwriting the payload. 26 | */ 27 | static uint8_t *s_dma_buf; 28 | 29 | static inline void 30 | dma_transfer (volatile void *lsa, 31 | unsigned int eah, 32 | unsigned int eal, 33 | unsigned int size, 34 | unsigned int tag_id, 35 | unsigned int dma_cmd) 36 | { 37 | spu_mfcdma64(lsa, eah, eal, size, tag_id, dma_cmd); 38 | } 39 | 40 | /* TX space is available when this bit is set */ 41 | #define SB_STAT_FFF308_CAN_PUTC 0x100 42 | 43 | void 44 | sb_putc (char c) 45 | { 46 | uint32_t *dma_resp = (uint32_t *)&s_dma_buf[8]; 47 | 48 | /* Wait for space to put the character */ 49 | do { 50 | dma_transfer(dma_resp, 51 | 0x240, 52 | 0xfff308, 53 | sizeof(*dma_resp), 54 | SPU_DMA_TAG, 55 | MFC_GET_CMD); 56 | do { 57 | mfc_write_tag_mask(SPU_DMA_TAG_MASK); 58 | } while(mfc_read_tag_status_immediate() == 0); 59 | } while (!((*dma_resp) & SB_STAT_FFF308_CAN_PUTC)); 60 | 61 | dma_resp = (uint32_t *)&s_dma_buf[0xc]; 62 | *dma_resp = c; 63 | 64 | dma_transfer(dma_resp, 65 | 0x240, 66 | 0xfff31c, 67 | sizeof(*dma_resp), 68 | SPU_DMA_TAG, 69 | MFC_PUT_CMD); 70 | do { 71 | mfc_write_tag_mask(SPU_DMA_TAG_MASK); 72 | } while(mfc_read_tag_status_immediate() == 0); 73 | } 74 | 75 | void 76 | sb_printstr (const char *str) 77 | { 78 | while (*str) { 79 | sb_putc(*str); 80 | *str++; 81 | } 82 | } 83 | 84 | void 85 | _entry (void) 86 | { 87 | uint8_t *ls_addr = 0; 88 | s_dma_buf = (uint8_t *)0x3e000; 89 | 90 | sb_printstr("Starting dump:\n"); 91 | 92 | for (ls_addr = 0; ls_addr < (uint8_t *)0x40000; ls_addr++) { 93 | sb_putc(*ls_addr); 94 | } 95 | 96 | sb_printstr("\nComplete!\n"); 97 | 98 | while(1) {} 99 | } 100 | 101 | --------------------------------------------------------------------------------