Compare commits

..

10 Commits

Author SHA1 Message Date
1c28a9520f e
Some checks failed
build all / build (push) Failing after 44s
2025-02-23 22:45:01 +00:00
7922bb06a3 remove fwver from info header (#8) 2025-01-30 12:51:13 -05:00
kxtz
b88c83d4d5
Update USAGE.md 2025-01-28 22:28:48 -05:00
kxtz
a11c95787a
stupid workflow 2025-01-28 21:41:22 -05:00
kxtz
6556774140
Update README.md 2025-01-28 21:39:56 -05:00
c98551ce1c update shim link 2025-01-28 18:05:04 +00:00
8d8f390e98 bug fixes (kvs 2.0.0-rc)
- add debug printing if `--debug` or `-d` is passed to the KVS binary
- remove old RMASmoke references
- Add Darkn to credits for testing
- update arg_checks library
- add error checking in sysinfo
2025-01-28 17:50:03 +00:00
67068807a7 floper 2025-01-17 17:43:01 +00:00
4e4dbc7d10 match with kvs-private repo 2025-01-17 14:35:29 +00:00
7a3a986ea6 skid-proof 2024-12-29 18:19:19 +00:00
15 changed files with 130 additions and 91 deletions

View File

@ -2,9 +2,9 @@ name: build all
on: on:
push: push:
branches: [ "2.0" ] branches: [ "v2.0" ]
pull_request: pull_request:
branches: [ "2.0" ] branches: [ "v2.0" ]
jobs: jobs:
build: build:

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
build/ build/
target/ target/
Cargo.lock Cargo.lock
*.bin*
# Prerequisites # Prerequisites
*.d *.d

View File

@ -5,7 +5,6 @@ SHELL ?= /bin/sh
KVSFLIST := \ KVSFLIST := \
src/KVS/main.c \ src/KVS/main.c \
src/KVS/tpm.c \ src/KVS/tpm.c \
src/KVS/rmasmoke.c \
src/KVS/ui.c \ src/KVS/ui.c \
src/KVS/hex_utils.c src/KVS/hex_utils.c
@ -21,7 +20,6 @@ TOOL_BINS := $(patsubst %,build/$(ARCH)/tools/%,$(TOOLS))
CFLAGS := \ CFLAGS := \
-Iinclude \ -Iinclude \
-g \ -g \
-O3 \
-Llib \ -Llib \
-static -static
@ -41,6 +39,8 @@ all: clean build kvs kvg tools
kvs: build build/$(ARCH)/bin/kvs kvs: build build/$(ARCH)/bin/kvs
kvg: build build/$(ARCH)/bin/kvg kvg: build build/$(ARCH)/bin/kvg
.PHONY: build
tools: build $(TOOL_BINS) tools: build $(TOOL_BINS)
build/$(ARCH)/tools/%: build/$(ARCH)/tools/%.o build/$(ARCH)/tools/%: build/$(ARCH)/tools/%.o
@ -68,5 +68,9 @@ install:
cp -r build/* /usr/local/ cp -r build/* /usr/local/
clean: clean:
rm -rf build/$(ARCH)
rm -rf target/$(TARGET)
deepclean:
rm -rf build rm -rf build
rm -rf target rm -rf target

View File

@ -1,11 +1,13 @@
# kvs # kvs
KVS: Kernel Version Switcher (anti-rollback rollbacker) KVS: Kernel Version Switcher (anti-rollback rollbacker)
<br> <br>
[![build kvs](https://github.com/kxtzownsu/KVS-private/actions/workflows/kvs.yaml/badge.svg)](https://github.com/kxtzownsu/KVS-private/actions/workflows/kvs.yaml) [![build all](https://github.com/kxtzownsu/KVS/actions/workflows/build.yaml/badge.svg)](https://github.com/kxtzownsu/KVS/actions/workflows/build.yaml)
[![build kvg](https://github.com/kxtzownsu/KVS-private/actions/workflows/kvg.yaml/badge.svg)](https://github.com/kxtzownsu/KVS-private/actions/workflows/kvg.yaml)
<sub> my first real C project, the code may look like shit, dont get mad at me because of it! :3 </sub> <sub> my first real C project, the code may look like shit, dont get mad at me because of it! </sub>
<sub> github is a mirror, source is at https://git.kxtz.dev/kxtzownsu/KVS </sub>
> [!IMPORTANT]
> This requires an unenrolled device. Any errors relating to being enrolled will be ignored and closed.
## Build Instructions ## Build Instructions
1) Clone the repo: <br /> 1) Clone the repo: <br />
@ -44,3 +46,4 @@ Any legal trouble you recieve due to possessing a raw shim for KVS is not my res
## Credits ## Credits
kxtzownsu - writing KVS & KVG, porting to C <br /> kxtzownsu - writing KVS & KVG, porting to C <br />
hannah - writing the `is_ti50` tool, moral support, testing <br /> hannah - writing the `is_ti50` tool, moral support, testing <br />
Darkn - testing

View File

@ -12,7 +12,7 @@ USAGE: ./kvg <kernver> <optional flags>
e.g.: ./kvg 0x00010001 --raw --ver=0 e.g.: ./kvg 0x00010001 --raw --ver=0
--raw - prints the output as raw hex bytes --raw - prints the output as raw hex bytes
--ver=<0/1> - specifies the kernver struct version to use --ver=<0/1> - specifies the kernver struct version to use
--help - shows this message :3 --help - shows this message
KVG was created by kxtzownsu KVG was created by kxtzownsu
(now written in Rust) (now written in Rust)
$ $
@ -31,13 +31,7 @@ NOT like this:
Passing `--raw` will give you the raw hex output, instead of it printing like this `02 4c` it would print `\x2\x4c`. 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) # Using KVS (Kernel Version Switcher)
One thing to note, if you aren't using cr50-hammer or RMASmoke, then you must be **UNENROLLED**! 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 <fill in later when cr50-hammer patch>.
If you *aren't* using RMASmoke or cr50-hammer, make sure to use the `tpm0 flash` method!
## Examples: ## Examples:
***Flashing via tpm0 flash*** ***Flashing via tpm0 flash***
@ -50,18 +44,14 @@ FWMP: 0x1
GSC RW Version: 0.5.229 GSC RW Version: 0.5.229
GSC Type: Cr50 GSC Type: Cr50
-=-=-=-=-=-=-=-=-=-=-=-=- -=-=-=-=-=-=-=-=-=-=-=-=-
1) Flash new kernver via /dev/tpm0 (REQ. UNENROLLED) 1) Flash new kernver
2) Flash new kernver via RMASmoke (REQ. CR50 VER 0.5.229 OR LOWER) 2) Run KAUB
3) Make kernver index unwritable 3) Kernver FAQ
4) Shell 4) Credits
5) Reboot 5) Shell
6) Reboot
> 1 > 1
What kernver would you like to flash? What kernver would you like to flash?
> 0x00010001 > 0x00010001
Does your device have lightmode (v0) or darkmode (v1) recovery? Please type either v0 or v1. Press ENTER to return to main menu
> v0
writing 13 bytes...
Finished! Press ENTER to return to main menu
``` ```
<finish docs when RMASmoke & cr50-hammer release>

View File

@ -10,32 +10,35 @@
int gargc; int gargc;
char **gargv; char **gargv;
char *fval(const char *arg, int param) // fval("--parameter", 1) = "burger" (assuming --parameter burger was passed)
char *fval(const char *arg, const char *shorthand, int param)
{ {
for (int i = 0; i < gargc; i++) { for (int i = 0; i < gargc; i++) {
if (!strcmp(gargv[i], arg)) return gargv[i + param]; if (!strcmp(gargv[i], arg) || !strcmp(gargv[i], shorthand)) return gargv[i + param];
} }
return ""; return "";
} }
bool fbool(const char *arg) // fequals("--parameter"); = "burger" (assuming --parameter=burger was passed)
{
for (int i = 0; i < gargc; i++) {
if (!strcmp(gargv[i], arg)) return true;
}
return false;
}
char *fequals(const char *arg) char *fequals(const char *arg)
{ {
for (int i = 0; i < gargc; i++) { for (int i = 0; i < gargc; i++) {
if (!memcmp(gargv[i], arg, strlen(arg) - 1)) if (!memcmp(gargv[i], arg, strlen(arg) - 1))
return gargv[i] + strlen(arg) + 1; return gargv[i] + strlen(arg) + 1;
} }
return ""; return "";
} }
// fbool("--parameter") == true (assuming --parameter was passed)
bool fbool(const char *arg, const char *shorthand)
{
for (int i = 0; i < gargc; i++) {
if (!strcmp(gargv[i], arg) || !strcmp(gargv[i], shorthand)) return true;
}
return false;
}
#endif #endif

View File

View File

@ -15,20 +15,28 @@ const char *KERNVER_TYPE = "N/A. This is an error, please report at https://gith
const char* getFirmwareVersion(){ const char* getFirmwareVersion(){
// note, may not work on all chromebooks // note, may not work on all chromebooks
// I also don't wanna have to rely on the crossystem binary for it // I also don't wanna have to rely on the crossystem binary for it
FILE *fptr;
char stupidfile[] = "/sys/class/dmi/id/bios_version"; // i hate ChromeOS
fptr = fopen(stupidfile, "r"); FILE *fp;
#ifdef __x86_64__
char stupidfile[] = "/sys/class/platform/chromeos_acpi/FWID";
#elif defined(__aarch64__)
char stupidfile[] = "/proc/device-tree/firmware/chromeos/firmware-version";
#elif defined(__arm__)
char stupidfile[] = "/proc/device-tree/firmware/chromeos/firmware-version";
#endif
fp = fopen(stupidfile, "r");
static char firmwareVersion[1024]; static char firmwareVersion[1024];
if (fptr == NULL) { if (fp == NULL) {
printf("Error reading Firmware Version\n"); printf("Error reading Firmware Version\n");
printf("Please report as a bug at https://github.com/kxtzownsu/KVS\n"); printf("Please report as a bug at https://github.com/kxtzownsu/KVS\n");
sleep(86400); sleep(86400);
return "Error!"; return "Error!";
} }
fgets(firmwareVersion, 100, fptr); fgets(firmwareVersion, 100, fp);
fclose(fptr); fclose(fp);
trim_newline(firmwareVersion); trim_newline(firmwareVersion);
return firmwareVersion; return firmwareVersion;
@ -49,7 +57,13 @@ char* getKernver() {
char cmd[] = "tpmc read 0x1008 9 2>/dev/null"; char cmd[] = "tpmc read 0x1008 9 2>/dev/null";
static char output[26]; static char output[26];
FILE* fp = popen(cmd, "r"); FILE* fp = popen(cmd, "r");
fgets(output, sizeof(output), fp); if (fgets(output, sizeof(output), fp) == NULL) {
printf("Error reading kernver\n");
printf("Please report as a bug at https://github.com/kxtzownsu/KVS\n");
sleep(86400);
return "Error!";
}
fclose(fp); fclose(fp);
trim_newline(output); trim_newline(output);
@ -57,6 +71,7 @@ char* getKernver() {
static char kernver_str[18] = "0x00000000"; static char kernver_str[18] = "0x00000000";
// ewwww yucky i hate this // ewwww yucky i hate this
// bitshift stuff sucks so bad when looking at it
if (strncmp(output, "10", 2) == 0) { if (strncmp(output, "10", 2) == 0) {
printf("using v1.0\n"); printf("using v1.0\n");
@ -73,6 +88,7 @@ char* getKernver() {
KERNVER_TYPE = "v0"; KERNVER_TYPE = "v0";
} }
KERNVER_TYPE = "v0";
return kernver_str; return kernver_str;
} }
@ -93,12 +109,10 @@ const char* getFWMPFlags(){
static char fwmp_str[5]; static char fwmp_str[5];
if (num_parsed != 1) { if (num_parsed != 1) {
printf("Failed to parse FWMP value from output.\n"); return "Failed to parse FWMP value from output.";
return 0;
} }
snprintf(fwmp_str, sizeof(fwmp_str), "0x%02x", fwmp); snprintf(fwmp_str, sizeof(fwmp_str), "0x%02x", fwmp);
return fwmp_str; return fwmp_str;
} }
@ -106,18 +120,33 @@ const char* getGSCRWVersion(){
char cmd[] = "gsctool -a -f | tail -n 1 | awk '{printf $2}'"; char cmd[] = "gsctool -a -f | tail -n 1 | awk '{printf $2}'";
static char output[8]; static char output[8];
FILE* fp = popen(cmd, "r"); FILE* fp = popen(cmd, "r");
fgets(output, sizeof(output), fp); if (fgets(output, sizeof(output), fp) == NULL) {
printf("Error reading GSC(cr50/ti50) version\n");
printf("Please report as a bug at https://github.com/kxtzownsu/KVS\n");
sleep(86400);
return "Error!";
}
fclose(fp); fclose(fp);
trim_newline(output); trim_newline(output);
return output; return output;
} }
// this being at a pre-made directory instead of
// being in PATH or /bin is probably bad, but
// I don't really care that much
const char* getGSCType(){ const char* getGSCType(){
char cmd[] = "/opt/kvs/bin/is_ti50 2>/dev/null"; char cmd[] = "/opt/kvs/bin/is_ti50 2>/dev/null";
static char output[7]; static char output[7];
FILE* fp = popen(cmd, "r"); FILE* fp = popen(cmd, "r");
fgets(output, sizeof(output), fp); if (fgets(output, sizeof(output), fp) == NULL) {
printf("Error getting GSC(cr50/ti50) type!\n");
printf("Please report as a bug at https://github.com/kxtzownsu/KVS\n");
sleep(86400);
return "Error!";
}
fclose(fp); fclose(fp);
trim_newline(output); trim_newline(output);

View File

@ -2,7 +2,7 @@
#define UI_H #define UI_H
void ui_flash(char* flashtype); void ui_flash(char* flashtype);
void ui_header(const char* fwver, const char* kernver, const char* tpmver, const char* fwmp, const char* gscver, const char* gsctype); void ui_header(const char* kernver, const char* tpmver, const char* fwmp, const char* gscver, const char* gsctype);
void show_credits(); void show_credits();
void troll(); void troll();
void enterToContinue(); void enterToContinue();

View File

@ -3,7 +3,7 @@
SCRIPT_DIR=$(dirname "$0") SCRIPT_DIR=$(dirname "$0")
VERSION=2.0.0 VERSION=2.0.0
HOST_ARCH=$(lscpu | grep Architecture | awk '{print $2}') HOST_ARCH=$(arch)
if [ $HOST_ARCH == "x86_64" ]; then if [ $HOST_ARCH == "x86_64" ]; then
CGPT="$SCRIPT_DIR/bins/cgpt.x86-64" CGPT="$SCRIPT_DIR/bins/cgpt.x86-64"
SFDISK="$SCRIPT_DIR/bins/sfdisk.x86-64" SFDISK="$SCRIPT_DIR/bins/sfdisk.x86-64"

View File

@ -100,7 +100,7 @@ fn main() {
e.g.: {} 0x00010001 --raw\n\ e.g.: {} 0x00010001 --raw\n\
--raw - prints the output as raw hex bytes\n\ --raw - prints the output as raw hex bytes\n\
--ver=<0/1> - specifies the kernver struct version to use\n\ --ver=<0/1> - specifies the kernver struct version to use\n\
--help - shows this message :3\n\ --help - shows this message\n\
KVG was created by kxtzownsu\n\ KVG was created by kxtzownsu\n\
(now written in Rust)", (now written in Rust)",
args[0], args[0] args[0], args[0]

View File

@ -4,6 +4,7 @@
#include <unistd.h> #include <unistd.h>
#include "ui.h" #include "ui.h"
#include "sysinfo.h" #include "sysinfo.h"
#include "arg_checks.h"
void kernver_faq(){ void kernver_faq(){
printf( printf(
@ -18,14 +19,22 @@ void kernver_faq(){
); );
}; };
void dbgprintf(char* text){
if (fbool("--debug","-d")){
printf("DEBUG: %s\n", text);
}
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
gargc = argc;
gargv = argv;
if (geteuid() != 0){ if (geteuid() != 0){
printf("Please run KVS as root!\n"); printf("Please run KVS as root!\n");
exit(1); printf("This is a bug, please report it at https://github.com/kxtzownsu/KVS");
sleep(86400);
} }
// example values for testing // const char* fwver = getFirmwareVersion();
const char* fwver = getFirmwareVersion();
const char* tpmver = getTpmVersion(); const char* tpmver = getTpmVersion();
const char* fwmp = getFWMPFlags(); const char* fwmp = getFWMPFlags();
const char* gscver = getGSCRWVersion(); const char* gscver = getGSCRWVersion();
@ -34,12 +43,13 @@ int main(int argc, char **argv) {
// only allow 2 characters (option & newline) // only allow 2 characters (option & newline)
char choice[3]; char choice[3];
dbgprintf("ui loop \n");
while (true) { while (true) {
char* kernver = getKernver(); char* kernver = getKernver();
printf("\033[H\033[J"); // clears the screen printf("\033[H\033[J"); // clears the screen
ui_header(fwver, kernver, tpmver, fwmp, gscver, gsctype); ui_header(kernver, tpmver, fwmp, gscver, gsctype);
printf("1) Flash new kernver \n"); printf("1) Flash new kernver \n");
printf("2) Run KAUB (Kernver Automatic Update Blocker) \n"); printf("2) Run KAUB (Kernver Automatic Update Blocker) \n");
printf("3) Kernver FAQ \n"); printf("3) Kernver FAQ \n");
@ -71,6 +81,10 @@ int main(int argc, char **argv) {
exit(1); exit(1);
} else if (!strcmp(choice, "7")) { } else if (!strcmp(choice, "7")) {
troll(); troll();
} else {
printf("You have entered an invalid option... how?? Next time, only input the number.\n");
printf("Example: > 1\n");
enterToContinue();
} }
} }

View File

View File

@ -24,55 +24,49 @@ void ui_flash(char* flashtype) {
printf("What kernver would you like to flash? \n"); printf("What kernver would you like to flash? \n");
printf("> "); printf("> ");
fgets(kerninput, sizeof(kerninput), stdin); fgets(kerninput, sizeof(kerninput), stdin);
// nya
if (kerninput[strlen(kerninput) - 1] == '\n') { if (kerninput[strlen(kerninput) - 1] == '\n') {
kerninput[strlen(kerninput) - 1] = '\0'; kerninput[strlen(kerninput) - 1] = '\0';
} }
if (!is_valid_hex(kerninput)){ if (!is_valid_hex(kerninput)){
fprintf(stderr, "Your kernver, %s, was an invalid input. Not hex.", kerninput); fprintf(stderr, "Your kernver, %s, was an invalid input, not hex. A valid input would be: 0x00010001", kerninput);
exit(1); exit(1);
} } else {
// the output of strcmp if it fails is True
if (strcmp(KERNVER_TYPE, "v0") && strcmp(KERNVER_TYPE, "v1")){
// the reason we're not redirecting the user to the issues page is because if KERNVER_TYPE
// isn't detected as v0 or v1 in sysinfo.h, it'll do that already
fprintf(stderr, "%s", KERNVER_TYPE);
sleep(86400);
}
// the output of strcmp if it fails is True // we check if its *false* since strcmp returns true if failing
if (strcmp(KERNVER_TYPE, "v0") && strcmp(KERNVER_TYPE, "v1")){ if (!strcmp(KERNVER_TYPE, "v0")){
fprintf(stderr, KERNVER_TYPE); char cmd[128];
exit(1);
}
// we check if its *false* since strcmp returns true if failing snprintf(cmd, sizeof(cmd), "kvg %s --ver=0", kerninput);
if (!strcmp(KERNVER_TYPE, "v0")){ FILE* fp = popen(cmd, "r");
char cmd[128]; fgets(kvgout_v0, sizeof(kvgout_v0), fp);
fclose(fp);
} else if (!strcmp(KERNVER_TYPE, "v1")) {
char cmd[128];
snprintf(cmd, sizeof(cmd), "kvg %s --ver=0", kerninput); snprintf(cmd, sizeof(cmd), "kvg %s --ver=1", kerninput);
FILE* fp = popen(cmd, "r"); FILE* fp = popen(cmd, "r");
fgets(kvgout_v0, sizeof(kvgout_v0), fp); fgets(kvgout_v1, sizeof(kvgout_v1), fp);
fclose(fp); fclose(fp);
} else if (!strcmp(KERNVER_TYPE, "v1")) { }
char cmd[128];
snprintf(cmd, sizeof(cmd), "kvg %s --ver=1", kerninput);
FILE* fp = popen(cmd, "r");
fgets(kvgout_v1, sizeof(kvgout_v1), fp);
fclose(fp);
}
if (flashtype == "tpm0"){
if (!strcmp(KERNVER_TYPE, "v0")) { if (!strcmp(KERNVER_TYPE, "v0")) {
tpm_nvwrite("0x1008", kvgout_v0); tpm_nvwrite("0x1008", kvgout_v0);
} else if (!strcmp(KERNVER_TYPE, "v1")) { } else if (!strcmp(KERNVER_TYPE, "v1")) {
tpm_nvwrite("0x1008", kvgout_v1); tpm_nvwrite("0x1008", kvgout_v1);
} }
} else if (flashtype == "rmasmoke"){
printf("using rmasmoke\n");
} }
} }
void ui_header(const char* fwver, char* kernver, const char* tpmver, const char* fwmp, const char* gscver, const char* gsctype){ void ui_header(char* kernver, const char* tpmver, const char* fwmp, const char* gscver, const char* gsctype){
printf("KVS: Kernel Version Switcher (codename Maglev, bid: 2.0.0)\n"); printf("KVS: Kernel Version Switcher (codename Maglev, bid: 2.0.0)\n");
printf("FW Version: %s\n", fwver); printf("Kernver: %s\n", kernver);
printf("Kernel Version: %s\n", kernver);
printf("TPM: %s\n", tpmver); printf("TPM: %s\n", tpmver);
printf("FWMP: %s\n", fwmp); printf("FWMP: %s\n", fwmp);
printf("GSC RW Version: %s\n", gscver); printf("GSC RW Version: %s\n", gscver);
@ -83,6 +77,7 @@ void ui_header(const char* fwver, char* kernver, const char* tpmver, const char*
void show_credits(){ void show_credits(){
printf("kxtzownsu - Writing KVS 1 and 2\n"); printf("kxtzownsu - Writing KVS 1 and 2\n");
printf("Hannah/ZegLol - writing is_ti50, mental support, testing\n"); printf("Hannah/ZegLol - writing is_ti50, mental support, testing\n");
printf("Darkn - testing\n");
} }
void troll(){ void troll(){

View File

@ -31,8 +31,9 @@
<p>This works using the hexdumps of 0x1008 (kernver TPM index) and <code>tpmc</code> to write the hexdumps of your selected kernver to the 0x1008 TPM index.</p> <p>This works using the hexdumps of 0x1008 (kernver TPM index) and <code>tpmc</code> to write the hexdumps of your selected kernver to the 0x1008 TPM index.</p>
</div> </div>
<div class="installation"> <div class="installation">
<p><b>YOU MUST BE UNENROLLED TO USE KVS! ANY ISSUES REPORTED RELATING TO BEING ENROLLED WHILE TRYING TO USE THIS WILL GET CLOSED IMMEDIATELY</b></p>
<h3>How do I use this?</h3> <h3>How do I use this?</h3>
<p>To use KVS, you must download your shim from <a href="https://dl.kxtz.dev/shims/KVS/">kxtz' shim mirror</a></p> <p>To use KVS, you must download your shim from <a href="https://dl.kxtz.dev/ChromeOS/shims/KVS/">kxtz' shim mirror</a></p>
<p>After downloading, flash your USB/SD with the file, I recommend Chrome Recovery Utility.</p> <p>After downloading, flash your USB/SD with the file, I recommend Chrome Recovery Utility.</p>
</div> </div>
<div class="faq"> <div class="faq">
@ -46,7 +47,6 @@
<p>kernver 0: any</p> <p>kernver 0: any</p>
<p>kernver 1: any</p> <p>kernver 1: any</p>
<p>kernver 2: 112 - 119</p> <p>kernver 2: 112 - 119</p>
<p>kernver 3: 120 - 124</p>
<p>kernver 4: 125 - latest</p> <p>kernver 4: 125 - latest</p>
<h4><b>Q: </b>What is the difference between kernver 0 and kernver 1?</h4> <h4><b>Q: </b>What is the difference between kernver 0 and kernver 1?</h4>
<p><b>A: </b>literally none, its just cool to see</p> <p><b>A: </b>literally none, its just cool to see</p>
@ -55,7 +55,7 @@
<h3>Credits</h3> <h3>Credits</h3>
<p><b>kxtzownsu</b> - Writing KVS</p> <p><b>kxtzownsu</b> - Writing KVS</p>
<p><b>OlyB</b> - Helping me with the shim builder, most of the shim builder wouldn't exist without him.</p> <p><b>OlyB</b> - Helping me with the shim builder, most of the shim builder wouldn't exist without him.</p>
<p><b>Google</b> - Writing the <code>tpmc</code> command :3</p> <p><b>Google</b> - Writing the <code>tpmc</code> command</p>
</div> </div>
<div style="padding-bottom: 3%;"></div> <div style="padding-bottom: 3%;"></div>
</div> </div>