added untested windows support

This commit is contained in:
Oscar Krause
2025-04-17 07:13:15 +02:00
parent dceefe032b
commit e6988a7a28
5 changed files with 244 additions and 65 deletions

View File

@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-License-Identifier: GPL-3.0-or-later
cmake_minimum_required(VERSION 3.5)
@@ -24,11 +24,13 @@ if (LIEF_SRC_ADDED)
)
set(LIEF_CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DLIEF_EXAMPLES=off
-DLIEF_ENABLE_JSON=off
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DLIEF_MACHO=off
-DLIEF_DEX=off
)
ExternalProject_Add(LIEF

View File

@@ -4,8 +4,6 @@ if [ "$1" == "clean" ] || [ "$1" == "rebuild" ]; then
rm -rf build gridd-unlock-patcher
fi
if [ "$1" == "rebuild" ] || [ "$1" != "clean" ]; then
mkdir -p build; cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && make -j$(nproc) && mv gridd-unlock-patcher ..
cd ..
if [ -z "$1" ] || [ "$1" == "rebuild" ] || [ "$1" == "build" ]; then
cmake -DCMAKE_BUILD_TYPE=Release -B build && make -j$(nproc) -C build && mv build/gridd-unlock-patcher .
fi

View File

@@ -1,31 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* SPDX-License-Identifier: GPL-3.0-or-later */
#undef NDEBUG
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <LIEF/ELF.hpp>
#include <LIEF/PE.hpp>
#include "nls-root_ca-certificates.hpp"
using namespace LIEF::ELF;
using namespace LIEF;
bool is_linux_guest;
// TODO: `mmap`ing this might be lighter
FILE *gridd_filep = NULL;
uint8_t *gridd_data = NULL;
FILE *cert_fp = NULL;
void *user_root_ca = NULL;
char *user_root_ca = NULL;
const char *gridd_hardcoded_cert_one;
const char *gridd_hardcoded_cert_two;
void usage(char *program_name)
{
printf("Usage: %s [OPTIONS]\n", program_name);
printf("\n"
" -h\n"
" -g <nvidia-gridd binary>\n"
" -c <root CA certificate>\n"
"\n");
printf("Usage: %s [OPTIONS]\n\n", program_name);
printf("Options:\n"
"\t-h\tShow this help message.\n"
"\t-g\t<GRID executable>\n"
"\t-c\t<root CA certificate>\n\n");
printf("For Windows guests, the GRID executable is Display.Driver/nvxdapix.dll.\n");
printf("For Linux guests, the GRID executable is nvidia-gridd.\n");
}
int parse_args(int argc, char *argv[])
int initialise_patcher(int argc, char *argv[])
{
int opt;
@@ -39,6 +53,7 @@ int parse_args(int argc, char *argv[])
return -1;
case 'g':
gridd_filep = fopen(optarg, "rb+");
is_linux_guest = strstr(optarg, ".dll") == NULL;
break;
case 'c':
cert_fp = fopen(optarg, "rb");
@@ -51,6 +66,9 @@ int parse_args(int argc, char *argv[])
return -1;
}
gridd_hardcoded_cert_one = is_linux_guest ? gridd_hardcoded_cert_one_linux : gridd_hardcoded_cert_one_windows;
gridd_hardcoded_cert_two = is_linux_guest ? gridd_hardcoded_cert_two_linux : gridd_hardcoded_cert_two_windows;
return 0;
}
void cleanup(void)
@@ -65,12 +83,51 @@ void cleanup(void)
fclose(gridd_filep);
}
int read_user_cert(size_t *user_ca_size)
{
char *temp_buffer = (char *)malloc(*user_ca_size);
assert(temp_buffer != NULL);
size_t number_bytes = fread((void *)temp_buffer, *user_ca_size - 1, 1, cert_fp);
assert(number_bytes > 0);
// Make it a valid string
temp_buffer[*user_ca_size] = 0;
// Basic checks that the input is a PEM
if (strstr(temp_buffer, PEM_BEGIN_CERTIFICATE) == NULL|| strstr(temp_buffer, PEM_END_CERTIFICATE) == NULL)
return -1;
if (is_linux_guest) {
user_root_ca = temp_buffer;
} else {
*user_ca_size -= strlen(PEM_BEGIN_CERTIFICATE) + strlen(PEM_END_CERTIFICATE);
// Just allocate it all
user_root_ca = (char *)calloc(1, *user_ca_size);
assert(user_root_ca != NULL);
size_t skipped_bytes = 0;
for (int i = 0; i < *user_ca_size - 1; i++) {
if (temp_buffer[i + strlen(PEM_BEGIN_CERTIFICATE)] != '\n')
user_root_ca[i - skipped_bytes] = temp_buffer[i + strlen(PEM_BEGIN_CERTIFICATE)];
else
skipped_bytes++;
}
free(temp_buffer);
*user_ca_size -= skipped_bytes;
}
return 0;
}
int main(int argc, char *argv[])
{
struct stat gridd_fp_stats, cert_fp_stats;
size_t status;
if (parse_args(argc, argv) == -1) {
printf("gridd-unlock-patcher: Patching GRID daemons with custom NLS certificates.\n");
if (initialise_patcher(argc, argv) == -1) {
cleanup();
return -1;
}
@@ -81,57 +138,104 @@ int main(int argc, char *argv[])
gridd_data = (uint8_t *)malloc(gridd_fp_stats.st_size);
assert(gridd_data != NULL);
status = fread((void *)gridd_data, gridd_fp_stats.st_size, 1, gridd_filep);
assert(status > 0);
size_t number_bytes = fread((void *)gridd_data, gridd_fp_stats.st_size, 1, gridd_filep);
assert(number_bytes > 0);
// Parse the ELF
// Parse the binary
std::vector<uint8_t> gridd_vec(gridd_data, gridd_data + gridd_fp_stats.st_size);
auto gridd_bin = Parser::parse(gridd_vec);
auto s_rodata = gridd_bin->get_section(".rodata");
auto s_data = gridd_bin->get_section(".data");
// There are two hardcoded certificates
size_t cert_one_offset = s_rodata->search(gridd_hardcoded_cert_one);
size_t cert_two_offset = s_rodata->search(gridd_hardcoded_cert_two);
printf("Found the two hardcoded NLS certificates at 0x%x and 0x%x.\n", cert_one_offset, cert_two_offset);
/* Validate the size of the provided root CA */
fstat(fileno(cert_fp), &cert_fp_stats);
// FIXME: Align up to cover the padding?
size_t total_cert_size = cert_two_offset + (strlen(gridd_hardcoded_cert_two) + 1) - cert_one_offset;
if (cert_fp_stats.st_size > total_cert_size) {
printf("The provided certificate (size %d) is larger than the available space (size %d)!\n",
cert_fp_stats.st_size, total_cert_size);
std::unique_ptr<LIEF::ELF::Binary> gridd_elf = nullptr;
std::unique_ptr<LIEF::PE::Binary> gridd_dll = nullptr;
if (is_linux_guest)
gridd_elf = ELF::Parser::parse(gridd_vec);
else
gridd_dll = PE::Parser::parse(gridd_vec);
if (!gridd_elf && !gridd_dll) {
printf("Failed to parse GRID executable! Are you sure it's either an ELF or a PE?\n");
cleanup();
return -1;
}
/* Patch in the provided certificate */
// Read the provided root CA
user_root_ca = malloc(cert_fp_stats.st_size);
assert(user_root_ca != NULL);
uint64_t image_base = is_linux_guest ? gridd_elf->imagebase() : gridd_dll->imagebase();
status = fread(user_root_ca, cert_fp_stats.st_size, 1, cert_fp);
assert(status > 0);
auto s_rdata = is_linux_guest ? (LIEF::Section *)gridd_elf->get_section(".rodata")
: (LIEF::Section *)gridd_dll->get_section(".rdata");
auto s_data = is_linux_guest ? (LIEF::Section *)gridd_elf->get_section(".data")
: (LIEF::Section *)gridd_dll->get_section(".data");
if (!s_rdata || !s_data) {
printf("Failed to parse GRID executable! Are you sure it's a GRID executable?\n");
cleanup();
return -1;
}
// There are two hardcoded certificates
size_t cert_one_offset = s_rdata->search(gridd_hardcoded_cert_one);
size_t cert_two_offset = s_rdata->search(gridd_hardcoded_cert_two);
if (cert_one_offset == -1 || cert_two_offset == -1) {
printf("Failed to find the hardcoded NLS certificates!\n");
cleanup();
return -1;
}
printf("Found the two hardcoded NLS certificates at 0x%x and 0x%x.\n",
s_rdata->offset() + cert_one_offset, s_rdata->offset() + cert_two_offset);
/* Patch in the provided certificate */
// Validate the size of the provided root CA
fstat(fileno(cert_fp), &cert_fp_stats);
size_t user_ca_size = cert_fp_stats.st_size + 1;
// TODO: Align up, we can use cert two's padding too
size_t total_cert_size = cert_two_offset + (strlen(gridd_hardcoded_cert_two) + 1) - cert_one_offset;
if (user_ca_size > total_cert_size) {
printf("The provided certificate (size %d) is larger than available space (size %d)!\n",
user_ca_size, total_cert_size);
cleanup();
return -1;
}
// Read the certificate in guest-native form.
if (read_user_cert(&user_ca_size) == -1) {
printf("Failed to process provided certificate! Are you sure it's an OpenSSL PEM?\n");
cleanup();
return -1;
}
// Overwrite the first certificate, NULL the second
uint8_t *cert_start = gridd_data + s_rodata->offset() + cert_one_offset;
memcpy((void *)cert_start, user_root_ca, cert_fp_stats.st_size);
memset((void *)(cert_start + cert_fp_stats.st_size), 0, total_cert_size - cert_fp_stats.st_size);
// - TODO: Consider an insecure mode that doesn't check that the certificate matches a hardcoded root CA.
uint8_t *cert_start = gridd_data + s_rdata->offset() + cert_one_offset;
memcpy((void *)cert_start, (void *)user_root_ca, user_ca_size);
memset((void *)(cert_start + user_ca_size), 0, total_cert_size - user_ca_size);
printf("Replaced the hardcoded certificates with the provided one.\n");
// Erase the XREF to the second certificate
size_t cert_xrefs_array = s_data->offset() + s_data->search(s_rodata->virtual_address() + cert_one_offset);
// - TODO: This would be better if it searched for both. Alternatively, use Zydis to get XREFs into this
uint64_t search_target_base_addr = s_rdata->virtual_address();
if (!is_linux_guest)
search_target_base_addr += image_base;
uint64_t cert_xrefs_array = s_data->offset() + s_data->search(search_target_base_addr + cert_one_offset);
printf("Found the list of certificates at 0x%x.\n", cert_xrefs_array);
// TODO: PEs have some mapping oddities, and this is off by a bit. Consider not confusing users.
printf("Erasing the dangling reference to the old certificate at 0x%x (Ignore for Windows daemon).\n",
*(uint64_t *)(gridd_data + cert_xrefs_array + sizeof(uint64_t)) - image_base);
memset((void *)(gridd_data + cert_xrefs_array + sizeof(uint64_t)), 0, sizeof(uint64_t));
printf("Fixed up the list of certificates. Done!\n");
/* Make it HTTP/2 compliant */
// TODO: Theoretically, it'd be better to exchange `strstr` for `strcasestr`. But I'd rather be done for now.
size_t x_nls_sig_offset = s_rdata->search("X-NLS-Signature");
if (x_nls_sig_offset != -1) {
uint8_t *x_nls_sig_start = gridd_data + s_rdata->offset() + x_nls_sig_offset;
memcpy((void *)x_nls_sig_start, "x-nls-signature", strlen("x-nls-signature"));
printf("Patched the HTTP header \"X-NLS-Signature\" for HTTP/2 compliance.\n");
}
// Write the nvidia-gridd binary
FILE *test = fopen("gridd_bin", "wb");
fwrite((void *)gridd_data, gridd_fp_stats.st_size, 1, test);
fclose(test);
fseek(gridd_filep, 0, SEEK_SET);
fwrite((void *)gridd_data, gridd_fp_stats.st_size, 1, gridd_filep);
cleanup();
printf("Done!\n");
return 0;
}

View File

@@ -1,6 +1,77 @@
const char *gridd_hardcoded_cert_one =
"-----BEGIN CERTIFICATE-----\n"
#define PEM_BEGIN_CERTIFICATE "-----BEGIN CERTIFICATE-----\n"
#define PEM_END_CERTIFICATE "\n-----END CERTIFICATE-----"
const char *gridd_hardcoded_cert_one_windows =
"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"
"bBj+W2v1REPS";
const char *gridd_hardcoded_cert_two_windows =
"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"
"JQ6S82i+o497";
const char *gridd_hardcoded_cert_one_linux =
PEM_BEGIN_CERTIFICATE
"MIIF1TCCA72gAwIBAgIUIcPuKZE8a/r0ORUPGs9JBxufz5QwDQYJKoZIhvcNAQEL\n"
"BQAwcjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNVBAoT\n"
"Bk52aWRpYTEnMCUGA1UECxMeTnZpZGlhIExpY2Vuc2luZyBTZXJ2aWNlIChOTFMp\n"
@@ -32,11 +103,11 @@ const char *gridd_hardcoded_cert_one =
"EWQkzanncetmttAyrtdbIg2HuSnNqGOe/BhqlzPUxo0V+atqc2zGnDQ8hW2uEXZr\n"
"wNYj2njFpyEs6J1++w0LqSBl4kxzCjw+o9+wrS/g5MujGrYxRbPjH1bEh3i5TC1w\n"
"RVBUXnPBBEHgld6+d57FwZevbpXJZ0SOVFq7bb89/3VRAI1wLdTEPv+XZQfGmFzV\n"
"bBj+W2v1REPS\n"
"-----END CERTIFICATE-----";
"bBj+W2v1REPS"
PEM_END_CERTIFICATE;
const char *gridd_hardcoded_cert_two =
"-----BEGIN CERTIFICATE-----\n"
const char *gridd_hardcoded_cert_two_linux =
PEM_BEGIN_CERTIFICATE
"MIIF1TCCA72gAwIBAgIUSnRB0/9eXcT08pxOgXW9L28a8SkwDQYJKoZIhvcNAQEL\n"
"BQAwcjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNVBAoT\n"
"Bk52aWRpYTEnMCUGA1UECxMeTnZpZGlhIExpY2Vuc2luZyBTZXJ2aWNlIChOTFMp\n"
@@ -68,6 +139,6 @@ const char *gridd_hardcoded_cert_two =
"nljeVlB/v3U9VE3hg127wUixd/CXXOT1xQmGw7+jme6WUCYg51mxwM70c3OauWdq\n"
"q8I+Cw4qjLeWvN9NMQFOCCil5/BiVmI68zuZ0raZIEoaFFfg2cMsiE6Z1RUpnLsl\n"
"EliSc/yEbSvgE0C+u27mEe5/sXHRYHApgmGEw0cCj3WlNKwrTCm5/XP5K6U89KCN\n"
"JQ6S82i+o497\n"
"-----END CERTIFICATE-----";
"JQ6S82i+o497"
PEM_END_CERTIFICATE;