Skip to content

Instantly share code, notes, and snippets.

@qwerty472123
Last active October 16, 2024 01:03
Show Gist options
  • Save qwerty472123/c2155683c6c4713bdf160cf8add3fe52 to your computer and use it in GitHub Desktop.
Save qwerty472123/c2155683c6c4713bdf160cf8add3fe52 to your computer and use it in GitHub Desktop.
Cert hooks for IDA Pro

In some scenarios, we may want IDA to trust certificates issued by users themselves, such as naim94a/lumen#124.

Here we provide so/dll on Linux and Windows, which can be injected into the IDA process to make it trust certificates placed in the directory where the main process is located, and named hexrays*.crt, hexvault*.crt, lumina*.crt, trusted_certs/*.crt.

Comparing with OpenLumina v0.3, this version does not rely on the export functions of ida.dll, so it can be injected independently. This is because some executables that also have the same certificate verification procedure, such as vault/lumina server/client, which do not have a plugin system. In addition, the Windows version supports "strange" root certificates, which are used by the server directly(lumen case) or trusted by the Windows certificate store.

Except for the version injected into ida_dll_shim, all codes supports IDA 9.0 Beta.

Linux

How

Thanks to the symbol lookup mechanism of GNU linker, we don't need to hook anything on Linux. We just need to load a so that exports the dlsym symbol, and the main process will use the replaced functions when searching for OpenSSL symbols through our dlsym.

The X509_STORE_add_cert will be changed, which will add more certificates from custom positions.

Resources

source: injector.cpp, compiled: injector.so

Usage

  1. Use LD_PRELOAD to inject: LD_PRELOAD=./injector.so lc ...
  2. Use patchelf to work for eternity: patchelf --add-needed \$ORIGIN/injector.so ./lc, and then put injector.so at the same path.

Windows

How

Using the minhook hook CertGetCertificateChain and CertAddEncodedCertificateToStore functions.

Add custom certificates in CertAddEncodedCertificateToStore and fake the return value for CertGetCertificateChain if it's required and the certificate is trustable.

Resources

source: CertInjector.cpp, DLLMain.cpp, compiled: injector64.dll, or ida.dll and ida64.dll used for ida_dll_shim(8.3 version)

dependency: https://www.nuget.org/packages/minhook

Usage

  1. Insert the codes into https://github.com/x0rloser/ida_dll_shim, the injector will run when the dll wrapper be loaded, work for IDA8.3 only.
  2. As a plugin(This code can export PLUGIN which can be identified by IDA, but not depends on its code), put it to IDA's plugins folder, work for IDA only.
  3. Use PE Editor to add an import in import directory(import the plugin dll plugins\injector64.dll and require the symbol PLUGIN)

Note: for 2 and 3, define STANDALONE marco in source code.

New Certificate

One self-signed certificate can be issued by following instruction:

openssl ecparam -name prime256v1 -genkey -out test.key
openssl req -new -x509 -key test.key -out test.crt -days 3652 -config test.cnf -extensions v3_req

with test.cnf:

[ req ]
default_bits = 2048
default_keyfile = ./test.pem
default_md = sha256
prompt = no
distinguished_name = root_ca_distinguished_name
x509_extensions = root_ca_extensions
req_extensions = v3_req

[ root_ca_distinguished_name ]
countryName = BE
organizationName = Test Org.
commonName = vault.test.com

[ root_ca_extensions ]
basicConstraints = CA:true

[ v3_req ]
basicConstraints = CA:true
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyCertSign, cRLSign
extendedKeyUsage = serverAuth, clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
subjectAltName = @alt_names

[ alt_names ]
IP.1 = 127.0.0.1
IP.2 = ::1
DNS.1 = localhost
DNS.2 = vault.test.com
#include "CertInjector.h"
#include "MinHook.h"
#include <string>
#include <unordered_set>
#define COUNT_OF(arr) (sizeof(arr) / sizeof(*arr))
// #define PRINT_DEBUG
#ifdef PRINT_DEBUG
#define CONSOLE_DEBUG
#define SHOW_CERTS
#ifdef CONSOLE_DEBUG
#define DebugMsgA(caption, fmt, ...) (printf("[" caption "] " fmt "\r\n", __VA_ARGS__))
#define DebugMsgW(caption, fmt, ...) (_wprintf_p(L"[" caption L"] " fmt L"\r\n", __VA_ARGS__))
#else
#define _CRT_SECURE_NO_WARNINGS
#define DebugMsgA(caption, ...) do { char buf[500] = { 0 }; snprintf(buf, sizeof(buf), __VA_ARGS__); MessageBoxA(NULL, buf, caption, MB_OK); } while (0)
#define DebugMsgW(caption, ...) do { WCHAR buf[500] = { 0 }; _snwprintf_s(buf, sizeof(buf), __VA_ARGS__); MessageBoxW(NULL, buf, caption, MB_OK); } while (0)
#endif
#else
#define DebugMsgA(...)
#define DebugMsgW(...)
#endif
thread_local INT iActiveStage = 0;
static std::unordered_set<std::string> ussTrustedCertBuffers;
static decltype(&CertGetCertificateChain) fpCertGetCertificateChain;
static decltype(&CertAddEncodedCertificateToStore) fpCertAddEncodedCertificateToStore;
static BOOL WINAPI DetourCertGetCertificateChain(
HCERTCHAINENGINE hChainEngine,
PCCERT_CONTEXT pCertContext,
LPFILETIME pTime,
HCERTSTORE hAdditionalStore,
PCERT_CHAIN_PARA pChainPara,
DWORD dwFlags,
LPVOID pvReserved,
PCERT_CHAIN_CONTEXT *ppChainContext
)
{
BOOL bRet = fpCertGetCertificateChain(hChainEngine, pCertContext, pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, const_cast<PCCERT_CHAIN_CONTEXT *>(ppChainContext));
INT iCurrentActiveStage = iActiveStage;
if (iCurrentActiveStage == 1) iCurrentActiveStage = 0;
INT iToBeActiveStage = iCurrentActiveStage;
if (bRet && ppChainContext && *ppChainContext) {
DWORD dwErrorStatus = (*ppChainContext)->TrustStatus.dwErrorStatus;
BOOL bConsiderChange = (dwErrorStatus == CERT_TRUST_IS_PARTIAL_CHAIN || dwErrorStatus == CERT_TRUST_IS_UNTRUSTED_ROOT || dwErrorStatus == CERT_TRUST_NO_ERROR) && pCertContext && pCertContext->pbCertEncoded && pCertContext->cbCertEncoded;
DWORD dwCorrectStatus = iCurrentActiveStage == 0 ? CERT_TRUST_IS_PARTIAL_CHAIN : CERT_TRUST_IS_UNTRUSTED_ROOT;
DebugMsgA("Note", "bConsiderChange: %d, dwCorrectStatus: %d, iCurrentActiveStage: %d, need change: %d, detail: %u", bConsiderChange ? 1 : 0, dwCorrectStatus ? 1 : 0, iCurrentActiveStage, dwErrorStatus != dwCorrectStatus ? 1 : 0, dwErrorStatus);
if (dwErrorStatus == dwCorrectStatus) bConsiderChange = FALSE;
if (bConsiderChange) {
BOOL bCorrectable = ussTrustedCertBuffers.find(std::string(reinterpret_cast<char*>(pCertContext->pbCertEncoded), pCertContext->cbCertEncoded)) != ussTrustedCertBuffers.end();
DebugMsgA("Note", "count %d", (*ppChainContext)->cChain);
for (DWORD i = 0; !bCorrectable && i < (*ppChainContext)->cChain; i++) {
PCERT_SIMPLE_CHAIN pSimpleChain = (*ppChainContext)->rgpChain[i];
if (!pSimpleChain->cElement) continue;
PCCERT_CONTEXT pBaseContext = pSimpleChain->rgpElement[0]->pCertContext;
BOOL bStartWithCert = pCertContext->cbCertEncoded == pBaseContext->cbCertEncoded && !memcmp(pCertContext->pbCertEncoded, pBaseContext->pbCertEncoded, pCertContext->cbCertEncoded);
BOOL bHaveMatch = FALSE;
for (DWORD j = 0; j < pSimpleChain->cElement; j++) {
PCCERT_CONTEXT pCurContext = pSimpleChain->rgpElement[j]->pCertContext;
if (ussTrustedCertBuffers.find(std::string(reinterpret_cast<char*>(pCurContext->pbCertEncoded), pCurContext->cbCertEncoded)) != ussTrustedCertBuffers.end()) {
bHaveMatch = TRUE;
#ifndef SHOW_CERTS
break;
#endif
}
#ifdef SHOW_CERTS
DWORD cbString = 0;
if (!CryptBinaryToStringA(pCurContext->pbCertEncoded, pCurContext->cbCertEncoded, CRYPT_STRING_BASE64HEADER, NULL, &cbString)) {
DebugMsgA("Note", "CryptBinaryToStringA failed");
}
LPSTR pbString = new char[cbString + 10];
if (!CryptBinaryToStringA(pCurContext->pbCertEncoded, pCurContext->cbCertEncoded, CRYPT_STRING_BASE64HEADER, pbString, &cbString)) {
DebugMsgA("Note", "CryptBinaryToStringA failed");
}
pbString[cbString] = 0;
DebugMsgA("Cert", "%d:\r\n%s", j, pbString);
delete[] pbString;
#endif
}
DebugMsgA("Note", "start: %d, elements: %d, have match: %d", bStartWithCert ? 1 : 0, (int)pSimpleChain->cElement, bHaveMatch);
if (bStartWithCert && bHaveMatch) {
bCorrectable = TRUE;
}
}
if (bCorrectable) {
(*ppChainContext)->TrustStatus.dwErrorStatus = dwCorrectStatus;
DebugMsgA("Note", "changed, bCorrectable: %d", bCorrectable);
}
}
if (iToBeActiveStage == 0 && (*ppChainContext)->TrustStatus.dwErrorStatus == CERT_TRUST_IS_PARTIAL_CHAIN) iToBeActiveStage = 1;
else iToBeActiveStage = 0;
}
else iToBeActiveStage = 0;
if (iToBeActiveStage != iCurrentActiveStage) {
iActiveStage = iToBeActiveStage;
}
return bRet;
}
static BOOL WINAPI DetourCertAddEncodedCertificateToStore(
HCERTSTORE hCertStore,
DWORD dwCertEncodingType,
const BYTE* pbCertEncoded,
DWORD cbCertEncoded,
DWORD dwAddDisposition,
PCCERT_CONTEXT* ppCertContext
)
{
BOOL bRet = fpCertAddEncodedCertificateToStore(hCertStore, dwCertEncodingType, pbCertEncoded, cbCertEncoded, dwAddDisposition, ppCertContext);
if (bRet && iActiveStage == 1) {
iActiveStage = 2;
for (auto&& sCertBuffer : ussTrustedCertBuffers) {
fpCertAddEncodedCertificateToStore(hCertStore, dwCertEncodingType, reinterpret_cast<const BYTE *>(sCertBuffer.data()), static_cast<DWORD>(sCertBuffer.size()), dwAddDisposition, ppCertContext);
}
}
else iActiveStage = 0;
return bRet;
}
static void AddCertFile(LPCWSTR wszCertCrtPath)
{
HANDLE hFileHandle = CreateFileW(wszCertCrtPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hFileHandle == INVALID_HANDLE_VALUE) return;
DebugMsgW(L"Adding Cert", L"%s", wszCertCrtPath);
DWORD cbCertBufferSize = GetFileSize(hFileHandle, NULL);
char* pbCertBuffer = static_cast<char*>(malloc(cbCertBufferSize));
if (!ReadFile(hFileHandle, pbCertBuffer, cbCertBufferSize, &cbCertBufferSize, 0))
{
free(pbCertBuffer);
CloseHandle(hFileHandle);
return;
}
if (pbCertBuffer && *pbCertBuffer == '-')
{
DWORD cbBinary = 0;
if (!CryptStringToBinaryA(reinterpret_cast<char*>(pbCertBuffer), cbCertBufferSize, CRYPT_STRING_BASE64HEADER, NULL, &cbBinary, NULL, NULL))
{
MessageBox(NULL, __TEXT("CryptStringToBinaryA Calc Length Failed"), __TEXT("IDA Cert Injector"), MB_OK);
}
char* pbCertDerBuffer = static_cast<char*>(malloc(cbBinary));
if (!CryptStringToBinaryA(reinterpret_cast<char*>(pbCertBuffer), cbCertBufferSize, CRYPT_STRING_BASE64HEADER, reinterpret_cast<BYTE*>(pbCertDerBuffer), &cbBinary, NULL, NULL))
{
MessageBox(NULL, __TEXT("CryptStringToBinaryA Convert Failed"), __TEXT("IDA Cert Injector"), MB_OK);
}
free(pbCertBuffer);
pbCertBuffer = pbCertDerBuffer;
cbCertBufferSize = cbBinary;
}
CloseHandle(hFileHandle);
ussTrustedCertBuffers.emplace(pbCertBuffer, pbCertBuffer + cbCertBufferSize);
}
void DoCertInjector()
{
WCHAR wszCertCrtPath[MAX_PATH];
if (!GetModuleFileNameW(NULL, wszCertCrtPath, COUNT_OF(wszCertCrtPath)))
{
MessageBox(NULL, __TEXT("Get Module File Name Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
WCHAR* pwcLastSep = wcsrchr(wszCertCrtPath, L'\\');
WCHAR* pwcLastPosixSep = wcsrchr(wszCertCrtPath, L'/');
if (pwcLastPosixSep > pwcLastSep)
{
pwcLastSep = pwcLastPosixSep;
}
if (pwcLastSep && *pwcLastSep)
{
pwcLastSep[1] = 0;
}
WCHAR wszSearchPattern[MAX_PATH] = { 0 };
if (!wcscpy_s(wszSearchPattern, wszCertCrtPath) && !wcscat_s(wszSearchPattern, L"*.crt")) {
WIN32_FIND_DATAW findData = { 0 };
HANDLE hFileFind = FindFirstFileW(wszSearchPattern, &findData);
if (INVALID_HANDLE_VALUE != hFileFind)
{
WCHAR* pwcAppendPtr = wszCertCrtPath + wcslen(wszCertCrtPath);
size_t szExtraSize = MAX_PATH - (pwcAppendPtr - wszCertCrtPath);
do
{
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
BOOL matched = FALSE;
for (const WCHAR* prefix : { L"hexrays", L"hexvault", L"lumina" }) {
if (!wcsncmp(prefix, findData.cFileName, wcslen(prefix))) {
matched = TRUE;
break;
}
}
if (matched && !wcscpy_s(pwcAppendPtr, szExtraSize, findData.cFileName)) {
AddCertFile(wszCertCrtPath);
}
}
} while (FindNextFileW(hFileFind, &findData));
FindClose(hFileFind);
*pwcAppendPtr = 0;
}
}
if (!wcscat_s(wszCertCrtPath, L"trusted_certs\\"))
{
if (!wcscpy_s(wszSearchPattern, wszCertCrtPath) && !wcscat_s(wszSearchPattern, L"*.crt")) {
WIN32_FIND_DATAW findData = { 0 };
HANDLE hFileFind = FindFirstFileW(wszSearchPattern, &findData);
if (INVALID_HANDLE_VALUE != hFileFind)
{
WCHAR* pwcAppendPtr = wszCertCrtPath + wcslen(wszCertCrtPath);
size_t szExtraSize = MAX_PATH - (pwcAppendPtr - wszCertCrtPath);
do
{
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
if (!wcscpy_s(pwcAppendPtr, szExtraSize, findData.cFileName)) {
AddCertFile(wszCertCrtPath);
}
}
} while (FindNextFileW(hFileFind, &findData));
FindClose(hFileFind);
}
}
}
if (MH_OK != MH_Initialize())
{
MessageBox(NULL, __TEXT("MH Hook Init Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_CreateHook(CertGetCertificateChain, &DetourCertGetCertificateChain,
reinterpret_cast<LPVOID*>(&fpCertGetCertificateChain)))
{
MessageBox(NULL, __TEXT("CertGetCertificateChain Hook Init Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_CreateHook(CertAddEncodedCertificateToStore, &DetourCertAddEncodedCertificateToStore,
reinterpret_cast<LPVOID*>(&fpCertAddEncodedCertificateToStore)))
{
MessageBox(NULL, __TEXT("CertAddEncodedCertificateToStore Hook Init Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_EnableHook(CertGetCertificateChain))
{
MessageBox(NULL, __TEXT("CertGetCertificateChain Hook Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_EnableHook(CertAddEncodedCertificateToStore))
{
MessageBox(NULL, __TEXT("CertAddEncodedCertificateToStore Hook Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
}
void UnDoCertInjector()
{
if (MH_OK != MH_DisableHook(CertAddEncodedCertificateToStore))
{
MessageBox(NULL, __TEXT("CertAddEncodedCertificateToStore Hook Disable Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_DisableHook(CertGetCertificateChain))
{
MessageBox(NULL, __TEXT("CertGetCertificateChain Hook Disable Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_RemoveHook(CertAddEncodedCertificateToStore))
{
MessageBox(NULL, __TEXT("CertAddEncodedCertificateToStore Hook Remove Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
if (MH_OK != MH_RemoveHook(CertGetCertificateChain))
{
MessageBox(NULL, __TEXT("CertGetCertificateChain Hook Remove Failed"), __TEXT("IDA Cert Injector"), MB_OK);
return;
}
ussTrustedCertBuffers.clear();
}
//
// ida.dll compatibility shim for IDA
// Check the readme.md for more info
//
// xorloser - November 2022
//
#include <Windows.h>
// Uncomment one of the following to produce a shim for that version of ida.
//
// SP1 etc versions are only provided if the SP version dll has different
// exports to the base version dll. This is not usually the case.
// If there is no SP version then use the base version. Eg v8.2sp1 is the
// same as v8.2, so to build a dll for v8.2sp1 just use IDA_820.
//
//#define IDA_700 1
//#define IDA_710 1
//#define IDA_710SP1 1
//#define IDA_720 1
//#define IDA_730 1
//#define IDA_740 1
//#define IDA_750 1
//#define IDA_760 1
//#define IDA_770 1
//#define IDA_800 1
//#define IDA_810 1
//#define IDA_820 1
#define IDA_830 1
#ifndef STANDALONE
#include "ida_dll_exports.h"
#else
#define __EA64__ 1
#define __NT__ 1
#define NDEBUG 1
#include <idp.hpp>
#include <loader.hpp>
static plugmod_t* idaapi init()
{
return PLUGIN_KEEP;
}
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
PLUGIN_HIDE | PLUGIN_FIX,
init,
nullptr,
nullptr,
"DLL Hijack gadget plugin",
nullptr,
"DLL Injector",
nullptr,
};
#endif
#include "CertInjector.h"
DWORD WINAPI ProcessAttach(
_In_ LPVOID Parameter
)
{
if ( Parameter == NULL )
return FALSE;
DoCertInjector();
return TRUE;
}
DWORD WINAPI ProcessDetach(
_In_ LPVOID Parameter
)
{
if ( Parameter == NULL )
return FALSE;
UnDoCertInjector();
return TRUE;
}
BOOL APIENTRY DllMain(
_In_ HINSTANCE Instance,
_In_ DWORD Reason,
_In_ LPVOID Reserved
)
{
switch ( Reason )
{
case DLL_PROCESS_ATTACH:
// Disable DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications
DisableThreadLibraryCalls( Instance );
return ProcessAttach( Instance );
case DLL_PROCESS_DETACH:
return ProcessDetach( Instance );
}
return TRUE;
}
// g++ -std=c++20 -fPIC -shared injector.cpp -O3 -o injector.so
#include <stdio.h>
#include <string.h>
#include <atomic>
#include <mutex>
#include <vector>
#include <filesystem>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <dlfcn.h>
#include <linux/limits.h>
namespace {
struct BIO;
struct X509;
struct X509_STORE;
struct SSL_CTX;
struct pem_password_cb;
struct spinlock_t {
pthread_spinlock_t spinlock;
spinlock_t() {
pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
}
~spinlock_t() {
pthread_spin_destroy(&spinlock);
}
void lock() {
pthread_spin_lock(&spinlock);
}
void unlock() {
pthread_spin_unlock(&spinlock);
}
bool try_lock() {
return pthread_spin_trylock(&spinlock) == 0;
}
};
}
static BIO *(*BIO_new_mem_buf_ptr)(const void *buf, int len) = NULL;
static int (*BIO_free_ptr)(BIO *a) = NULL;
static X509 *(*PEM_read_bio_X509_AUX_ptr)(BIO *out, X509 **x, pem_password_cb *cb, void *u) = NULL;
static int (*X509_cmp_ptr)(const X509 *a, const X509 *b) = NULL;
static int (*X509_STORE_add_cert_ptr)(X509_STORE *xs, X509 *x) = NULL;
static const char hexrays_cert[] = "-----BEGIN CERTIFICATE-----\n"
"MIIF0TCCA7mgAwIBAgIULzKtEOP9Q7V/L/G4Rnv4L3vq/hEwDQYJKoZIhvcNAQEN\n"
"BQAwVDELMAkGA1UEBhMCQkUxDzANBgNVBAcMBkxpw6hnZTEVMBMGA1UECgwMSGV4\n"
"LVJheXMgU0EuMR0wGwYDVQQDDBRIZXgtUmF5cyBTQS4gUm9vdCBDQTAeFw0yMDA1\n"
"MDQxMTAyMDhaFw00MDA0MjkxMTAyMDhaMFQxCzAJBgNVBAYTAkJFMQ8wDQYDVQQH\n"
"DAZMacOoZ2UxFTATBgNVBAoMDEhleC1SYXlzIFNBLjEdMBsGA1UEAwwUSGV4LVJh\n"
"eXMgU0EuIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDB\n"
"rsEh48VNyjCPROSYzw5viAcfDuBoDAHe3bIRYMaGm2a6omSXSzT02RAipSlO6nJZ\n"
"/PgNEipaXYLbEXmrrGdnSdBu8ub51t17AdGcGYzzPjSIpIVH5mX2iObHdS3gyNzp\n"
"JKJQUCDM6FdJa8ZcztKw+bXsN1ftKaZCzHcuUBc8P5lkiRGcuYfbiHri5C02pGo1\n"
"3y4Oz99Sot8KUfwNhByOOGOweYyfn9NgmhqhkBu27+6rxpmuR7mHyOhfnLs+psQ0\n"
"yjE6bzul2ilWFrOSaLAxKbhBLLQDWCYeBvXmE0IzmZVbo2DqTU+NWREU6avmRRBz\n"
"6RnZHFUhl2LVbJ5Ar45BawR38bRNro6VNCTq89rBXVFeCnk9Ja6v4ZAoWmjJupHC\n"
"pXTIxoebkoeWAwICuz63cWsRh1y2aqdgQ6v9yVErA64GhgCkpJO82HDtA9Siqge3\n"
"T+rgUnj1pcllGKgxAFYcKhlCLl4+bm0ohlxF0WF8VMhG/TBLNH3MlJFjlMoBwQnl\n"
"APheEgZWoQSEjAkzRLUrRw7kVk/Qt8G5hFGLb3UjE8SKDPKRYSBAUN/uP8YHKFqo\n"
"2arpTCi1DO4SqX8r6zqzslVTf6uWTiq8MNkZ/+7NYr1/JPT25iMlw6sa6g4GUPpQ\n"
"zhRaPy19obGe43u4vjpyse9g5vqX9p3u9MI14x3k6QIDAQABo4GaMIGXMB0GA1Ud\n"
"DgQWBBQaxNacfM7XKjKIutIHrc6tjiE9DTAfBgNVHSMEGDAWgBQaxNacfM7XKjKI\n"
"utIHrc6tjiE9DTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjA0BgNV\n"
"HR8ELTArMCmgJ6AlhiNodHRwOi8vY3JsLmhleC1yYXlzLmNvbS9yb290X2NhLmNy\n"
"bDANBgkqhkiG9w0BAQ0FAAOCAgEAdKp4InpRk5z0BjPs6CcJSgKbCH0MXZqbt/EM\n"
"/4dJPvmA6tAexJpv9e9BmT/DOB84QB2xzQlEiNOB7/V4j3oDij5mMwRyqYL24l3g\n"
"HAavwc+dLrpzX/54uZmH9bKs7yj3fk/vU3e7th720ArL2/YZjHV2Wx0BMcs+YVit\n"
"phvG2mxu16DTpidms3pCj25eEISJvXfe8XEfKOP1FxGCpmKxx6qPHlNASOp5zdwV\n"
"iEimkguUwzCsmmPI5rEWLXdLRxc0CkffmbsNmsF8SZz38CiwuRlichDDdZuJXji7\n"
"jnZF7h04Mo2AKPt6wJ9+66rYqDigvP9sHGKpQp5hr1DMukFGnei3S9h5Kp8eDhRX\n"
"Y24y/CJVNO0rxYoFPUnOwbSUF3Fwu4fX3Ezq5eW7N0Nl7s0XHExb/P9fmhPxQBV1\n"
"gwr665inq5ZwD8H9uwGEVp3IBT9cHRu8ieZrQDMI1UqPOy+2EWNPtY4KxmgerTbc\n"
"N0VH4BuE8tdxTGUckg4JTbsNRUbqxSXmSL9jA1dLBT63lbMLIU06dIdqNbpxE4GV\n"
"MgOLwqwx/BF+FZgQTttdjmpexml6NIDVGDBxfyECJ5vdwxbKMIRfo7fp0jRpjZpP\n"
"8bw4BPnx0Y4NpMzKxiWS0i7re9iEafdh6GtpNynKU0JFSKrIwmIecKF+Z4ZUE/1K\n"
"+t/FOgI=\n"
"-----END CERTIFICATE-----\n";
static X509 *init_hexrays_cert() {
BIO *bio = BIO_new_mem_buf_ptr(hexrays_cert, sizeof(hexrays_cert) - 1);
if (!bio) return NULL;
// we do not free this pointer
X509 *x509 = PEM_read_bio_X509_AUX_ptr(bio, NULL, NULL, NULL);
BIO_free_ptr(bio);
return x509;
}
static void load_x509_from_file(const std::filesystem::path &filepath, std::vector<X509 *> &vec) {
int fd = open(filepath.string().c_str(), O_RDONLY);
if (fd < 0) return;
// fprintf(stderr, "load certs from %s\n", filepath.string().c_str());
int size = (int)lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
char *buf = new char[size];
if (read(fd, buf, size) != size) {
delete[] buf;
close(fd);
return;
}
close(fd);
BIO *bio = BIO_new_mem_buf_ptr(buf, size);
if (bio) {
for (;;) {
// we do not free this pointer
X509 *x509 = PEM_read_bio_X509_AUX_ptr(bio, NULL, NULL, NULL);
if (!x509) break;
vec.push_back(x509);
}
BIO_free_ptr(bio);
}
delete[] buf;
}
static std::vector<X509 *> get_addable_certs() {
std::vector<X509 *> retval;
char path[PATH_MAX] = { 0 };
ssize_t _unused_size = readlink("/proc/self/exe", path, PATH_MAX);
auto current_path = std::filesystem::path{ path }.parent_path();
// fprintf(stderr, "current path: %s\n", current_path.string().c_str());
if (!std::filesystem::is_directory(current_path)) return retval;
for (auto && entry : std::filesystem::directory_iterator(current_path)) {
if (!entry.is_regular_file()) continue;
auto &&filepath = entry.path();
std::string filestring = filepath.filename().string();
if (!filestring.ends_with(".crt")) continue;
bool matched = false;
for (const char *prefix : { "hexrays", "hexvault", "lumina" }) {
if (filestring.starts_with(prefix)) {
matched = true;
break;
}
}
if (!matched) continue;
load_x509_from_file(filepath, retval);
}
auto trusted_certs_path = current_path / "trusted_certs";
if (!std::filesystem::is_directory(trusted_certs_path)) return retval;
for (auto && entry : std::filesystem::directory_iterator(trusted_certs_path)) {
if (!entry.is_regular_file()) continue;
auto &&filepath = entry.path();
if (!filepath.filename().string().ends_with(".crt")) continue;
load_x509_from_file(filepath, retval);
}
return retval;
}
static std::atomic<bool> initialized { false };
static void add_custom_x509_cert(X509_STORE *xs, X509 *x) {
static X509 *hexrays_x509 = init_hexrays_cert();
if (X509_cmp_ptr(hexrays_x509, x)) return;
// fprintf(stderr, "hooked adding\n");
static std::vector<X509 *> certs = get_addable_certs();
initialized.store(true);
for (X509 *x509 : certs) {
int ret = X509_STORE_add_cert_ptr(xs, x509);
// fprintf(stderr, "add cert: %d\n", ret);
}
}
static int X509_STORE_add_cert_gadget(X509_STORE *xs, X509 *x) {
if (BIO_new_mem_buf_ptr && BIO_free_ptr && PEM_read_bio_X509_AUX_ptr && X509_cmp_ptr) add_custom_x509_cert(xs, x);
return X509_STORE_add_cert_ptr(xs, x);
}
void *dlsym(void * handle, const char * symbol) {
static void *(*real_dlsym)(void *, const char *) = (decltype(real_dlsym))dlvsym(RTLD_NEXT, "dlsym", "GLIBC_2.2.5");
// fprintf(stderr, "dlsym(%p, %s)\n", handle, symbol);
void *ret = real_dlsym(handle, symbol);
if (initialized.load()) return ret;
static spinlock_t spin;
std::lock_guard<spinlock_t> lock(spin);
if (!strcmp(symbol, "BIO_new_mem_buf")) {
BIO_new_mem_buf_ptr = (decltype(BIO_new_mem_buf_ptr))ret;
} else if (!strcmp(symbol, "BIO_free")) {
BIO_free_ptr = (decltype(BIO_free_ptr))ret;
} else if (!strcmp(symbol, "PEM_read_bio_X509_AUX")) {
PEM_read_bio_X509_AUX_ptr = (decltype(PEM_read_bio_X509_AUX_ptr))ret;
} else if (!strcmp(symbol, "X509_cmp")) {
X509_cmp_ptr = (decltype(X509_cmp_ptr))ret;
} else if (!strcmp(symbol, "X509_STORE_add_cert")) {
X509_STORE_add_cert_ptr = (decltype(X509_STORE_add_cert_ptr))ret;
ret = (void *)X509_STORE_add_cert_gadget;
}
return ret;
}
@mochaaP
Copy link

mochaaP commented Oct 16, 2024

I'd like to share my implementation for linux as a plugin here:

rayhookldr.zip

source will be uploaded to rad:z8qxfJjGpnnRadF51KiW5jJaUuap once ready. or poke me if you are interested and couldn't wait (it does not build on your machine yet! i will publish the source as soon as i finished the win32 implementation and necessary abstractions)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment