Initial attempt at guessing engine structure
This commit is contained in:
commit
bcef2641cc
7 changed files with 564 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/aes-test
|
||||||
|
/deploy.sh
|
81
README.md
Normal file
81
README.md
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# S5L8730 AES Engine
|
||||||
|
While toying around with an iPod nano 5th generation, I was curious on if one could utilize the AES engine beyond AES-128-CBC (as present within its firmware and bootrom). Unfortunately, there appears to be very little documentation regarding its operation beyond an implementation for the original iPhone/iPod touch (mirroring its bootrom as well). (Further frustratingly, the [S5L8700X datasheet](<https://freemyipod.org/wiki/S5L8700_datasheet>) marks the AES engine at `0x38C00000` marked as "reserved" and provides no insight beyond that.) The following is some experimentation on this in case it helps anyone else researching.
|
||||||
|
|
||||||
|
It appears that the engine present in S5L8730 (and presumably earlier?) may be a modification of what Samsung put in their later S5P cores - very close to what's present in modern Exynos. Another Samsung SoC, the S5PV210, has support for its crypto engine in the [Linux kernel](<https://github.com/torvalds/linux/blob/7ba2090ca64ea1aa435744884124387db1fac70f/drivers/crypto/s5p-sss.c#L97>) and its datasheet can be found floating around. Based on such, the S5L8730's engine appears to be a vague mix of its "feed" and AES engines. However, unlike the S5PV210, the S5L8730's appears to be purely DMA, and there is no PKA (public key accelerator).
|
||||||
|
|
||||||
|
## Register Layout
|
||||||
|
Please feel free to pull request and create issues - by no means should this be taken as a source of truth; descriptions and meanings can/will be wrong :)
|
||||||
|
|
||||||
|
As follows is the guessed layout of register space, base `0x38C00000`. Some discussion proceeds these tables. Please do not consider these official names.
|
||||||
|
| Offset | Name | Notes |
|
||||||
|
|--------|----------------------------|---------------------------------------|
|
||||||
|
| 0x0 | `AES_REGISTER_CONTROL` | Appears to control engine operation, possibly? Keys must be set up afterwards. |
|
||||||
|
| 0x4 | `AES_REGISTER_GO` | Bit 0 controls operation; all others appear to be ignored. |
|
||||||
|
| 0x8 | [`AES_REGISTER_KEY_UNKNOWN`](#aes_register_key_unknown-0x08) | Bit 0 appears to block operation of the engine. |
|
||||||
|
| 0xc | [`AES_REGISTER_STATUS`](#aes_register_status-0x0c) | Presumably, at least? Its lowest three bits change throughout operation. Write those three bits to clear. |
|
||||||
|
| 0x10 | `AES_REGISTER_UNKNOWN_1` | Seemingly unused. It appears to ignore all writes. |
|
||||||
|
| 0x14 | [`AES_REGISTER_SETUP`](#aes_register_setup-0x14) | Configures the operation the engine should perform. |
|
||||||
|
| 0x18 | `AES_REGISTER_OUT_SIZE` | The length of data to write to output. |
|
||||||
|
| 0x1c | `AES_REGISTER_OUT_UNUSED` | Seemingly unused. All writes are ignored. |
|
||||||
|
| 0x20 | `AES_REGISTER_OUT_ADDRESS` | The address of the output buffer. |
|
||||||
|
| 0x24 | `AES_REGISTER_IN_SIZE` | The length of data to process. |
|
||||||
|
| 0x28 | `AES_REGISTER_IN_ADDRESS` | The address of the input buffer. |
|
||||||
|
| 0x2c | `AES_REGISTER_AUX_SIZE` | Unclear on usage - its value must match `AES_REGISTER_IN_SIZE` or things break. |
|
||||||
|
| 0x30 | `AES_REGISTER_AUX_ADDRESS` | Unclear on usage - its value must match `AES_REGISTER_IN_ADDRESS` or things break. |
|
||||||
|
| 0x34 | `AES_REGISTER_ADDITIONAL_SIZE` | Unclear on usage - its value must also match `AES_REGISTER_IN_SIZE` or the engine will error. |
|
||||||
|
| 0x38 | `AES_REGISTER_UNKNOWN_2` | Appears to have a value 0x12040 greater than `AES_REGISTER_OUT_ADDRESS`. Possibly address to next block in... some way? |
|
||||||
|
| 0x3c | `AES_REGISTER_UNKNOWN_3` | This is 0x40 greater than UNKNOWN_2 above - 0x12080 greater than the out address. |
|
||||||
|
| 0x40 | `AES_REGISTER_UNKNOWN_4` | This is set to `1` if `AES_REGISTER_OUT_SIZE` is greater than `AES_REGISTER_IN_SIZE`. |
|
||||||
|
| 0x44 | `AES_REGISTER_UNKNOWN_5` | Its value is often `00004040`. On error, its value may be `00054040`. |
|
||||||
|
| 0x48 | `AES_REGISTER_UNKNOWN_6` | Always observed to be zero - unclear. |
|
||||||
|
| 0x4c | `AES_REGISTER_KEY1` | For 256-bit AES, begin writing the key here. |
|
||||||
|
| 0x50 | `AES_REGISTER_KEY2` | [...] |
|
||||||
|
| 0x54 | `AES_REGISTER_KEY3` | For 192-bit AES, begin writing the key here. |
|
||||||
|
| 0x58 | `AES_REGISTER_KEY4` | [...] |
|
||||||
|
| 0x5c | `AES_REGISTER_KEY5` | For 128-bit AES, begin writing the key here. |
|
||||||
|
| 0x60 | `AES_REGISTER_KEY6` | [...] |
|
||||||
|
| 0x64 | `AES_REGISTER_KEY7` | [...] |
|
||||||
|
| 0x68 | `AES_REGISTER_KEY8` | Lowest half of AES key. |
|
||||||
|
| 0x6c | `AES_REGISTER_KEY_TYPE` | The key type to utilize. Its value must be also written to `AES_REGISTER_KEY_TYPE_AGAIN` in an inverted form. |
|
||||||
|
| 0x70 | `AES_REGISTER_OPERATION_UNKNOWN` | ???? |
|
||||||
|
| 0x74 | `AES_REGISTER_IV1` | Upper word of AES IV. |
|
||||||
|
| 0x78 | `AES_REGISTER_IV2` | [...] |
|
||||||
|
| 0x7c | `AES_REGISTER_IV3` | [...] |
|
||||||
|
| 0x80 | `AES_REGISTER_IV4` | [...] |
|
||||||
|
| 0x84 | `AES_REGISTER_UKNOWN_UNUSED_1` | Its value appears to reset to 0xf. It additionally appears to ignore writes. |
|
||||||
|
| 0x88 | `AES_REGISTER_KEY_TYPE_AGAIN` | The key type to utilize... again. Its value must be an inverted form of `AES_REGISTER_KEY_TYPE`. |
|
||||||
|
|
||||||
|
AES keys and IVs must be little-endian. Unlike later SoCs, there appears to be no byte swapping.
|
||||||
|
|
||||||
|
### AES_REGISTER_KEY_UNKNOWN (0x08)
|
||||||
|
Its usage is unknown. Setting its zeroth bit appears to halt engine operation - perhaps it denotes engine state?
|
||||||
|
|
||||||
|
Some notes on its usage:
|
||||||
|
- Within the iPod nano BootROM, this register is polled until its fourth bit is cleared. Through observation, only three bits were ever set.
|
||||||
|
- The retail firmware waits for the zeroth bit to be cleared.
|
||||||
|
- Oddly, the EFI driver simply waits for 2000 us.
|
||||||
|
|
||||||
|
### AES_REGISTER_STATUS (0x0c)
|
||||||
|
Within its retail firmware and EFI driver, 0x7 is written to this prior to operation. The BootROM does not whatsoever. Its lowest bit is checked to ensure an operation has completed. It appears you must write the lowest bit to allow operation to continue.
|
||||||
|
|
||||||
|
| Bit | Description |
|
||||||
|
|--------|---------------------------|
|
||||||
|
| [31:5] | Seemingly unused. Ignores writes. |
|
||||||
|
| [4] | Unknown; see above. |
|
||||||
|
| [3] | Unknown. Set by default. |
|
||||||
|
| [2] | Unknown. Set by default. |
|
||||||
|
| [1] | Whether an operation is ongoing. |
|
||||||
|
|
||||||
|
### AES_REGISTER_SETUP (0x14)
|
||||||
|
It's unclear on how to configure CTR. It appears to only produce valid output with AES-192-CTR and AES-256-CTR. (This may be wrong - please validate yourself.)
|
||||||
|
|
||||||
|
| Bit | Description |
|
||||||
|
|--------|---------------------------|
|
||||||
|
| [31:6] | Seemingly ignored. |
|
||||||
|
| [5:4] | Specifies key size.<br><br>**00** => 128-bit<br>**01** => 192-bit<br>**10** => 256-bit<br>**11** => Also 256-bit(?) |
|
||||||
|
| [3:2] | Specifies AES mode.<br><br>**00** => ECB<br>**01** => Also ECB(?)<br>**10** => CBC<br>**11** => CTR |
|
||||||
|
| [1] | Seemingly ignored - unsetting it appears to have no change, but it is set within iPod firmware. |
|
||||||
|
| [0] | Whether to encrypt.<br><br>**0** => Decrypt<br>**1** => Encrypt |
|
||||||
|
|
||||||
|
### AES_REGISTER_KEY_TYPE_AGAIN (0x6c)
|
||||||
|
Its value must be the inverse (i.e. its value bitwise NOR'd), or the used key will not differ. Notably, this is 0xffffffff on error - could it possibly deal with error handling as well?
|
321
aes-test.c
Normal file
321
aes-test.c
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "hex-dump.h"
|
||||||
|
#include "virt-to-phys.h"
|
||||||
|
|
||||||
|
#define AES_REGISTER_BASE 0x38c00000
|
||||||
|
|
||||||
|
// A mix between `FCINTSTAT` and `AES_Status`?
|
||||||
|
#define AES_REGISTER_CONTROL AES_REGISTER_BASE + 0x00
|
||||||
|
|
||||||
|
// Appears to begin the AES operation? Maybe?
|
||||||
|
#define AES_REGISTER_GO AES_REGISTER_BASE + 0x04
|
||||||
|
// 0x1 is written to it to when using the fourth key (0x3, 0b11).
|
||||||
|
// Its value appears to be checked for to be zero after write.
|
||||||
|
// However, writing zero to it appears to allow usage again.
|
||||||
|
#define AES_REGISTER_KEY_UNKNOWN AES_REGISTER_BASE + 0x08
|
||||||
|
#define AES_REGISTER_STATUS AES_REGISTER_BASE + 0x0c
|
||||||
|
// Seemingly unused.
|
||||||
|
#define AES_REGISTER_UNKNOWN_1 AES_REGISTER_BASE + 0x10
|
||||||
|
#define AES_REGISTER_KEY_CONTROL_TEST AES_REGISTER_BASE + 0x14
|
||||||
|
|
||||||
|
#define AES_REGISTER_OUT_SIZE AES_REGISTER_BASE + 0x18
|
||||||
|
// This appears to not do anything. Any values written to it are seemingly ignored.
|
||||||
|
#define AES_REGISTER_OUT_UNUSED AES_REGISTER_BASE + 0x1c
|
||||||
|
#define AES_REGISTER_OUT_ADDRESS AES_REGISTER_BASE + 0x20
|
||||||
|
|
||||||
|
#define AES_REGISTER_IN_SIZE AES_REGISTER_BASE + 0x24
|
||||||
|
#define AES_REGISTER_IN_ADDRESS AES_REGISTER_BASE + 0x28
|
||||||
|
|
||||||
|
// Referred to as "aux" - what is this?
|
||||||
|
#define AES_REGISTER_AUX_SIZE AES_REGISTER_BASE + 0x2c
|
||||||
|
#define AES_REGISTER_AUX_ADDR AES_REGISTER_BASE + 0x30
|
||||||
|
// Why is the size present twice?
|
||||||
|
#define AES_REGISTER_ADDITIONAL_SIZE AES_REGISTER_BASE + 0x34
|
||||||
|
|
||||||
|
// TODO: What are these?
|
||||||
|
#define AES_REGISTER_UNKNOWN_2 AES_REGISTER_BASE + 0x38
|
||||||
|
#define AES_REGISTER_UNKNOWN_3 AES_REGISTER_BASE + 0x3c
|
||||||
|
#define AES_REGISTER_UNKNOWN_4 AES_REGISTER_BASE + 0x40
|
||||||
|
#define AES_REGISTER_UNKNOWN_5 AES_REGISTER_BASE + 0x44
|
||||||
|
#define AES_REGISTER_UNKNOWN_6 AES_REGISTER_BASE + 0x48
|
||||||
|
|
||||||
|
// XXX: This seems to be useful for 256-bit AES keys.
|
||||||
|
#define AES_REGISTER_KEY1 AES_REGISTER_BASE + 0x4c
|
||||||
|
#define AES_REGISTER_KEY2 AES_REGISTER_BASE + 0x50
|
||||||
|
// XXX: This appears to be useful for 192-bit AES keys.
|
||||||
|
#define AES_REGISTER_KEY3 AES_REGISTER_BASE + 0x54
|
||||||
|
#define AES_REGISTER_KEY4 AES_REGISTER_BASE + 0x58
|
||||||
|
// XXX: This appears to be useful for 128-bit AES keys.
|
||||||
|
#define AES_REGISTER_KEY5 AES_REGISTER_BASE + 0x5c
|
||||||
|
#define AES_REGISTER_KEY6 AES_REGISTER_BASE + 0x60
|
||||||
|
#define AES_REGISTER_KEY7 AES_REGISTER_BASE + 0x64
|
||||||
|
#define AES_REGISTER_KEY8 AES_REGISTER_BASE + 0x68
|
||||||
|
|
||||||
|
#define AES_REGISTER_KEY_TYPE AES_REGISTER_BASE + 0x6c
|
||||||
|
#define AES_REGISTER_OPERATION_UNKNOWN AES_REGISTER_BASE + 0x70
|
||||||
|
|
||||||
|
#define AES_REGISTER_IV1 AES_REGISTER_BASE + 0x74
|
||||||
|
#define AES_REGISTER_IV2 AES_REGISTER_BASE + 0x78
|
||||||
|
#define AES_REGISTER_IV3 AES_REGISTER_BASE + 0x7c
|
||||||
|
#define AES_REGISTER_IV4 AES_REGISTER_BASE + 0x80
|
||||||
|
|
||||||
|
// What happened to 0x84?
|
||||||
|
#define AES_REGISTER_UKNOWN_UNUSED_1 AES_REGISTER_BASE + 0x84
|
||||||
|
|
||||||
|
// ???
|
||||||
|
#define AES_REGISTER_KEY_TYPE_AGAIN AES_REGISTER_BASE + 0x88
|
||||||
|
|
||||||
|
// Our global memory.
|
||||||
|
uint32_t *aes_mem = NULL;
|
||||||
|
|
||||||
|
// Writes to a location in memory. Assumes the address is 0x38c00000 to 0x38c00100 (0x100 bytes).
|
||||||
|
void write_uint32(uint32_t register_offset, uint32_t value) {
|
||||||
|
if (register_offset < AES_REGISTER_BASE || register_offset > (AES_REGISTER_BASE + 0x100)) {
|
||||||
|
printf("Invalid write to 0x%08x!", register_offset);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t adjusted_offset = register_offset - AES_REGISTER_BASE;
|
||||||
|
uint32_t mmap_array_index = adjusted_offset / 4;
|
||||||
|
aes_mem[mmap_array_index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a uint32_t from memory. Assumes the value is 0x38c00000 to 0x38c00100 (0x100 bytes).
|
||||||
|
uint32_t read_uint32(uint32_t register_offset) {
|
||||||
|
if (register_offset < AES_REGISTER_BASE || register_offset > (AES_REGISTER_BASE + 0x100)) {
|
||||||
|
printf("Invalid read from 0x%08x!", register_offset);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t adjusted_offset = register_offset - AES_REGISTER_BASE;
|
||||||
|
|
||||||
|
// However, this is an array of uint32_ts - the actual address is divided by sizeof(uint32_t), or 4.
|
||||||
|
uint32_t mmap_array_index = adjusted_offset / 4;
|
||||||
|
return (uint32_t)aes_mem[mmap_array_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Outputs the value of the current register, along with its bits.
|
||||||
|
void dump_register(uint32_t current_value) {
|
||||||
|
printf("%08x (", current_value);
|
||||||
|
for (int current_bit = 32; current_bit > 0; current_bit -= 8) {
|
||||||
|
// Add a space between groups of bits.
|
||||||
|
if (current_bit != 32) {
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int bit_offset = 0; bit_offset < 8; bit_offset++) {
|
||||||
|
int shifted_value = 1 << (current_bit - bit_offset - 1);
|
||||||
|
bool has_bit = (current_value & shifted_value) == shifted_value;
|
||||||
|
|
||||||
|
printf("%d", has_bit ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(")\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dumps 0x90 bytes.
|
||||||
|
void dump_mem() {
|
||||||
|
printf("Dumping:\n");
|
||||||
|
for (size_t i = 0; i < 0x90; i += 0x4) {
|
||||||
|
uint32_t current_offset = AES_REGISTER_BASE + i;
|
||||||
|
uint32_t current_value = read_uint32(current_offset);
|
||||||
|
printf("%08x => ", current_offset);
|
||||||
|
|
||||||
|
// List bits
|
||||||
|
dump_register(current_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - AAAAA
|
||||||
|
|
||||||
|
// [1:0] Key type
|
||||||
|
//
|
||||||
|
// 00 - custom key
|
||||||
|
// 01 - GID key, ignores custom key
|
||||||
|
// 10 - ???
|
||||||
|
// 11 - ???
|
||||||
|
// Any bits set above those two appear to be ignored.
|
||||||
|
#define AES_KEY_TYPE_CONTENTS 0b00
|
||||||
|
|
||||||
|
uint32_t key_control_contents() {
|
||||||
|
return
|
||||||
|
// [31:6] Not functional, seemingly.
|
||||||
|
(1 << 31) + (1 << 30) + (1 << 29) + (1 << 28) +
|
||||||
|
(1 << 27) + (1 << 26) + (1 << 25) + (1 << 24) +
|
||||||
|
(1 << 23) + (1 << 22) + (1 << 21) + (1 << 20) +
|
||||||
|
(1 << 19) + (1 << 18) + (1 << 17) + (1 << 16) +
|
||||||
|
(1 << 15) + (1 << 14) + (1 << 13) + (1 << 12) +
|
||||||
|
(1 << 11) + (1 << 10) + (1 << 9) + (1 << 8) +
|
||||||
|
(1 << 7) + (1 << 6) +
|
||||||
|
// [5:4] Key size
|
||||||
|
//
|
||||||
|
// 00 => 128-bit
|
||||||
|
// 01 => 192-bit
|
||||||
|
// 10 => 256-bit
|
||||||
|
// 11 => ??? behaves like 256-bit
|
||||||
|
(0 << 5) + (0 << 4) +
|
||||||
|
// [3:2] Operation
|
||||||
|
//
|
||||||
|
// 00 => ??? looks like ECB
|
||||||
|
// 01 => ECB
|
||||||
|
// 10 => CBC
|
||||||
|
// 11 => CTR
|
||||||
|
(1 << 3) + (0 << 2) +
|
||||||
|
// [1] Seemingly ignored, or not functional?
|
||||||
|
// It appears to be set within the iPod's firmware, so it must have some meaning.
|
||||||
|
(1 << 1) +
|
||||||
|
// [0] Mode
|
||||||
|
//
|
||||||
|
// 0 => Decrypt
|
||||||
|
// 1 => Encrypt
|
||||||
|
(1 << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_aes_key() {
|
||||||
|
// To clear the 256-bit half:
|
||||||
|
// write_uint32(AES_REGISTER_KEY1, 0);
|
||||||
|
// write_uint32(AES_REGISTER_KEY2, 0);
|
||||||
|
// and the 192-bit half:
|
||||||
|
// write_uint32(AES_REGISTER_KEY3, 0);
|
||||||
|
// write_uint32(AES_REGISTER_KEY4, 0);
|
||||||
|
// lastly, for the 128-bit half:
|
||||||
|
// write_uint32(AES_REGISTER_KEY5, 0);
|
||||||
|
// write_uint32(AES_REGISTER_KEY6, 0);
|
||||||
|
// write_uint32(AES_REGISTER_KEY7, 0);
|
||||||
|
// write_uint32(AES_REGISTER_KEY8, 0);
|
||||||
|
|
||||||
|
// Set our testing key, 02418105 dfb3be2a f2a76248 e026f702:
|
||||||
|
// Note that the
|
||||||
|
write_uint32(AES_REGISTER_KEY5, 0x05814102);
|
||||||
|
write_uint32(AES_REGISTER_KEY6, 0x2abeb3df);
|
||||||
|
write_uint32(AES_REGISTER_KEY7, 0x4862a7f2);
|
||||||
|
write_uint32(AES_REGISTER_KEY8, 0x02f726e0);
|
||||||
|
|
||||||
|
// Set an empty IV.
|
||||||
|
write_uint32(AES_REGISTER_IV1, 0);
|
||||||
|
write_uint32(AES_REGISTER_IV2, 0);
|
||||||
|
write_uint32(AES_REGISTER_IV3, 0);
|
||||||
|
write_uint32(AES_REGISTER_IV4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char in_data[64] = "";
|
||||||
|
static unsigned char out_data[64] = "";
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// As described in https://stackoverflow.com/a/12041352
|
||||||
|
int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||||
|
|
||||||
|
// 0x38c00000 (AES_REGISTER_BASE) is definitely a multiple of the page size,
|
||||||
|
// so we'll use it directly.
|
||||||
|
//
|
||||||
|
// We'll map about 0x100 in, just because we can. I have no clue what its true size is.
|
||||||
|
aes_mem = mmap(NULL, 0x100, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, AES_REGISTER_BASE);
|
||||||
|
if (aes_mem == MAP_FAILED) {
|
||||||
|
printf("Can't map memory\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apparently we must read and write this for various lazy allocation reasons.
|
||||||
|
strcpy(in_data, "Hello, world!");
|
||||||
|
strcpy(out_data, "");
|
||||||
|
|
||||||
|
// We'll now obtain the physical addresses of our buffers.
|
||||||
|
uintptr_t phys_out_addr = virt_to_phys_user("out_buf", (uintptr_t)&out_data);
|
||||||
|
uintptr_t phys_in_addr = virt_to_phys_user("in_buf", (uintptr_t)&in_data);
|
||||||
|
|
||||||
|
// As performed by 0x0818ea44 ("AESHardwareDecryptEncrypt") within firmware 1.0.2 for the iPod nano 5th gen:
|
||||||
|
printf("Prior to preparation:\n");
|
||||||
|
|
||||||
|
// TODO: What are these?
|
||||||
|
// write_uint32(AES_REGISTER_UNKNOWN_2, 0);
|
||||||
|
// write_uint32(AES_REGISTER_UNKNOWN_3, 0);
|
||||||
|
// write_uint32(AES_REGISTER_UNKNOWN_4, 0);
|
||||||
|
write_uint32(AES_REGISTER_UKNOWN_UNUSED_1, 0);
|
||||||
|
// write_uint32(AES_REGISTER_UNKNOWN_6, 0);
|
||||||
|
|
||||||
|
dump_mem();
|
||||||
|
|
||||||
|
// // Called with non-custom keys (i.e. what we're doing).
|
||||||
|
// // Writing 1 appears to halt functionality, and 0 seems to have it resume.
|
||||||
|
// //
|
||||||
|
// // TODO: That's... wrong. What is the right approach?
|
||||||
|
// write_uint32(AES_REGISTER_KEY_UNKNOWN, 0);
|
||||||
|
// write_uint32(AES_REGISTER_KEY_UNKNOWN, 1);
|
||||||
|
// uint32_t current_status = read_uint32(AES_REGISTER_KEY_UNKNOWN);
|
||||||
|
// printf("Preparation value: ");
|
||||||
|
// dump_register(current_status);
|
||||||
|
// write_uint32(AES_REGISTER_KEY_UNKNOWN, 0);
|
||||||
|
|
||||||
|
// TODO: What is this register?
|
||||||
|
// Possibly flush, or reset to zero?
|
||||||
|
write_uint32(AES_REGISTER_OPERATION_UNKNOWN, 0b001);
|
||||||
|
// Let's not specify a custom key, just for testing.
|
||||||
|
write_uint32(AES_REGISTER_KEY_TYPE, AES_KEY_TYPE_CONTENTS);
|
||||||
|
// This is read, and... written within firmware? Reading does not appear to be necessary.
|
||||||
|
uint32_t weird_key_type_value = read_uint32(AES_REGISTER_KEY_TYPE);
|
||||||
|
printf("Key type: ");
|
||||||
|
dump_register(weird_key_type_value);
|
||||||
|
write_uint32(AES_REGISTER_KEY_TYPE_AGAIN, ~weird_key_type_value);
|
||||||
|
|
||||||
|
// After control is set to 1, it appears the actual setup begins.
|
||||||
|
// TODO: What exactly does this control?
|
||||||
|
write_uint32(AES_REGISTER_CONTROL, 1);
|
||||||
|
|
||||||
|
// See the comments within key_control_contents for guessed structure.
|
||||||
|
write_uint32(AES_REGISTER_KEY_CONTROL_TEST, key_control_contents());
|
||||||
|
|
||||||
|
// Our output data is only used in two registers.
|
||||||
|
write_uint32(AES_REGISTER_OUT_SIZE, 64);
|
||||||
|
write_uint32(AES_REGISTER_OUT_ADDRESS, phys_out_addr);
|
||||||
|
|
||||||
|
// Meanwhile, input appears to be both in its own, and auxilary.
|
||||||
|
write_uint32(AES_REGISTER_IN_SIZE, 64);
|
||||||
|
write_uint32(AES_REGISTER_IN_ADDRESS, phys_in_addr);
|
||||||
|
// What exactly is auxilary?
|
||||||
|
write_uint32(AES_REGISTER_AUX_SIZE, 64);
|
||||||
|
write_uint32(AES_REGISTER_AUX_ADDR, phys_in_addr);
|
||||||
|
// What is this additional size?
|
||||||
|
write_uint32(AES_REGISTER_ADDITIONAL_SIZE, 64);
|
||||||
|
|
||||||
|
setup_aes_key();
|
||||||
|
|
||||||
|
printf("About to encrypt...\n");
|
||||||
|
dump_mem();
|
||||||
|
|
||||||
|
// TODO: Determine bit fields for status. It's set to 7 within firmware, but 6 within bootrom.
|
||||||
|
// It seems it really only needs to be one...
|
||||||
|
write_uint32(AES_REGISTER_STATUS, 7);
|
||||||
|
write_uint32(AES_REGISTER_GO, 1);
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
|
while (running) {
|
||||||
|
uint32_t current_status = read_uint32(AES_REGISTER_STATUS);
|
||||||
|
printf("Current status: ");
|
||||||
|
dump_register(current_status);
|
||||||
|
|
||||||
|
if ((current_status & 1) == 0) {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(100);
|
||||||
|
|
||||||
|
// TODO: What exactly does this do?
|
||||||
|
write_uint32(AES_REGISTER_CONTROL, 0);
|
||||||
|
|
||||||
|
printf("Finished encrypting!\n");
|
||||||
|
dump_mem();
|
||||||
|
|
||||||
|
hexDump("input buf", &in_data, 64, 16);
|
||||||
|
hexDump("output buf", &out_data, 64, 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
72
hex-dump.c
Normal file
72
hex-dump.c
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// XXX: Taken from the wonderful https://stackoverflow.com/a/7776146
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void hexDump (
|
||||||
|
const char * desc,
|
||||||
|
const void * addr,
|
||||||
|
const int len,
|
||||||
|
int perLine
|
||||||
|
) {
|
||||||
|
// Silently ignore silly per-line values.
|
||||||
|
|
||||||
|
if (perLine < 4 || perLine > 64) perLine = 16;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
unsigned char buff[perLine+1];
|
||||||
|
const unsigned char * pc = (const unsigned char *)addr;
|
||||||
|
|
||||||
|
// Output description if given.
|
||||||
|
|
||||||
|
if (desc != NULL) printf ("%s:\n", desc);
|
||||||
|
|
||||||
|
// Length checks.
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
printf(" ZERO LENGTH\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len < 0) {
|
||||||
|
printf(" NEGATIVE LENGTH: %d\n", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process every byte in the data.
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
// Multiple of perLine means new or first line (with line offset).
|
||||||
|
|
||||||
|
if ((i % perLine) == 0) {
|
||||||
|
// Only print previous-line ASCII buffer for lines beyond first.
|
||||||
|
|
||||||
|
if (i != 0) printf (" %s\n", buff);
|
||||||
|
|
||||||
|
// Output the offset of current line.
|
||||||
|
|
||||||
|
printf (" %04x ", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the hex code for the specific character.
|
||||||
|
|
||||||
|
printf (" %02x", pc[i]);
|
||||||
|
|
||||||
|
// And buffer a printable ASCII character for later.
|
||||||
|
|
||||||
|
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better.
|
||||||
|
buff[i % perLine] = '.';
|
||||||
|
else
|
||||||
|
buff[i % perLine] = pc[i];
|
||||||
|
buff[(i % perLine) + 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad out last line if not exactly perLine characters.
|
||||||
|
|
||||||
|
while ((i % perLine) != 0) {
|
||||||
|
printf (" ");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And print the final ASCII buffer.
|
||||||
|
|
||||||
|
printf (" %s\n", buff);
|
||||||
|
}
|
8
hex-dump.h
Normal file
8
hex-dump.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Usage:
|
||||||
|
// hexDump(desc, addr, len, perLine);
|
||||||
|
// desc: if non-NULL, printed as a description before hex dump.
|
||||||
|
// addr: the address to start dumping from.
|
||||||
|
// len: the number of bytes to dump.
|
||||||
|
// perLine: number of bytes on each output line.
|
||||||
|
|
||||||
|
void hexDump(const char * desc, const void * addr, const int len, int perLine);
|
77
virt-to-phys.c
Normal file
77
virt-to-phys.c
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// XXX: Taken from https://stackoverflow.com/a/45128487
|
||||||
|
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#include <fcntl.h> /* open */
|
||||||
|
#include <stdint.h> /* uint64_t */
|
||||||
|
#include <stdio.h> /* printf */
|
||||||
|
#include <stdlib.h> /* size_t */
|
||||||
|
#include <unistd.h> /* pread, sysconf */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t pfn : 55;
|
||||||
|
unsigned int soft_dirty : 1;
|
||||||
|
unsigned int file_page : 1;
|
||||||
|
unsigned int swapped : 1;
|
||||||
|
unsigned int present : 1;
|
||||||
|
} PagemapEntry;
|
||||||
|
|
||||||
|
/* Parse the pagemap entry for the given virtual address.
|
||||||
|
*
|
||||||
|
* @param[out] entry the parsed entry
|
||||||
|
* @param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file
|
||||||
|
* @param[in] vaddr virtual address to get entry for
|
||||||
|
* @return 0 for success, 1 for failure
|
||||||
|
*/
|
||||||
|
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
|
||||||
|
{
|
||||||
|
size_t nread;
|
||||||
|
ssize_t ret;
|
||||||
|
uint64_t data;
|
||||||
|
uintptr_t vpn;
|
||||||
|
|
||||||
|
vpn = vaddr / sysconf(_SC_PAGE_SIZE);
|
||||||
|
nread = 0;
|
||||||
|
while (nread < sizeof(data)) {
|
||||||
|
ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data) - nread,
|
||||||
|
vpn * sizeof(data) + nread);
|
||||||
|
nread += ret;
|
||||||
|
if (ret <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry->pfn = data & (((uint64_t)1 << 55) - 1);
|
||||||
|
entry->soft_dirty = (data >> 55) & 1;
|
||||||
|
entry->file_page = (data >> 61) & 1;
|
||||||
|
entry->swapped = (data >> 62) & 1;
|
||||||
|
entry->present = (data >> 63) & 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert the given virtual address to physical using /proc/PID/pagemap.
|
||||||
|
*
|
||||||
|
* @param[name] name of variable
|
||||||
|
* @param[in] vaddr virtual address to get entry for
|
||||||
|
* @return paddr (physical address) on success, exit on failure
|
||||||
|
*/
|
||||||
|
int virt_to_phys_user(char* name, uintptr_t vaddr)
|
||||||
|
{
|
||||||
|
char pagemap_file[BUFSIZ];
|
||||||
|
int pagemap_fd;
|
||||||
|
|
||||||
|
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)getpid());
|
||||||
|
pagemap_fd = open(pagemap_file, O_RDONLY);
|
||||||
|
if (pagemap_fd < 0) {
|
||||||
|
printf("Failed to open pagemap for process!");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
PagemapEntry entry;
|
||||||
|
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
|
||||||
|
printf("Failed to get entry for pagemap!");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
close(pagemap_fd);
|
||||||
|
|
||||||
|
uintptr_t paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
|
||||||
|
printf("%s: virt @ %p, phys @ %p\n", name, (void *)vaddr, (void*)paddr);
|
||||||
|
return paddr;
|
||||||
|
}
|
3
virt-to-phys.h
Normal file
3
virt-to-phys.h
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
uintptr_t virt_to_phys_user(char* name, uintptr_t vaddr);
|
Loading…
Add table
Add a link
Reference in a new issue