1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
/* -*- c-file-style: "GNU" -*- */
/*
* Copyright (C) CNRS, INRIA, Université Bordeaux 1, Télécom SudParis
* See COPYING in top-level directory.
*
*
* hijack.c - test for symbol hijacking
*
* Created on: 4 Sep. 2012
* Author: Damien Martin-Guillerez <damien.martin-guillerez@inria.fr>
*/
#include <errno.h>
#include <string.h>
#include <binary.h>
#include <tracing.h>
#include <hijack.h>
#include <opcodes.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include "common.h"
int testfork;
int waitattach;
// Common parts
pid_t child;
int fds[2];
void *bin;
char *prog;
static inline int run_ipc() {
if (testfork)
return 0;
pipe(fds);
child = trace_run(NULL, NULL, NULL, 1);
if (child > 0) {
close(fds[1]);
bin = open_binary(prog);
return 1;
}
return 0;
}
static inline void close_ipc() {
close_binary(bin);
close(fds[0]);
}
#define FINISH_IPC() do { \
trace_detach(child); \
trace_wait(child); \
DEBUG("process finished"); \
} while(0)
#define IPC_END() do { \
DEBUG("traced process is exiting"); \
exit(0); \
} while(0)
#define READ_WORD(w) TEST1(-1 != read(fds[0], &w, sizeof(w)), read, "read failed with error %s!", strerror(errno))
#define WRITE_WORD(w) write(fds[1], &w, sizeof(w))
void buffer_function() {
asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop");
}
void bad_function() {
char *__method__ = "bad_function";
int r = 3;
DEBUG("in");
WRITE_WORD(r);
}
void (*original_pointer)() = bad_function;
void original_function() {
char *__method__ = "original_function";
int r = 1;
DEBUG("in");
WRITE_WORD(r);
}
void good_function() {
char *__method__ = "good_function";
int r = 2;
DEBUG2("in (original_pointer = %p, r = %d)", original_pointer, r);
WRITE_WORD(r);
original_pointer();
WRITE_WORD(r);
DEBUG("out");
}
#define BEGIN_TEST_HIJACK(name) \
BEGIN_TEST(name) \
if(run_ipc()) {
#define END_TEST_HIJACK(name) \
FINISH_IPC(); \
int r; \
READ_WORD(r); TEST1(r == 2, prolog, "Not in good_function() (r = %d)!", r); \
READ_WORD(r); TEST1(r == 1, original, "Not in original_function() (r = %d)!", r); \
READ_WORD(r); TEST1(r == 2, epilog, "Not in good_function() (r = %d)!", r); \
close_ipc(); \
} else { \
if(waitattach) { fprintf(stderr, "child pid = %d\n", getpid()); sleep(10); } \
original_function(); \
IPC_END(); \
} \
END_TEST(name)
// End of common parts
BEGIN_TEST_HIJACK(hijack_code)
ssize_t res = hijack_code(
bin,
child,
(word_uint) (word_uint) original_function,
(word_uint) (word_uint) good_function
- (word_uint) (word_uint) original_function,
(word_uint) (word_uint) buffer_function,
(word_uint) (word_uint) &original_pointer,
(word_uint) (word_uint) good_function);
TEST1(res > 0, result,
"hijack_code returned negative value ("WORD_DEC_FORMAT")!", res);
END_TEST_HIJACK(hijack_code)
BEGIN_TEST_HIJACK(hijack)
INIT_ZZT_SYMBOL(toHijack, (word_uint )original_function,
(word_uint )good_function - (word_uint )original_function);
INIT_ZZT_SYMBOL(orig, (word_uint )&original_pointer,
sizeof(original_pointer));
INIT_ZZT_SYMBOL(repl, (word_uint )good_function, 0);
ssize_t res = hijack(bin, child, &toHijack, &orig, &repl);
TEST1(res > 0, result,
"hijack returned negative value ("WORD_DEC_FORMAT")!", res);
END_TEST_HIJACK(hijack)
void usage(char **av, char option) {
if (option != 0) {
fprintf(stderr, "Unknown option '%c'", option);
}
fprintf(
stderr,
"Usage: %s [-v[v]dg] [test1 test2 test3 ...]\n"
"\t-v output verbose debugging information (the more d option, the more verbose the output)\n"
"\t-d don't actually run the tests, simply execute the child (used in conjunction with test specifiers to debug)\n"
"\t-g print the pid of the child and sleep 10 seconds after detachment to enable GDB attachment.\n"
"\ttest specifies the tests to runs (default is all tests). Available tests: hijack_code, hijack.\n",
av[0]);
exit(-1);
}
int main(int ac, char **av) {
int r, i, j;
debug = 0;
testfork = 0;
waitattach = 0;
int runall = 1;
prog = av[0];
for (i = 1; i < ac; i++) {
if (av[i][0] == '-') {
for (j = 1; av[i][j] != 0; j++) {
switch (av[i][j]) {
case 'v':
debug++;
break;
case 'g':
waitattach++;
break;
case 'd':
testfork++;
break;
default:
usage(av, av[i][j]);
}
}
} else {
PARSE_TEST(hijack_code);
PARSE_TEST(hijack);
}
}
RUN_TEST(hijack_code);
RUN_TEST(hijack);
return 0;
}
|