From bfb70c6ec9b54d04261f9ac7d625e8634131e09c Mon Sep 17 00:00:00 2001 From: kxtzownsu Date: Fri, 20 Dec 2024 00:30:58 -0500 Subject: [PATCH] Port KVG to Rust & write docs Also, this patch adds a fix that gets rid of cursor offset for VSCode Web (e.g: vscode.dev, GH Codespaces) TEST=compile KVG on all architectures --- .cargo/config.toml | 5 ++ .gitignore | 7 ++ .vscode/settings.json | 10 +++ Cargo.toml | 18 ++++ Makefile | 29 +++++- docs/BUILDING.md | 23 ++--- docs/USAGE.md | 67 ++++++++++++++ docs/cross-compilation.md | 10 +++ src/KVG/main.rs | 185 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 340 insertions(+), 14 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 .vscode/settings.json create mode 100644 Cargo.toml create mode 100644 docs/USAGE.md create mode 100644 docs/cross-compilation.md create mode 100644 src/KVG/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..15c6ad8 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-musl-gcc" + +[target.armv7-unknown-linux-musleabihf] +linker = "rust-lld" diff --git a/.gitignore b/.gitignore index 7c69e09..759b73c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # nya build/ +target/ +Cargo.lock # Prerequisites *.d @@ -53,3 +55,8 @@ modules.order Module.symvers Mkfile.old dkms.conf + + +# Added by cargo + +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ec8c142 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "files.associations": { + "stdio.h": "c" + }, + + // this setting isn't "unknown", its just for troubleshooting, + // but it fixes the vscode.dev cursor on ChromeOS. + "editor.disableMonospaceOptimizations": true + +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e635832 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "kvg-kvs" +version = "0.1.0" +authors = ["Katelyn"] +edition = "2021" + +[workspace] + +[dependencies] +clap = { version = "4.0", features = ["derive"] } + +[[bin]] +name = "KVG" +path = "src/KVG/main.rs" + +[[bin]] +name = "KVS" +path = "src/KVS/main.rs" diff --git a/Makefile b/Makefile index c80b0ed..72d9783 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -CC := gcc +CC ?= gcc +ARCH ?= x86_64 +TOOLCHAIN ?= musl SHELL ?= /bin/sh KVSFLIST := \ src/KVS/main.c \ @@ -13,10 +15,22 @@ CFLAGS := \ -Llib \ -static +ifeq ($(ARCH), aarch64) +CC := aarch64-linux-gnu-gcc +endif + +ifeq ($(ARCH), armv7) +CC := armv7-linux-gnu-gcc +TOOLCHAIN := musleabihf +endif + +TARGET = ${ARCH}-unknown-linux-${TOOLCHAIN} + all: clean build kvs kvg kvs: build build/bin/kvs kvg: build build/bin/kvg +kvg-c: build build/bin/kvg-c build: $(shell mkdir -p build/bin) @@ -25,12 +39,19 @@ build/bin/kvs: src/KVS/main.c $(CC) $(KVSFLIST) -o build/bin/kvs $(CFLAGS) chmod +rx build/bin/kvs -build/bin/kvg: src/KVG/main.c - $(CC) src/KVG/main.c -o build/bin/kvg $(CFLAGS) - chmod +rx build/bin/kvg +build/bin/kvg: src/KVG/main.rs + cargo build --bin KVG --target=$(TARGET) --release + cp target/$(TARGET)/release/KVG build/bin/kvg + +# The C version of KVS, not normally built. +# Also guaranteed to be out-of-date. +build/bin/kvg-c: src/KVG/main.c + $(CC) src/KVG/main.c -o build/bin/kvg-c $(CFLAGS) + chmod +rx build/bin/kvg-c install: cp -r build/* /usr/local/ clean: rm -rf build + rm -rf target diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 416f891..57a9df4 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -1,10 +1,10 @@ # Building KVS: ### Dependencies -You only need gcc & make! All static libs are inside `lib/` +You only need gcc, make, and musl-tools! All static libs are inside `lib/` ``` -Debian-based systems: sudo apt install gcc make -Arch-based systems: sudo pacman -S gcc make +Debian-based systems: sudo apt install gcc make musl-tools +Arch-based systems: sudo pacman -S gcc make musl Alpine-based systems: apk add gcc make ``` @@ -19,8 +19,8 @@ git clone https://github.com/kxtzownsu/KVS cd KVS # Third, run two command to compile the KVS & KVG binary -make kvs # final binary is at ./build/kvs -make kvg # final binary is at ./build/kvg +make kvs # final binary is at ./build/bin/kvs +make kvg # final binary is at ./build/bin/kvg # (OPTIONAL) Fourth, run the shim builder sudo make shim-builder @@ -35,11 +35,11 @@ Notes: KVS **requires** KVG or else the shim will not build successfully # Building KVG: ### Dependencies -Same as KVS, you only need `gcc` and `make` +Same as KVS, you only need `gcc`, `make`, and `musl` ``` -Debian-based systems: sudo apt install gcc make -Arch-based systems: sudo pacman -S gcc make +Debian-based systems: sudo apt install gcc make musl-tools +Arch-based systems: sudo pacman -S gcc make musl Alpine-based systems: apk add gcc make ``` @@ -50,5 +50,8 @@ git clone https://github.com/kxtzownsu/KVS # insiders, use KVS-private cd KVS -make kvg # final binary will be at ./build/kvg -``` \ No newline at end of file +make kvg # final binary will be at ./build/bin/kvg +``` + +# Cross-compiling +See [cross-compilation.md](cross-compilation.md). \ No newline at end of file diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..4313cb7 --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,67 @@ +# Using KVG (Kernel Version Generator) +Using KVG is very simple, all you need to know is if your device has v0 or v1. + +This can be figured out very easily, if your device is oldui (white mode) recovery, then its v0, + +if your device is newui (dark mode) recovery, then its v1.

+ +CLI Args: +``` +$ ./kvg +USAGE: ./kvg +e.g.: ./kvg 0x00010001 --raw --ver=0 +--raw - prints the output as raw hex bytes +--ver=<0/1> - specifies the kernver struct version to use +--help - shows this message :3 +KVG was created by kxtzownsu +(now written in Rust) +$ +``` + +To use V0 recovery, pass `--ver=0` onto the END of the command, after putting the binary, + +pass your flags, like this: + +`./kvg 0x00010001 --ver=0 --raw` + +NOT like this: + +`./kvg --raw --ver=0 0x00010001` + +Passing `--raw` will give you the raw hex output, instead of it printing like this `02 4c` it would print `\x2\x4c`. + +# Using KVS (Kernel Version Switcher) +One thing to note, if you aren't using cr50-hammer or RMASmoke, then you must be **UNENROLLED**! + +If you *are* using RMASmoke, make sure you're on Cr50 RW Version 0.5.229 or lower. + +If you *are* using cr50-hammer, make sure . + +If you *aren't* using RMASmoke or cr50-hammer, make sure to use the `tpm0 flash` method! + +## Examples: +***Flashing via tpm0 flash*** +``` +KVS: Kernel Version Switcher (codename Maglev, bid: 2.0.0)) +FW Version: Google_Grunt.11031.149.0 +Kernel Version: 0x00010001 +TPM: 2.0 +FWMP: 0x1 +GSC RW Version: 0.5.229 +GSC Type: Cr50 +-=-=-=-=-=-=-=-=-=-=-=-=- +1) Flash new kernver via /dev/tpm0 (REQ. UNENROLLED) +2) Flash new kernver via RMASmoke (REQ. CR50 VER 0.5.229 OR LOWER) +3) Make kernver index unwritable +4) Shell +5) Reboot +> 1 +What kernver would you like to flash? +> 0x00010001 +Does your device have lightmode (v0) or darkmode (v1) recovery? Please type either v0 or v1. +> v0 +writing 13 bytes... +Finished! Press ENTER to return to main menu +``` + + diff --git a/docs/cross-compilation.md b/docs/cross-compilation.md new file mode 100644 index 0000000..532a414 --- /dev/null +++ b/docs/cross-compilation.md @@ -0,0 +1,10 @@ +# Cross-compiling the KVS project +To cross-compile the KVS project, all you need to do is set `$ARCH` to a correct architecture, +and also install the cross-compilation libraries for your target arch. + +Heres an example: + +`ARCH=aarch64 make all` + +All architectures: +`x86_64, armv7, aarch64` \ No newline at end of file diff --git a/src/KVG/main.rs b/src/KVG/main.rs new file mode 100644 index 0000000..68ad9cd --- /dev/null +++ b/src/KVG/main.rs @@ -0,0 +1,185 @@ +use std::env; +use std::process; +use std::io::{self, Write}; + +const VB2_SHA256_DIGEST_SIZE: usize = 32; +const MAX_HEX_LENGTH: usize = 10; + +#[repr(C, packed)] +struct Vb2SecdataKernelV0 { + struct_version: u8, + uid: u32, + kernver: u32, + reserved: [u8; 3], + crc8: u8, +} + +#[repr(C, packed)] +struct Vb2SecdataKernelV1 { + struct_version: u8, + struct_size: u8, + crc8: u8, + flags: u8, + kernver: u32, + ec_hash: [u8; VB2_SHA256_DIGEST_SIZE], +} + +struct Vb2Context<'a> { + secdata_kernel: &'a [u8], +} + +fn vb2_crc8(data: &[u8]) -> u8 { + let mut crc: u16 = 0; + for byte in data { + crc ^= (*byte as u16) << 8; + for _ in 0..8 { + if crc & 0x8000 != 0 { + crc ^= 0x1070 << 3; + } + crc <<= 1; + } + } + (crc >> 8) as u8 +} + +fn is_v0(context: &Vb2Context) -> bool { + let major_version = (context.secdata_kernel[0] & 0xf0) >> 4; + major_version == 0 +} + +fn secdata_kernel_crc(context: &Vb2Context) -> u8 { + if is_v0(context) { + let size = std::mem::size_of::() - 1; // Exclude crc8 + vb2_crc8(&context.secdata_kernel[0..size]) + } else { + let struct_size = context.secdata_kernel[1] as usize; + let offset = 3; // Offset for flags in V1 + vb2_crc8(&context.secdata_kernel[offset..struct_size]) + } +} + +fn is_valid_hex(input: &str) -> bool { + if input.len() > MAX_HEX_LENGTH { + return false; + } + + if let Some(stripped) = input.strip_prefix("0x") { + stripped.chars().all(|c| c.is_digit(16)) + } else { + false + } +} + +fn sanitize_input(input: &str) -> String { + input.replace('\n', "\0") +} + +fn convert_to_u32(input: &str) -> u32 { + u32::from_str_radix(input.trim_start_matches("0x"), 16).unwrap_or_else(|_| { + eprintln!( + "The entered kernver, {}, was not valid hexadecimal. Please try again.", + input + ); + process::exit(1); + }) +} + +fn print_hex(data: &[u8]) { + for byte in data { + print!("{:02x} ", byte); + } + println!(); +} + +fn main() { + let args: Vec = env::args().collect(); + + if args.len() < 2 || args.contains(&"--help".to_string()) { + println!( + "USAGE: {} \n\ + e.g.: {} 0x00010001 --raw\n\ + --raw - prints the output as raw hex bytes\n\ + --ver=<0/1> - specifies the kernver struct version to use\n\ + --help - shows this message :3\n\ + KVG was created by kxtzownsu\n\ + (now written in Rust)", + args[0], args[0] + ); + process::exit(0); + } + + let kernver = sanitize_input(&args[1]); + if !is_valid_hex(&kernver) { + eprintln!( + "The entered kernver: {}, wasn't detected as valid hexadecimal (max 10 characters). Please try again.", + kernver + ); + process::exit(1); + } + let kvarg = convert_to_u32(&kernver); + + let version = args.iter().find(|arg| arg.starts_with("--ver=")).map_or("", |v| &v[6..]); + if version != "0" && version != "1" { + eprintln!( + "The entered struct version: {}, wasn't valid (see --help). Please try again.", + version + ); + process::exit(1); + } + + let mut buffer = Vec::new(); + if version == "0" { + let mut secdata0 = Vb2SecdataKernelV0 { + struct_version: 0x02, + uid: 0x4752574c, + kernver: kvarg, + reserved: [0x00; 3], + crc8: 0, + }; + let ctx = Vb2Context { + secdata_kernel: unsafe { + std::slice::from_raw_parts( + &secdata0 as *const _ as *const u8, + std::mem::size_of::(), + ) + }, + }; + secdata0.crc8 = secdata_kernel_crc(&ctx); + buffer.extend_from_slice(unsafe { + std::slice::from_raw_parts( + &secdata0 as *const _ as *const u8, + std::mem::size_of::(), + ) + }); + } else if version == "1" { + let mut secdata1 = Vb2SecdataKernelV1 { + struct_version: 0x10, + struct_size: std::mem::size_of::() as u8, + crc8: 0, + flags: 0x0, + kernver: kvarg, + ec_hash: [0; VB2_SHA256_DIGEST_SIZE], + }; + let ctx = Vb2Context { + secdata_kernel: unsafe { + std::slice::from_raw_parts( + &secdata1 as *const _ as *const u8, + std::mem::size_of::(), + ) + }, + }; + secdata1.crc8 = secdata_kernel_crc(&ctx); + buffer.extend_from_slice(unsafe { + std::slice::from_raw_parts( + &secdata1 as *const _ as *const u8, + std::mem::size_of::(), + ) + }); + } + + if args.contains(&"--raw".to_string()) { + io::stdout().write_all(&buffer).unwrap(); + } else { + print_hex(&buffer); + } +}