[LINUX] Remplacez printf plus tard par LD_PRELOAD

TL;DR Résumé de la façon de remplacer ultérieurement les fonctions binaires prédéfinies

Code cible de remplacement

Préparez ptintf et fprintf avec et sans arguments de longueur variable comme échantillons à remplacer.

print.c


#include <stdio.h>

int main(int argc, char const* argv[])
{
    printf("printf no args\n");
    printf("printf %d\n", 1);

    fprintf(stdout, "fprintf no args\n");
    fprintf(stdout, "fprintf %d\n", 1);

    return 0;
}

Les résultats de la construction et de l'exécution sont les suivants.

Créer et exécuter des résultats


$ gcc print.c 
$ ./a.out 
printf no args
printf 1
fprintf no args
fprintf 1

Code de remplacement

Ensuite, préparez un code à remplacer par LD_PRELOAD. Fondamentalement, vous pouvez apporter la définition de la fonction que vous souhaitez remplacer et la redéfinir indépendamment de la présence ou de l'absence d'arguments de longueur variable.

override.c


#include <stdio.h>
#include <stdarg.h>
#include <string.h>

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
	const char *c = "override fwrite\n";
	write(1, c, strlen(c)); // stdout
	return 0;
}

int fprintf (FILE *__restrict __stream, const char *__restrict __format, ...) {
	const char *c = "override fprintf\n";
	write(1, c, strlen(c)); // stdout
	return 0;
}

int puts(const char *s) {
	const char *c = "override puts\n";
	write(1, c, strlen(c)); // stdout
	return 0;
}

int printf (const char *__restrict __format, ...) {
	const char *c = "override printf\n";
	write(1, c, strlen(c)); // stdout
	return 0;
}

Si vous spécifiez ceci avec LD_PRELOAD et que vous l'exécutez, vous pouvez remplacer la définition ultérieurement.

Créer et exécuter des résultats


$ gcc -shared -fPIC override.c -o liboverride.so
$ LD_PRELOAD=./liboverride.so ./a.out
override puts
override printf
override fwrite
override fprintf

Cependant, dans GCC, si l'élément d'argument de longueur variable est 0 dans printf et fprintf, ils seront respectivement remplacés par put et fwrite au moment de la compilation. (GCC peut être remplacé même par -O0, dans clang 3.6 il n'y a pas de remplacement par printf à -O0, remplacé par -O1 ou supérieur)

$ gcc -S print.c
$ cat print.s
        .file   "print.c"
        .section        .rodata
.LC0:
        .string "printf no args"
.LC1:
        .string "printf %d\n"
.LC2:
        .string "fprintf no args\n"
.LC3:
        .string "fprintf %d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $.LC0, %edi
        call    puts             <-printf est met
        movl    $1, %esi
        movl    $.LC1, %edi
        movl    $0, %eax
        call    printf
        movq    stdout(%rip), %rax
        movq    %rax, %rcx
        movl    $16, %edx
        movl    $1, %esi
        movl    $.LC2, %edi
        call    fwrite             <-fprintf est fwrite
        movq    stdout(%rip), %rax
        movl    $1, %edx
        movl    $.LC3, %esi
        movq    %rax, %rdi
        movl    $0, %eax
        call    fprintf
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
        .section        .note.GNU-stack,"",@progbits

Si vous voulez désactiver ce remplacement dans GCC, construisez avec -fno-builtin-printf`` -fno-builtin-fprintf.

Résultat de construction et d'exécution avec remplacement désactivé


$ gcc -fno-builtin-printf -fno-builtin-fprintf print.c                                                                                                                
$ LD_PRELOAD=./liboverride.so ./a.out                                                                                                                                 
override printf
override printf
override fprintf
override fprintf

point important

Bien sûr, c'est naturel, mais si vous appelez à nouveau la même fonction dans la fonction remplacée, ce sera un appel récursif et vous ne pourrez pas vous échapper. Par exemple, lors du remplacement de printf, il peut être utilisé dans la bibliothèque utilisée dans le processus de remplacement, vous devez donc faire attention séparément.

Appel récursif


int puts(const char *s) {
        const char *c = "override puts\n";
        write(1, c, strlen(c)); // stdout

        puts("recursive call?"); //* Je ne peux pas sortir des put

        return 0;
}

Livre de référence

"BINARY HACKS" #HACK 60: Remplacez la bibliothèque partagée par LD_PRELOAD

Relation

Tracez toutes les entrées / sorties des fonctions d'exécution - Qiita

référence

Modifiez le comportement de la bibliothèque partagée Linux (.so). Profitez de jouer avec LD_PRELOAD: Garbage In Garbage Out Accrochez la fonction avec LD_PRELOAD! - Comment marcher binaire Écraser les fonctions de la bibliothèque dynamique avec LD_PRELOAD | Blog de Siguniang

Recommended Posts

Remplacez printf plus tard par LD_PRELOAD
11. Remplacez les onglets par des espaces
Remplacer / supprimer les symboles spéciaux par sed
Remplacez la valeur du dictionnaire par Python> update ()
Remplacez tout d'un coup par sed