Skip to content

Instantly share code, notes, and snippets.

@smx-smx
Created February 9, 2024 01:41
Show Gist options
  • Save smx-smx/8581f6d787c76f18a44b2bcffd131362 to your computer and use it in GitHub Desktop.
Save smx-smx/8581f6d787c76f18a44b2bcffd131362 to your computer and use it in GitHub Desktop.
!linker_proxy
project(linker_test)
macro(handle_asm_file file)
set_property(SOURCE "${file}" PROPERTY LANGUAGE C)
set_property(SOURCE "${file}" PROPERTY COMPILE_DEFINITIONS __ASSEMBLY__)
endmacro()
find_library(LIBCRYPTO_LIBRARY NAMES crypto REQUIRED)
message(STATUS ${LIBCRYPTO_LIBRARY})
set(asm_out ${CMAKE_BINARY_DIR}/libcrypto.S)
set(lds_out ${CMAKE_BINARY_DIR}/libcrypto.lds)
add_custom_command(
OUTPUT ${asm_out} ${lds_out}
COMMAND php ${CMAKE_SOURCE_DIR}/makedef.php
/lib/x86_64-linux-gnu/libcrypto.so.3 ${asm_out} ${lds_out}
DEPENDS ${CMAKE_SOURCE_DIR}/makedef.php
)
add_custom_target(make_proxy DEPENDS libcrypto.S)
handle_asm_file(libcrypto.S)
add_executable(test
${asm_out} lib.c
)
target_link_options(test PRIVATE
-T ${lds_out}
"LINKER:--gc-sections"
#"LINKER:--print-gc-sections"
"LINKER:-z,start-stop-gc"
"LINKER:--as-needed"
)
target_link_libraries(test ${LIBCRYPTO_LIBRARY})
/**
* @file lib.c
* @author Stefano Moioli <[email protected]>
* @brief
* @version 0.1
* @date 2024-02-09
*
* @copyright Copyright (c) 2024
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
extern void *X509_new();
extern void *X509_STORE_CTX_new();
void null_catch(){
puts("STUB");
}
extern char __start_thunks_code;
extern char __stop_thunks_code;
/** refers to the used thunk names */
extern char __start_thunks_data;
extern char __stop_thunks_data;
/** refers to the used thunk pointers */
extern void* __start_thunks_ptr;
extern void* __stop_thunks_ptr;
static void *lib = NULL;
extern char str_lib_name[];
static void load_library(){
lib = dlopen(str_lib_name, RTLD_GLOBAL | RTLD_LAZY);
if(lib == NULL){
char *error = dlerror();
fprintf(stderr, "dlopen() failed: %s\n",
error != NULL ? error : "(unknown error)");
fflush(stderr);
abort();
}
}
void *get_fptr(
const char *lib_name,
const char *func_name,
void **thunk_ptr
){
printf("resolve \"%s\" \"%s\"\n",
lib_name, func_name);
void *sym = dlsym(lib, func_name);
if(sym == NULL){
fprintf(stderr, "dlsym('%s') failed\n", func_name);
fflush(stderr);
abort();
}
//void *fptr = &null_catch;
void *fptr = sym;
/** store the resolved symbol to avoid calling the resolved again */
*thunk_ptr = fptr;
return fptr;
}
static void resolve_symbols(){
void **ptr_start = &__start_thunks_ptr;
void **ptr_end = &__stop_thunks_ptr;
char *str_start = &__start_thunks_data;
char *str_end = &__stop_thunks_data;
for(;
str_start < str_end;
ptr_start++
){
char *symbol_name = str_start;
str_start += strlen(str_start) + 1;
void *sym = dlsym(lib, symbol_name);
if(sym == NULL){
fprintf(stderr, "dlsym('%s') failed\n", symbol_name);
fflush(stderr);
abort();
}
/** replace the fptr to point to the resolved symbol */
*ptr_start = sym;
}
}
#define LAZY_SYMBOLS
void __attribute__((constructor(101)))
init(){
printf("load %s\n", str_lib_name);
#ifndef LAZY_SYMBOLS
load_library();
resolve_symbols();
#endif
}
#define DEBUG
int main(int argc, char *argv[]){
#ifdef DEBUG
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
#endif
puts("main");
X509_new();
X509_STORE_CTX_new();
X509_new();
X509_STORE_CTX_new();
return 0;
}
<?php
/**
* @author Stefano Moioli <[email protected]>
*/
function getsyms(string $lib_path){
$hProc = proc_open(['nm', '-D', $lib_path], [
1 => ['pipe', 'w']
], $p);
while(!feof($p[1])){
$l = rtrim(fgets($p[1]));
if($l === false || empty($l)) continue;
list($addr, $type, $name) = preg_split('/\s+/', $l, 3);
if($type === 'U'
|| str_contains($name, '@GLIBC_')
) {
continue;
}
switch($name){
case '__gmon_start__':
case '_ITM_deregisterTMCloneTable':
case '_ITM_registerTMCloneTable':
continue 2;
}
$orig_name = $name;
$pos = strpos($name, '@@');
if($pos !== false){
$name = substr($name, 0, $pos);
}
yield [$orig_name, $name];
}
proc_close($hProc);
}
function genproxy(string $lib_path, $asm, $lds){
$lib_name = pathinfo($lib_path, PATHINFO_FILENAME);
fwrite($asm, <<<EOS
.intel_syntax noprefix
.section .note.GNU-stack,"",@progbits
//.section .thunks,"ax"
EOS);
fwrite($lds, <<<EOS
SECTIONS {
.thunks_code :
{
__start_thunks_code = .;
*(SORT_BY_NAME(.thunk_code_*))
__stop_thunks_code = .;
}
}
INSERT AFTER .text;
SECTIONS {
.thunks_data : {
. = ALIGN(0);
__start_thunks_data = .;
*(SORT_BY_NAME(.thunk_data_*))
__stop_thunks_data = .;
}
.thunks_ptr : {
__start_thunks_ptr = .;
*(SORT_BY_NAME(.thunk_ptr*))
__stop_thunks_ptr = .;
}
}
INSERT AFTER .data;
EOS);
$fn_ids = [];
$proxy_id = 0;
foreach(getsyms($lib_path) as $itm){
list($orig_name, $name) = $itm;
$proxy_fn = "proxy_{$proxy_id}";
$fn_ids[$proxy_id] = $name;
$fcall_fn = "fcall_{$proxy_id}";
fwrite($asm, <<<EOS
.section .thunk_code_{$proxy_id},"ax"
//.globl {$proxy_fn}
.hidden {$proxy_fn}
{$proxy_fn}:
// load function name
lea rdi, [rip + str_lib_name]
lea rsi, [rip + str_{$proxy_id}]
lea rdx, [rip + ptr_{$proxy_id}]
call get_fptr
pushq rax
retq
.globl {$fcall_fn}
.hidden {$fcall_fn}
{$fcall_fn}:
pushq [rip + ptr_{$proxy_id}]
retq
EOS);
fwrite($lds, <<<EOS
"{$orig_name}" = "fcall_{$proxy_id}";
EOS);
/*fwrite($lds, <<<EOS
"{$orig_name}" = "{$proxy_fn}";
EOS);*/
if($name != $orig_name){
fwrite($lds, <<<EOS
"{$name}" = "{$orig_name}";
EOS);
}
++$proxy_id;
}
fwrite($asm, <<<EOS
//.section .thunk_strings,"aS",@progbits
.globl str_lib_name
str_lib_name: .asciz "{$lib_name}"
EOS);
foreach($fn_ids as $i => $name){
fwrite($asm, <<<EOS
.section .thunk_data_{$i},"aS",@progbits
str_{$i}: .asciz "{$name}"
.section .thunk_ptr_{$i},"aw",@progbits
ptr_{$i}: .quad .thunk_code_{$i}
EOS);
}
}
$lib_name = $argv[1];
$out_asm = fopen($argv[2], 'w');
$out_lds = fopen($argv[3], 'w');
genproxy($lib_name, $out_asm, $out_lds);
fclose($out_asm);
fclose($out_lds);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment