├── .gitignore ├── README.md ├── images ├── image_0.75.png ├── setup.jpg └── tiled_small.jpg ├── src-experiment └── experiment.py ├── src-memcopy ├── Makefile ├── README.md ├── boot.S ├── io.c ├── io.h ├── kernel.c └── link.ld ├── src-module ├── Makefile ├── mona-big.tga ├── mona.tga └── ramrec.c └── src ├── Makefile ├── load.c ├── mona-big.tga └── mona.tga /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | load 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ramrecovery 2 | 3 | Simple demo illustrating remanence of data in RAM. See https://en.wikipedia.org/wiki/Cold_boot_attack and associated papers for much more information! In one of the original cold boot 4 | attack papers "Lest We Remember: Cold Boot Attacks on Encryption Keys" by Halderman et al. they loaded an image of the Mona Lisa and "cut power for varying lengths of time". 5 | 6 | * src - Program to fill memory with the Mona Lisa 7 | * src-experiment - Automated experiment, uses Raspberry Pi OS to inject single image into contiguous RAM, then uses bare metal kernel to dump memory. Makes use of modified USB hub that is controlled by relays, 8 | to automatically choose which USB disk to boot from. Uses Wifi plug running Tasmota turn on/off Pi. The experiment script controls the target Pi using SSH. 9 | * src-memcopy - Bare metal kernel to dump memory over Raspberry Pi UART interface. 10 | * src-module - Linux kernel module to inject image(s) into contiguous memory 11 | 12 | Also see my little article about this project in https://pagedout.institute/?page=issues.php Issue #3, page 14. 13 | 14 | The following image shows the setup for dumping memory from a Raspberry Pi using the aforementioned bare metal kernel. A USB hub was modified so that active devices could be chosen via relays (should switch to using MOSFETs sometime!), to choose between a USB disk running the bare metal kernel or Raspberry Pi OS. The memory could then be dumped, after varying delays from the Pi via UART at 1 MBaud to another Pi. 15 | 16 | ![Setup](images/setup.jpg) 17 | 18 | The program in src, loads many images of the Mona Lisa into RAM on the Pi, running Raspberry Pi OS Lite. 19 | 20 | ``` 21 | cd src 22 | make run 23 | ``` 24 | 25 | Then quickly turn off/on the power on the raspberry pi. 26 | 27 | Using the memory forensics kernel module LiME, we can see there is still some remanence of the Mona Lisa in memory: 28 | 29 | ``` 30 | git clone https://github.com/504ensicsLabs/LiME.git 31 | cd LiME/src/ 32 | make 33 | ``` 34 | 35 | ``` 36 | sudo insmod ./lime-$(uname -r).ko "path=out.dump format=padded" 37 | ``` 38 | 39 | Extract Mona Lisa images from memory dump: 40 | 41 | ``` 42 | LANG=C grep --text --byte-offset --only-matching --perl-regexp '\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x58\x02\x93\x01\x58\x02\x18\x20' out.dump | LANG=C sed "s/:.*//g" | xargs -I {} dd if=out.dump bs=1 skip={} count=725444 of={}.tga 43 | montage -border 0 -mode concatenate *.tga tiled.jpg 44 | convert -resize "3000>" tiled.jpg tiled_small.jpg 45 | ``` 46 | 47 | ## Kernel module for filling contiguous RAM with Mona Lisa 48 | 49 | The kernel module obtains contiguous RAM and fills with Mona Lisa. 50 | 51 | In /boot/cmdline.txt, added the following to allocate 700MB of contiguous kernel RAM: 52 | ``` 53 | cma=700M@36M 54 | ``` 55 | 56 | And the following (from https://forums.raspberrypi.com/viewtopic.php?p=2132596#p2132596) to /boot/config.txt (to maximise CMA): 57 | 58 | ``` 59 | [all] 60 | device_tree_address=0x2000000 61 | device_tree_end=0x20FFFFF 62 | ``` 63 | 64 | ``` 65 | cd src-module 66 | make 67 | ``` 68 | 69 | Use the following to fill contiguous RAM, with Mona. 70 | 71 | ``` 72 | sudo insmod ramrec.ko writetoram=true filename="mona.tga" singleimage=false 73 | ``` 74 | 75 | ## Image recovered after 0.75s delay 76 | 77 | The following image was extracted after a 0.75s delay, without cooling through the automated experiment (see src-experiment). I noticed the images decayed very quickly with no cooling, for example they appeared almost completely decayed around 1s. 78 | 79 | ![Recovered Image](images/image_0.75.png) 80 | -------------------------------------------------------------------------------- /images/image_0.75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anfractuosity/ramrecovery/eea1647b66d5fab3e560d531ef7cebcd585fb624/images/image_0.75.png -------------------------------------------------------------------------------- /images/setup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anfractuosity/ramrecovery/eea1647b66d5fab3e560d531ef7cebcd585fb624/images/setup.jpg -------------------------------------------------------------------------------- /images/tiled_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anfractuosity/ramrecovery/eea1647b66d5fab3e560d531ef7cebcd585fb624/images/tiled_small.jpg -------------------------------------------------------------------------------- /src-experiment/experiment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import time 5 | import json 6 | import serial 7 | import urllib.request 8 | import paramiko 9 | from paramiko import SSHConfig 10 | from gpiozero import DigitalOutputDevice 11 | 12 | """ 13 | sudo apt install python3-paramiko 14 | sudo apt install python3-serial 15 | """ 16 | 17 | pi_os_pin = DigitalOutputDevice(4) 18 | baremetal_kernel_pin = DigitalOutputDevice(3) 19 | 20 | target_pi = os.environ['TARGETPI'] 21 | tasmota_plug = os.environ['TASMOTAPLUG'] 22 | 23 | def runnew(host, user): 24 | ret = None 25 | ssh = paramiko.SSHClient() 26 | ssh.load_system_host_keys() 27 | ssh.connect(hostname=host, username=user) 28 | stdin, stdout, stderr = ssh.exec_command('cd /home/pi/ramrecovery/src-module; sudo insmod ramrec.ko writetoram=true filename="mona-big.tga" singleimage=true',get_pty=True) 29 | stdin, stdout, stderr = ssh.exec_command('dmesg | grep "Writing to address"',get_pty=True) 30 | for line in stdout.readlines(): 31 | print(line) 32 | if "Writing to" in line: 33 | print(line) 34 | ret = int(line[line.rfind(" "):].strip(), 16) 35 | ssh.close() 36 | return ret 37 | return None 38 | 39 | def power_change(on=False): 40 | on = "On" if on else "Off" 41 | url = f"{tasmota_plug}cm?cmnd=Power%20{on}" 42 | with urllib.request.urlopen(url) as response: 43 | res = json.loads(response.read())['POWER'] 44 | return res == "ON" 45 | 46 | def power_status(): 47 | url = tasmota_plug + "cm?cmnd=Status" 48 | with urllib.request.urlopen(url) as response: 49 | return json.loads(response.read())['Status']['Power'] == 1 50 | 51 | def choose_usb(pios=True): 52 | if pios: 53 | pi_os_pin.off() 54 | baremetal_kernel_pin.on() 55 | else: 56 | pi_os_pin.on() 57 | baremetal_kernel_pin.off() 58 | 59 | def read_serial(filename): 60 | leng = 0 61 | dat = open(filename, "wb") 62 | with serial.Serial('/dev/ttyUSB0', int(1e6), timeout=160) as ser: 63 | while True: 64 | s = ser.read(1024 * 1024) 65 | if s: 66 | dat.write(s) 67 | leng += len(s) 68 | dat.flush() 69 | if leng >= 70 * 1024 * 1024: 70 | break 71 | 72 | def write_image(serial_filename, image_filename, addr): 73 | mona = open("../src-module/mona-big.tga", "rb").read() 74 | mona_header = mona[0:18] 75 | dump = open(serial_filename, "rb").read() 76 | tga = open(image_filename, "wb") 77 | tga.write(mona_header) 78 | tga.write(dump[addr+18:addr+len(mona)]) 79 | tga.close() 80 | 81 | def main(): 82 | power_change(False) 83 | time.sleep(10) 84 | 85 | # See image after approx, X seconds 86 | for exp in [0.9]: 87 | # Choose raspberry pi OS 88 | choose_usb(True) 89 | # Turn power on 90 | power_change(True) 91 | # Wait for OS to startup 92 | time.sleep(2 * 60) 93 | # Fill memory with Mona Lisa 94 | addr = runnew(target_pi, "pi") 95 | print(addr) 96 | # Turn power off 97 | power_change(False) 98 | # Wait for particular duration in minutes 99 | time.sleep(exp) 100 | # Choose bare metal kernel 101 | choose_usb(False) 102 | # Turn power on 103 | power_change(True) 104 | # Read serial data from bare metal kernel 105 | read_serial(f"serial_{exp}.bin") 106 | # Turn power off 107 | power_change(False) 108 | # Write image 109 | write_image(f"serial_{exp}.bin", f"image_{exp}.tga", addr) 110 | # Wait 20 mins 111 | time.sleep(20 * 60) 112 | 113 | if __name__ == "__main__": 114 | main() 115 | -------------------------------------------------------------------------------- /src-memcopy/Makefile: -------------------------------------------------------------------------------- 1 | CFILES = $(wildcard *.c) 2 | OFILES = $(CFILES:.c=.o) 3 | GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles 4 | GCCPATH = /usr/bin 5 | 6 | all: clean kernel8.img 7 | 8 | boot.o: boot.S 9 | $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o 10 | 11 | %.o: %.c 12 | $(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@ 13 | 14 | kernel8.img: boot.o $(OFILES) 15 | $(GCCPATH)/aarch64-none-elf-ld -nostdlib boot.o $(OFILES) -T link.ld -o kernel8.elf 16 | $(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img 17 | 18 | clean: 19 | /bin/rm kernel8.elf *.o *.img > /dev/null 2> /dev/null || true 20 | -------------------------------------------------------------------------------- /src-memcopy/README.md: -------------------------------------------------------------------------------- 1 | # memcopy 2 | 3 | Very simple Raspberry Pi kernel, to dump memory over UART interface at 1e6 baud. 4 | 5 | Uses code extensively from https://github.com/isometimes/rpi4-osdev, for kernel setup and functions such 6 | as UART control etc. 7 | -------------------------------------------------------------------------------- /src-memcopy/boot.S: -------------------------------------------------------------------------------- 1 | .section ".text.boot" // Make sure the linker puts this at the start of the kernel image 2 | 3 | .global _start // Execution starts here 4 | 5 | _start: 6 | // Check processor ID is zero (executing on main core), else hang 7 | mrs x1, mpidr_el1 8 | and x1, x1, #3 9 | cbz x1, 2f 10 | // We're not on the main core, so hang in an infinite wait loop 11 | 1: wfe 12 | b 1b 13 | 2: // We're on the main core! 14 | 15 | // Set stack to start below our code 16 | ldr x1, =_start 17 | mov sp, x1 18 | 19 | // Clean the BSS section 20 | ldr x1, =__bss_start // Start address 21 | ldr w2, =__bss_size // Size of the section 22 | 3: cbz w2, 4f // Quit loop if zero 23 | str xzr, [x1], #8 24 | sub w2, w2, #1 25 | cbnz w2, 3b // Loop if non-zero 26 | 27 | // Jump to our main() routine in C (make sure it doesn't return) 28 | 4: bl main 29 | // In case it does return, halt the master core too 30 | b 1b 31 | -------------------------------------------------------------------------------- /src-memcopy/io.c: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | // GPIO 4 | 5 | enum { 6 | GPFSEL0 = PERIPHERAL_BASE + 0x200000, 7 | GPSET0 = PERIPHERAL_BASE + 0x20001C, 8 | GPCLR0 = PERIPHERAL_BASE + 0x200028, 9 | GPPUPPDN0 = PERIPHERAL_BASE + 0x2000E4 10 | }; 11 | 12 | enum { 13 | GPIO_MAX_PIN = 53, 14 | GPIO_FUNCTION_OUT = 1, 15 | GPIO_FUNCTION_ALT5 = 2, 16 | GPIO_FUNCTION_ALT3 = 7 17 | }; 18 | 19 | enum { 20 | Pull_None = 0, 21 | Pull_Down = 1, // Are down and up the right way around? 22 | Pull_Up = 2 23 | }; 24 | 25 | void mmio_write(long reg, unsigned int val) { *(volatile unsigned int *)reg = val; } 26 | unsigned int mmio_read(long reg) { return *(volatile unsigned int *)reg; } 27 | 28 | unsigned int gpio_call(unsigned int pin_number, unsigned int value, unsigned int base, unsigned int field_size, unsigned int field_max) { 29 | unsigned int field_mask = (1 << field_size) - 1; 30 | 31 | if (pin_number > field_max) return 0; 32 | if (value > field_mask) return 0; 33 | 34 | unsigned int num_fields = 32 / field_size; 35 | unsigned int reg = base + ((pin_number / num_fields) * 4); 36 | unsigned int shift = (pin_number % num_fields) * field_size; 37 | 38 | unsigned int curval = mmio_read(reg); 39 | curval &= ~(field_mask << shift); 40 | curval |= value << shift; 41 | mmio_write(reg, curval); 42 | 43 | return 1; 44 | } 45 | 46 | unsigned int gpio_set (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPSET0, 1, GPIO_MAX_PIN); } 47 | unsigned int gpio_clear (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPCLR0, 1, GPIO_MAX_PIN); } 48 | unsigned int gpio_pull (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPPUPPDN0, 2, GPIO_MAX_PIN); } 49 | unsigned int gpio_function(unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPFSEL0, 3, GPIO_MAX_PIN); } 50 | 51 | void gpio_useAsAlt3(unsigned int pin_number) { 52 | gpio_pull(pin_number, Pull_None); 53 | gpio_function(pin_number, GPIO_FUNCTION_ALT3); 54 | } 55 | 56 | void gpio_useAsAlt5(unsigned int pin_number) { 57 | gpio_pull(pin_number, Pull_None); 58 | gpio_function(pin_number, GPIO_FUNCTION_ALT5); 59 | } 60 | 61 | void gpio_initOutputPinWithPullNone(unsigned int pin_number) { 62 | gpio_pull(pin_number, Pull_None); 63 | gpio_function(pin_number, GPIO_FUNCTION_OUT); 64 | } 65 | 66 | void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff) { 67 | if (onOrOff) { 68 | gpio_set(pin_number, 1); 69 | } else { 70 | gpio_clear(pin_number, 1); 71 | } 72 | } 73 | 74 | // UART 75 | 76 | enum { 77 | AUX_BASE = PERIPHERAL_BASE + 0x215000, 78 | AUX_IRQ = AUX_BASE, 79 | AUX_ENABLES = AUX_BASE + 4, 80 | AUX_MU_IO_REG = AUX_BASE + 64, 81 | AUX_MU_IER_REG = AUX_BASE + 68, 82 | AUX_MU_IIR_REG = AUX_BASE + 72, 83 | AUX_MU_LCR_REG = AUX_BASE + 76, 84 | AUX_MU_MCR_REG = AUX_BASE + 80, 85 | AUX_MU_LSR_REG = AUX_BASE + 84, 86 | AUX_MU_MSR_REG = AUX_BASE + 88, 87 | AUX_MU_SCRATCH = AUX_BASE + 92, 88 | AUX_MU_CNTL_REG = AUX_BASE + 96, 89 | AUX_MU_STAT_REG = AUX_BASE + 100, 90 | AUX_MU_BAUD_REG = AUX_BASE + 104, 91 | AUX_UART_CLOCK = 500000000, 92 | UART_MAX_QUEUE = 16 * 1024 93 | }; 94 | 95 | #define AUX_MU_BAUD(baud) ((AUX_UART_CLOCK/(baud*8))-1) 96 | 97 | unsigned char uart_output_queue[UART_MAX_QUEUE]; 98 | unsigned int uart_output_queue_write = 0; 99 | unsigned int uart_output_queue_read = 0; 100 | 101 | void uart_init() { 102 | mmio_write(AUX_ENABLES, 1); //enable UART1 103 | mmio_write(AUX_MU_IER_REG, 0); 104 | mmio_write(AUX_MU_CNTL_REG, 0); 105 | mmio_write(AUX_MU_LCR_REG, 3); //8 bits 106 | mmio_write(AUX_MU_MCR_REG, 0); 107 | mmio_write(AUX_MU_IER_REG, 0); 108 | mmio_write(AUX_MU_IIR_REG, 0xC6); //disable interrupts 109 | mmio_write(AUX_MU_BAUD_REG, AUX_MU_BAUD(1000000)); 110 | gpio_useAsAlt5(14); 111 | gpio_useAsAlt5(15); 112 | mmio_write(AUX_MU_CNTL_REG, 3); //enable RX/TX 113 | } 114 | 115 | unsigned int uart_isOutputQueueEmpty() { 116 | return uart_output_queue_read == uart_output_queue_write; 117 | } 118 | 119 | unsigned int uart_isReadByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x01; } 120 | unsigned int uart_isWriteByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x20; } 121 | 122 | unsigned char uart_readByte() { 123 | while (!uart_isReadByteReady()); 124 | return (unsigned char)mmio_read(AUX_MU_IO_REG); 125 | } 126 | 127 | void uart_writeByteBlockingActual(unsigned char ch) { 128 | while (!uart_isWriteByteReady()); 129 | mmio_write(AUX_MU_IO_REG, (unsigned int)ch); 130 | } 131 | 132 | void uart_loadOutputFifo() { 133 | while (!uart_isOutputQueueEmpty() && uart_isWriteByteReady()) { 134 | uart_writeByteBlockingActual(uart_output_queue[uart_output_queue_read]); 135 | uart_output_queue_read = (uart_output_queue_read + 1) & (UART_MAX_QUEUE - 1); // Don't overrun 136 | } 137 | } 138 | 139 | void uart_writeByteBlocking(unsigned char ch) { 140 | unsigned int next = (uart_output_queue_write + 1) & (UART_MAX_QUEUE - 1); // Don't overrun 141 | 142 | while (next == uart_output_queue_read) uart_loadOutputFifo(); 143 | 144 | uart_output_queue[uart_output_queue_write] = ch; 145 | uart_output_queue_write = next; 146 | } 147 | 148 | void uart_drainOutputQueue() { 149 | while (!uart_isOutputQueueEmpty()) uart_loadOutputFifo(); 150 | } 151 | 152 | void uart_writeText(char *buffer) { 153 | while (*buffer) { 154 | if (*buffer == '\n') uart_writeByteBlocking('\r'); 155 | uart_writeByteBlocking(*buffer++); 156 | } 157 | uart_drainOutputQueue(); 158 | } 159 | 160 | void uart_update() { 161 | uart_loadOutputFifo(); 162 | 163 | if (uart_isReadByteReady()) { 164 | unsigned char ch = uart_readByte(); 165 | if (ch == '\r') uart_writeText("\n"); else uart_writeByteBlocking(ch); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src-memcopy/io.h: -------------------------------------------------------------------------------- 1 | #define PERIPHERAL_BASE 0xFE000000 2 | 3 | void uart_init(); 4 | void uart_writeText(char *buffer); 5 | void uart_loadOutputFifo(); 6 | unsigned char uart_readByte(); 7 | unsigned int uart_isReadByteReady(); 8 | void uart_writeByteBlocking(unsigned char ch); 9 | void uart_update(); 10 | void mmio_write(long reg, unsigned int val); 11 | unsigned int mmio_read(long reg); 12 | -------------------------------------------------------------------------------- /src-memcopy/kernel.c: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | // Very very simple 'kernel' to dump memory over UART 4 | unsigned long long *HEAP_START_; 5 | 6 | void main() 7 | { 8 | uart_init(); 9 | unsigned char *HEAP_START = (unsigned char *)0x0; 10 | unsigned long HEAP_SIZE = 0x100000000 / 8; 11 | 12 | if ((long)HEAP_START % 8 != 0) { 13 | HEAP_START += 8 - ((long)HEAP_START % 8); 14 | } 15 | 16 | HEAP_START_ = (unsigned long long *)HEAP_START; 17 | 18 | int b = 0; 19 | for (long i = 0; i < HEAP_SIZE; i++) { 20 | char *bytes = (char*)&HEAP_START_[i]; 21 | for(b = 0; b < 8; b++) 22 | uart_writeByteBlocking(bytes[b]); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src-memcopy/link.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | . = 0x80000; /* Kernel load address for AArch64 */ 4 | .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) } 5 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) } 6 | PROVIDE(_data = .); 7 | .data : { *(.data .data.* .gnu.linkonce.d*) } 8 | .bss (NOLOAD) : { 9 | . = ALIGN(16); 10 | __bss_start = .; 11 | *(.bss .bss.*) 12 | *(COMMON) 13 | __bss_end = .; 14 | } 15 | _end = .; 16 | 17 | /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } 18 | } 19 | __bss_size = (__bss_end - __bss_start)>>3; 20 | -------------------------------------------------------------------------------- /src-module/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += ramrec.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /src-module/mona-big.tga: -------------------------------------------------------------------------------- 1 | ../src/mona-big.tga -------------------------------------------------------------------------------- /src-module/mona.tga: -------------------------------------------------------------------------------- 1 | ../src/mona.tga -------------------------------------------------------------------------------- /src-module/ramrec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define CONTIGUOUS_START 0x2400000 13 | 14 | MODULE_LICENSE("GPL"); 15 | 16 | static bool writetoram = true; // Whether to write Mona to RAM, or try to read Mona from RAM 17 | module_param(writetoram, bool, 0660); 18 | static bool singleimage = false; // Whether to write a single image or not 19 | module_param(singleimage, bool, 0660); 20 | static char *filename = "./mona.tga"; // Name of filename to load into RAM 21 | module_param(filename, charp, 0660); 22 | 23 | static char *kbuf = NULL; 24 | static dma_addr_t handle; 25 | static size_t size = 1024 * 1024 * 650; // Contiguous RAM to allocate 26 | void devrelease(struct device *dev); 27 | 28 | static struct platform_device dev = { 29 | .name = "ramrecovery", 30 | .id = 0, 31 | }; 32 | 33 | void devrelease(struct device *dev) 34 | { 35 | if (kbuf) 36 | dma_free_coherent(dev, size, kbuf, handle); 37 | printk(KERN_INFO "Ramrec, free\n"); 38 | } 39 | 40 | static int __init ramrecovery_init(void) 41 | { 42 | struct file *fp; // File pointer, to Mona Lisa image, or memory dump binary 43 | int image; // Image number 44 | int imagebyte; // Index of nth byte of Mona Lisa image 45 | char *mona; // Holds Mona Lisa 46 | int count; // Count number of images wrote to memory 47 | int image_size; // Size of image 48 | int ret; 49 | 50 | if (writetoram) { 51 | // Try to fill contiguous RAM with Mona 52 | ret = platform_device_register(&dev); 53 | 54 | if (ret) { 55 | printk("Unable to register device\n"); 56 | return -1; 57 | } 58 | 59 | dev.dev.release = devrelease; 60 | 61 | ret = dma_set_mask_and_coherent(&dev.dev, DMA_BIT_MASK(64)); 62 | if (ret) { 63 | printk(KERN_ERR "dma_set_mask returned: %d\n", ret); 64 | goto err; 65 | } 66 | 67 | // Allocate lots of contiguous RAM 68 | kbuf = dma_alloc_coherent(&dev.dev, size, &handle, GFP_KERNEL); 69 | if (!kbuf) { 70 | printk(KERN_ERR "dma_alloc_coherent failed\n"); 71 | goto err; 72 | } 73 | 74 | // Open Mona file 75 | fp = filp_open(filename, O_RDONLY, 0); 76 | if (IS_ERR(fp)) { 77 | printk(KERN_ERR "Loading Mona failed\n"); 78 | goto err; 79 | } 80 | 81 | // Read Mona to buffer 82 | image_size = i_size_read(file_inode(fp)); 83 | printk("Image size %d\n", image_size); 84 | mona = kmalloc(image_size, GFP_KERNEL); 85 | ret = kernel_read(fp, mona, image_size, NULL); 86 | 87 | // Fill contiguous RAM with Mona 88 | printk("Writing to address %llx\n", dma_to_phys(&dev.dev, handle)); 89 | for (image = 0; image < size; image += image_size) { 90 | printk("Image %d\n", image); 91 | if (image + image_size >= size) 92 | break; 93 | for (imagebyte = 0; imagebyte < image_size; imagebyte++) { 94 | kbuf[image + imagebyte] = mona[imagebyte]; 95 | } 96 | count++; 97 | if (singleimage) 98 | break; 99 | } 100 | printk("Wrote %d images\n", count); 101 | } else { 102 | // Try to read from same amount of contiguous RAM, as we allocated previously 103 | kbuf = memremap(CONTIGUOUS_START, size, MEMREMAP_WB); 104 | 105 | if (!kbuf) { 106 | printk("Memory mapping failed\n"); 107 | return -1; 108 | } 109 | // Dump memory to a file 110 | fp = filp_open("./out.bin", O_RDWR | O_CREAT, 0644); 111 | ret = kernel_write(fp, kbuf, size, 0); 112 | iounmap(kbuf); 113 | } 114 | 115 | return 0; 116 | 117 | err: 118 | // Something bad happened 119 | platform_device_unregister(&dev); 120 | return -1; 121 | } 122 | 123 | static void __exit ramrecovery_exit(void) 124 | { 125 | if (writetoram) 126 | platform_device_unregister(&dev); 127 | printk(KERN_INFO "Ramrec, fin.\n"); 128 | } 129 | 130 | module_init(ramrecovery_init); 131 | module_exit(ramrecovery_exit); 132 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall 3 | 4 | all: load 5 | load: load.o 6 | load.o: load.c 7 | 8 | clean: 9 | rm -f load load.o 10 | run: load 11 | ./load 12 | -------------------------------------------------------------------------------- /src/load.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // https://stackoverflow.com/questions/2513505/how-to-get-available-memory-c-g 7 | unsigned long long getTotalSystemMemory() 8 | { 9 | long pages = sysconf(_SC_PHYS_PAGES); 10 | long page_size = sysconf(_SC_PAGE_SIZE); 11 | return pages * page_size; 12 | } 13 | 14 | int main() 15 | { 16 | // Load image 17 | FILE *fp = fopen("mona.tga", "rb"); 18 | 19 | // Get size of Mona file 20 | fseek(fp, 0L, SEEK_END); 21 | int monasize = ftell(fp); 22 | rewind(fp); 23 | 24 | // Read image to buffer 25 | char *buffer = malloc(monasize); 26 | fread(buffer, monasize, 1, fp); 27 | 28 | // Try to guess how much memory is free and make sure to subtract 29 | // an amount to avoid getting OOM killed (500MB in this case). 30 | unsigned long size = getTotalSystemMemory() - (1024 * 1024 * 500); 31 | char *buffer2 = NULL; 32 | 33 | // Try amounts decreasing by 50MB at a time, till we are able to allocate 34 | // memory (or stop attempting at 512MB) 35 | for (; size >= 1024 * 1024 * 512; size -= 1024 * 1024 * 50) { 36 | printf("Attempting to allocate %lu bytes\n", size); 37 | buffer2 = malloc(size); 38 | if (buffer2 != NULL) 39 | break; 40 | } 41 | 42 | if (buffer2) { 43 | // Fill memory with Mona Lisa 44 | long i = 0; 45 | while ((i * monasize) + monasize < size) { 46 | printf("%lu\n", (i * monasize)); 47 | memcpy(buffer2 + (i * monasize), buffer, monasize); 48 | i++; 49 | } 50 | 51 | printf("Done - injected %lu images\n", i+1); 52 | fflush(stdout); 53 | 54 | while (1) { 55 | sleep(5); 56 | } 57 | } else 58 | fprintf(stderr, "Failed\n"); 59 | 60 | fclose(fp); 61 | } 62 | -------------------------------------------------------------------------------- /src/mona-big.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anfractuosity/ramrecovery/eea1647b66d5fab3e560d531ef7cebcd585fb624/src/mona-big.tga -------------------------------------------------------------------------------- /src/mona.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anfractuosity/ramrecovery/eea1647b66d5fab3e560d531ef7cebcd585fb624/src/mona.tga --------------------------------------------------------------------------------