├── README.md ├── intercept.c ├── snippet.bin └── xnippet.c /README.md: -------------------------------------------------------------------------------- 1 | # xnippet 2 | 3 | ## Original description by [Gonzalo J. Carracedo](https://twitter.com/BatchDrake) 4 | 5 | I had an amazing weekend in the Xtrelan LAN Party here in Badajoz (Spain). Among other contests and activities, an entertaining hacking contest was brought by Miguel Gesteiro (@mgesteiro) again, and I decided to participate in it. And there I found a reversing challenge for Win32. I think it's not necessary to say I don't have any Windows machine, so I had to do the entire reversing via Wine + IDA. 6 | 7 | After 32 fucking hours since the challenge was first presented, I finally found the solution, and because the precious time I spent starting the binary, waiting the condition for jumping to the breakpoint, blah, blah, blah I thought it would be a good idea to write a little tool to extract the exact function you want to test to a file, load and execute it separately. And that was what I was spending my time in this afternoon: xnippet. 8 | 9 | xnippet is a tool that lets you load code snippets or isolated functions (no matter the operating system they came from), pass parameters to it in several formats (signed decimal, string, unsigned hexadecimal...), hook other functions called by the snippet and analyze the result. The tool is written in a way that will let me improve it in a future, defining new calling conventions and output argument pointers. 10 | 11 | But still, I think the best way to illustrate its features is by a proof of concept against the challenge that costed me that much. 12 | 13 | The challenge consisted of a binary embedded inside another binary which performed some mathematical calculus that is passed via SendMessage to another thread. The result must be strictly positive in order to crack it. Specifically, it calculates the result of a second grade equation with given coefficients a, b and c. The prototype of this mysterious function is something like this: 14 | 15 | ` 16 | void checkPassword (int a, int b, int c, void *hWnd, void *parenthWnd); 17 | ` 18 | 19 | The problem was I was not paying attention to how were the arguments being converted (the coefficients were read as chars from the password field and then converted to int, extending its sign). But let's forget that for a while and ask the following question to ourselves: is there a way to run that function to check how it behaves with different values for a, b, c *without* debugging the entire application? 20 | 21 | The answer is yes, and the only tool we need is xnippet. The procedure is the following: 22 | 23 | First, we have to locate the function we want to debug and save its bytes to a file. In my case was: 24 | 25 | ``` 26 | 402542: 55 push %ebp 27 | 402543: 8b ec mov %esp,%ebp 28 | 402545: 83 c4 fc add $0xfffffffc,%esp 29 | 402548: 9b db e3 finit 30 | 40254b: db 45 0c fildl 0xc(%ebp) 31 | 40254e: d8 c8 fmul %st(0),%st 32 | 402550: c7 45 fc fc ff ff ff movl $0xfffffffc,-0x4(%ebp) 33 | 402557: db 45 fc fildl -0x4(%ebp) 34 | 40255a: da 4d 08 fimull 0x8(%ebp) 35 | 40255d: da 4d 10 fimull 0x10(%ebp) 36 | 402560: de c1 faddp %st,%st(1) 37 | 402562: db 5d fc fistpl -0x4(%ebp) 38 | 402565: 8b 45 fc mov -0x4(%ebp),%eax 39 | 402568: 83 f8 00 cmp $0x0,%eax 40 | 40256b: 7c 46 jl 0x4025b3 41 | 40256d: db 45 fc fildl -0x4(%ebp) 42 | 402570: d9 fa fsqrt 43 | 402572: db 45 0c fildl 0xc(%ebp) 44 | 402575: c7 45 fc ff ff ff ff movl $0xffffffff,-0x4(%ebp) 45 | 40257c: da 4d fc fimull -0x4(%ebp) 46 | 40257f: de c1 faddp %st,%st(1) 47 | 402581: da 75 08 fidivl 0x8(%ebp) 48 | 402584: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%ebp) 49 | 40258b: da 75 fc fidivl -0x4(%ebp) 50 | 40258e: db 5d fc fistpl -0x4(%ebp) 51 | 402591: 6a 00 push $0x0 52 | 402593: ff 75 fc pushl -0x4(%ebp) 53 | 402596: 68 0c 04 00 00 push $0x40c 54 | 40259b: ff 75 18 pushl 0x18(%ebp) 55 | 40259e: e8 cb 00 00 00 call 0x40266e 56 | 4025a3: 6a 00 push $0x0 57 | 4025a5: 6a 00 push $0x0 58 | 4025a7: 6a 10 push $0x10 59 | 4025a9: ff 75 14 pushl 0x14(%ebp) 60 | 4025ac: e8 c3 00 00 00 call 0x402674 61 | 4025b1: eb 0e jmp 0x4025c1 62 | 4025b3: 6a 00 push $0x0 63 | 4025b5: 6a 00 push $0x0 64 | 4025b7: 6a 10 push $0x10 65 | 4025b9: ff 75 14 pushl 0x14(%ebp) 66 | 4025bc: e8 b3 00 00 00 call 0x402674 67 | 4025c1: c9 leave 68 | 4025c2: c2 14 00 ret $0x14 69 | ``` 70 | 71 | Note that the starting address (the base as I'll call it from now on) is 402542. We need to know this address because most of the jumps and calls we'll found are performed via relative addressing. 72 | 73 | The next step is looking for every single call and jump outside the boundaries of the function. We need to do this carefully in order to keep the logic of our snippet: we must bind every single function this code depends on if we don't want it to crash (and debugging with xnippet, although possible, is primitive). 74 | 75 | We see here three calls to two functions (0x402674 and 0x40266e, which after debugging with IDA happened to be SendMessage and PostMessage respectively, we don't really need this information, it should be enough knowing how many arguments are passed to each function we want to bind). 76 | 77 | As I said above, this functions need to be, if not fully implemented, at least replaced with stubs. And since I run a Linux box, that will be our wise decision. 78 | 79 | xnippet looks for functions expored by .so files, so I'm going to writte a couple of functions imitating PostMessage and SendMessage, I'll call it intercept.c: 80 | 81 | ``` 82 | #include 83 | 84 | int 85 | PostMessage (void *hWnd, int msg, unsigned int wParam, unsigned int lParam) 86 | { 87 | printf ("PostMessage (%p, %d, %d, %d)\n", hWnd, msg, wParam, lParam); 88 | } 89 | 90 | int 91 | SendMessage (void *hWnd, int msg, unsigned int wParam, unsigned int lParam) 92 | { 93 | printf ("SendMessage (%p, %d, %d, %d)\n", hWnd, msg, wParam, lParam); 94 | } 95 | ``` 96 | 97 | Let's compile it: 98 | 99 | ` 100 | % gcc intercept.c -o intercept.so --shared -fPIC 101 | ` 102 | 103 | And now we have everything ready to start our execution. We bind the functions called by the snippet to the stubs we've just written and try it out: 104 | 105 | ` 106 | % ./xnippet -b 0x402542 -f ./intercept.so:PostMessage:0x40266e -f ./intercept.so:SendMessage:0x402674 snippet.bin i:01 i:10: i:1 x:0xaaaaaaaa x:0xbbbbbbbb -m 107 | ` 108 | 109 | xnippet accepts a few options, with -b I specify where the function must be loaded (our base address) and with -f I define a function binding. In this case, I bind 0x402674 to SendMessage inside intercept.so and 0x40266e to PostMessage. We can bind many functions to many different libraries (even the libc.so.6). -m just draws a horizontal line to separate where the text output of the snipper starts and where it ends. The arguments after the filename are the arguments passed to the function, in format type:value. Type may be int (for integers), str (for strings, passing the pointer to the stack), u (unsigned decimal) and x (unsigned hexadecimal). With the above command, we're calling the snippet like this: 110 | 111 | ` 112 | void snippet (1, 10, 1, (void *) 0xaaaaaaaa, (void *) 0xbbbbbbbb); 113 | ` 114 | 115 | And the results are like this: 116 | 117 | ``` 118 | % ./xnippet -b 0x402542 -f ./intercept.so:PostMessage:0x40266e -f ./intercept.so:SendMessage:0x402674 snippet.bin i:01 i:10: i:1 x:0xaaaaaaaa x:0xbbbbbbbb -m 119 | ----------8<----------------------------------- 120 | PostMessage (0xbbbbbbbb, 1036, 0, 0) 121 | SendMessage (0xaaaaaaaa, 16, 0, 0) 122 | ----------8<----------------------------------- 123 | ``` 124 | 125 | Playing with the values we can discover different behaviors: 126 | 127 | ``` 128 | % ./xnippet -b 0x402542 -f ./intercept.so:PostMessage:0x40266e -f ./intercept.so:SendMessage:0x402674 snippet.bin i:01 i:10: i:20 x:0xaaaaaaaa x:0xbbbbbbbb -m 129 | ----------8<----------------------------------- 130 | PostMessage (0xbbbbbbbb, 1036, -3, 0) 131 | SendMessage (0xaaaaaaaa, 16, 0, 0) 132 | ----------8<----------------------------------- 133 | 134 | % ./xnippet -b 0x402542 -f ./intercept.so:PostMessage:0x40266e -f ./intercept.so:SendMessage:0x402674 snippet.bin i:30 i:10: i:20 x:0xaaaaaaaa x:0xbbbbbbbb -m 135 | ----------8<----------------------------------- 136 | SendMessage (0xaaaaaaaa, 16, 0, 0) 137 | ----------8<----------------------------------- 138 | ``` 139 | 140 | And the one I was looking for: 141 | 142 | ``` 143 | % ./xnippet -b 0x402542 -f ./intercept.so:PostMessage:0x40266e -f ./intercept.so:SendMessage:0x402674 snippet.bin i:01 i:-35: i:0 x:0xaaaaaaaa x:0xbbbbbbbb -m 144 | ----------8<----------------------------------- 145 | PostMessage (0xbbbbbbbb, 1036, 35, 0) 146 | SendMessage (0xaaaaaaaa, 16, 0, 0) 147 | ----------8<----------------------------------- 148 | ``` 149 | 150 | Which sends a 35, a positive value I can use with my crackme. 151 | 152 | There are another interesting ways to use this. With -T xnippet triggers a trap the moment before performs the absolute jump against the snippet (which is performed via push / ret, stopping in the ret). With -e we can examinate the return value and with -r and -s we can analyze registers and siginfo structure after the execution. 153 | 154 | Bugs: 155 | 156 | It won't work outside 32 bits x86 Linux boxes. 157 | 158 | I wanted to make it as portable as possible, but there's no much I can do with just a x86 machine. All the code is highly dependant on the underlying architecture, and well, I didn't compile it in other Unix flavor, but I have no hope on it. 159 | 160 | By the moment, stdcall calling conventions are the only supported, but if you guys find this useful, I can implement pascal and winapi conventions with a few lines more. 161 | 162 | I attached the xnippet in a single C file (which must be compiled with flag -ldl) and the files I used on this example. It should work without much trouble. 163 | 164 | Hope you enjoy it :) 165 | -------------------------------------------------------------------------------- /intercept.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | PostMessage (void *hWnd, int msg, unsigned int wParam, unsigned int lParam) 7 | { 8 | printf ("PostMessage (%p, %d, %d, %d)\n", hWnd, msg, wParam, lParam); 9 | } 10 | 11 | int 12 | SendMessage (void *hWnd, int msg, unsigned int wParam, unsigned int lParam) 13 | { 14 | printf ("SendMessage (%p, %d, %d, %d)\n", hWnd, msg, wParam, lParam); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /snippet.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osirislab/xnippet/c55d153331e9f951b92f3b51038e78548f582015/snippet.bin -------------------------------------------------------------------------------- /xnippet.c: -------------------------------------------------------------------------------- 1 | /* 2 | * xnippet.c: extract snippets of code from a program and run them 3 | * output it, and debug it. 4 | * 5 | * Copyright (c) 2011 Gonzalo J. Carracedo (BatchDrake) 6 | * (c) 2011 Painsec 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see 20 | */ 21 | 22 | #define _GNU_SOURCE 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | /* TODO: reserve data pages */ 39 | 40 | #define PAGE_SIZE 4096 41 | 42 | #define _JOIN(a, b) a ## b 43 | #define JOIN(a, b) _JOIN(a, b) 44 | 45 | #define PTR_LIST(type, name) \ 46 | type ** name ## _list; \ 47 | int name ## _count; 48 | 49 | #define PTR_LIST_APPEND(name, ptr) \ 50 | ptr_list_append ((void ***) &JOIN (name, _list), \ 51 | &JOIN (name, _count), ptr) 52 | 53 | typedef unsigned int busword_t; 54 | typedef unsigned int reg_t; 55 | 56 | #define TRAMPOLINE_SIZE 6 57 | #define SIGHANDLER_STACK_SIZE SIGSTKSZ 58 | 59 | #define PAGE_OFFSET_MASK (PAGE_SIZE - 1) 60 | #define NOP_BYTE 0x90 61 | 62 | #define MMAP_EXTRA_BYTES 0x16 /* Clening up the stack */ 63 | #define error(fmt, arg...) fprintf (stderr, "xnippet error: " fmt, ##arg) 64 | #define warning(fmt, arg...) fprintf (stderr, "xnippet warning: " fmt, ##arg) 65 | #define notice(fmt, arg...) fprintf (stderr, "xnippet notice: " fmt, ##arg) 66 | #define debug(fmt, arg...) \ 67 | do { \ 68 | if (verbose) \ 69 | fprintf (stderr, "xnippet notice: " fmt, ##arg); \ 70 | } while (0); 71 | 72 | #define CALL_TYPE_STDCALL 0 73 | #define CALL_TYPE_PASCAL 1 74 | 75 | #define X86_REG_EAX 7 76 | #define X86_REG_ECX 6 77 | #define X86_REG_EDX 5 78 | #define X86_REG_EBX 4 79 | #define X86_REG_ESP 3 80 | #define X86_REG_EBP 2 81 | #define X86_REG_ESI 1 82 | #define X86_REG_EDI 0 83 | 84 | /* Everybody loves executable stacks */ 85 | #define DEFAULT_STACK_PROT PROT_READ | PROT_EXEC | PROT_WRITE 86 | #define DEFAULT_STACK_SIZE 16384 /* 16 KiB stack */ 87 | 88 | 89 | const int failure_sigs[] = 90 | {SIGTSTP, SIGTTIN, SIGTTOU, SIGTRAP, 91 | SIGSYS, SIGBUS, SIGSEGV, SIGILL, 92 | SIGFPE, SIGPIPE, SIGSTKFLT, SIGXCPU, 93 | SIGXFSZ, SIGHUP, SIGINT}; 94 | 95 | 96 | struct snippet_context_info 97 | { 98 | void *stack_top; 99 | size_t stack_size; 100 | int stack_prot; 101 | 102 | /* -----8<----------------- MACHINE DEPENDANT SECTION START ------------- */ 103 | reg_t registers[8]; 104 | /* -----8<----------------- MACHINE DEPENDANT SECTION STOP -------------- */ 105 | }; 106 | 107 | struct snippet_call_info 108 | { 109 | int call_type; 110 | int arg_count; 111 | void **arg_list; 112 | int retval; 113 | void *snippet; 114 | size_t len; 115 | 116 | struct snippet_context_info context; 117 | }; 118 | 119 | struct lib_info 120 | { 121 | char *name; 122 | void *handle; 123 | }; 124 | 125 | struct trampoline 126 | { 127 | void *from; 128 | void *to; 129 | }; 130 | 131 | PTR_LIST (struct trampoline, trampolines); 132 | PTR_LIST (struct lib_info, libs); 133 | 134 | sigjmp_buf env; 135 | siginfo_t siginfo; 136 | 137 | void **trampoline_page; 138 | int trampoline_page_count; 139 | 140 | void *sighandler_stack_top; 141 | void *base; 142 | 143 | int print_siginfo; 144 | int print_regdump; 145 | int print_retval; 146 | int mark_output; 147 | int do_trap; 148 | int verbose; 149 | 150 | void* 151 | xmalloc (size_t size) 152 | { 153 | void* m; 154 | 155 | m = malloc (size); 156 | 157 | if (m == NULL) 158 | abort (); 159 | 160 | return m; 161 | } 162 | 163 | /* Wrapper para strdup */ 164 | char * 165 | xstrdup (const char *str) 166 | { 167 | char *ret; 168 | 169 | if (str != NULL) 170 | { 171 | ret = xmalloc (strlen (str) + 1); 172 | strcpy (ret, str); 173 | } 174 | else 175 | ret = NULL; 176 | 177 | return ret; 178 | } 179 | 180 | 181 | /* Wrapper para realloc */ 182 | void* 183 | xrealloc (void* ptr, size_t new_size) 184 | { 185 | void* m; 186 | 187 | m = realloc (ptr, new_size); 188 | 189 | if (m == NULL) 190 | abort (); 191 | 192 | return m; 193 | } 194 | 195 | void 196 | ptr_list_append (void ***list, int *count, void *new) 197 | { 198 | int i; 199 | 200 | for (i = 0; i < *count; i++) 201 | if ((*list)[i] == NULL) 202 | break; 203 | 204 | if (i == *count) 205 | *list = xrealloc (*list, ++*count * sizeof (void *)); 206 | 207 | (*list)[i] = new; 208 | } 209 | 210 | static inline void 211 | __x86_setup_trampoline (char *from, void *to) 212 | { 213 | from[0] = 0x68; /* push dword */ 214 | memcpy (from + 1, &to, 4); /* Address */ 215 | from[5] = 0xc3; /* ret */ 216 | } 217 | 218 | void * 219 | trampoline_allocate_page (void *address) 220 | { 221 | void *tramp; 222 | 223 | if (address == (void *) ((busword_t) base & ~PAGE_OFFSET_MASK)) 224 | return address; /* We already have this */ 225 | 226 | tramp = mmap (address, 227 | PAGE_SIZE, 228 | PROT_READ | PROT_EXEC | PROT_WRITE, 229 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, 0, 0); 230 | 231 | if (tramp != (void *) -1) 232 | memset (tramp, NOP_BYTE, PAGE_SIZE); 233 | else 234 | error ("mmap failed: %s\n", strerror (errno)); 235 | return tramp; 236 | } 237 | 238 | void 239 | mark (void) 240 | { 241 | int i; 242 | 243 | printf ("----------8<"); 244 | 245 | for (i = 5; i < 40; i++) 246 | putchar ('-'); 247 | 248 | putchar (10); 249 | } 250 | 251 | 252 | int 253 | trampoline_register (void *from, void *to) 254 | { 255 | int i; 256 | busword_t offset; 257 | void *page; 258 | 259 | 260 | page = (void *) ((busword_t) from & ~PAGE_OFFSET_MASK); 261 | offset = (busword_t) from & ~PAGE_OFFSET_MASK; 262 | 263 | for (i = 0; i < trampoline_page_count; i++) 264 | if (page == trampoline_page[i]) 265 | break; 266 | 267 | if (i == trampoline_page_count) 268 | { 269 | if (trampoline_allocate_page (page) == (void *) -1) 270 | return -1; 271 | 272 | trampoline_page = xrealloc (trampoline_page, ++trampoline_page_count * sizeof (void *)); 273 | trampoline_page[i] = page; 274 | 275 | /* What if trampoline code lies between two pages? We have to consider 276 | this highly unlikely posibility */ 277 | 278 | if (PAGE_SIZE - offset < TRAMPOLINE_SIZE) 279 | { 280 | if (trampoline_allocate_page (page + PAGE_SIZE) == (void *) -1) 281 | return -1; 282 | 283 | trampoline_page = xrealloc (trampoline_page, ++trampoline_page_count * sizeof (void *)); 284 | trampoline_page[i + 1] = page + PAGE_SIZE; 285 | } 286 | } 287 | 288 | __x86_setup_trampoline (from, to); 289 | 290 | return 0; 291 | } 292 | 293 | struct snippet_call_info * 294 | snippet_call_info_new (int type) 295 | { 296 | struct snippet_call_info *new; 297 | 298 | new = xmalloc (sizeof (struct snippet_call_info)); 299 | 300 | memset (new, 0, sizeof (struct snippet_call_info)); 301 | 302 | new->call_type = type; 303 | new->arg_list = NULL; 304 | new->arg_count = 0; 305 | 306 | return new; 307 | } 308 | 309 | void 310 | snippet_set_address (struct snippet_call_info *info, void *addr, size_t len) 311 | { 312 | info->snippet = addr; 313 | info->len = len; 314 | } 315 | 316 | void 317 | snippet_append_argument (struct snippet_call_info *info, void *dword) 318 | { 319 | info->arg_list = xrealloc (info->arg_list, (info->arg_count + 1) * sizeof (void *)); 320 | info->arg_list[info->arg_count++] = dword; 321 | } 322 | 323 | void 324 | __snippet_x86_set_reg (struct snippet_call_info *info, int reg, reg_t data) 325 | { 326 | info->context.registers[reg] = data; 327 | } 328 | 329 | int 330 | snippet_setup_stack (struct snippet_call_info *info, int size, int prot) 331 | { 332 | void *addr; 333 | 334 | if ((addr = mmap (NULL, size, prot, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) == (void *) -1) 335 | { 336 | error ("couldn't allocate stack: %s\n", strerror (errno)); 337 | return -1; 338 | } 339 | 340 | info->context.stack_top = addr; 341 | info->context.stack_size = size; 342 | info->context.stack_prot = prot; 343 | 344 | return 0; 345 | } 346 | 347 | void 348 | __snippet_x86_enter (struct snippet_call_info *info) 349 | { 350 | void *esp; 351 | static struct snippet_call_info *save; 352 | static void *snippet_addr; 353 | static int arg_count; 354 | static int saved_esp; 355 | char *regs[] = {"%edi", "%esi", "%ebp", "%esp", "%ebx", "%edx", "%ecx", "%eax"}; 356 | int i; 357 | 358 | 359 | /* We have to make room in our stack to hold information about registers, 360 | arguments and saved %esp (the %esp in this function) */ 361 | 362 | esp = info->context.stack_top + info->context.stack_size 363 | - sizeof (info->context.registers) 364 | - sizeof (void *) * info->arg_count; 365 | 366 | memcpy (esp, info->context.registers, sizeof (info->context.registers)); 367 | memcpy (esp + sizeof (info->context.registers), info->arg_list, sizeof (void *) * info->arg_count); 368 | 369 | save = info; 370 | snippet_addr = save->snippet; 371 | arg_count = sizeof (void *) * save->arg_count; 372 | /* CALL_TYPE_PASCAL? We must alter info->context.registers in order to 373 | perform a call of this type */ 374 | 375 | if (mark_output) 376 | mark (); 377 | 378 | asm volatile ("pusha"); 379 | asm volatile ("movl %%esp, %0" : "=g" (saved_esp)); 380 | asm volatile ("movl %0, %%esp" :: "g" (esp)); 381 | asm volatile ("popa" ::: "eax", "ebx", "ecx", "edx", "esi", "edi"); 382 | asm volatile ("pushl $ret_point"); 383 | asm volatile ("pushl %0" :: "m" (snippet_addr)); 384 | asm volatile ("cmp $0, %0" :: "m" (do_trap)); 385 | asm volatile ("jz 1f"); 386 | asm volatile ("int $3"); 387 | asm volatile ("1:"); 388 | asm volatile ("ret"); /* I call this retrocalling */ 389 | asm volatile ("ret_point:"); 390 | asm volatile ("pusha"); 391 | asm volatile ("addl $32, %esp"); 392 | /* Adjust stack pointer */ 393 | asm volatile ("addl %0, %%esp" :: "g" (arg_count)); 394 | asm volatile ("movl %0,%%esp" :: "g" (saved_esp)); 395 | asm volatile ("popa" ::: "eax", "ebx", "ecx", "edx", "esi", "edi"); 396 | 397 | if (mark_output) 398 | mark (); 399 | 400 | memcpy (info->context.registers, esp, sizeof (info->context.registers)); 401 | 402 | if (print_regdump) 403 | { 404 | fprintf (stderr, "regdump:\n"); 405 | 406 | for (i = 7; i >= 4; i--) 407 | fprintf (stderr, " %s = 0x%08x", regs[i], info->context.registers[i]); 408 | 409 | puts (""); 410 | 411 | for (i = 3; i >= 0; i--) 412 | fprintf (stderr, " %s = 0x%08x", regs[i], info->context.registers[i]); 413 | 414 | puts (""); 415 | } 416 | 417 | info->retval = info->context.registers[X86_REG_EAX]; 418 | } 419 | 420 | 421 | void 422 | generic_sigaction (int sig, siginfo_t *info, void *unused) 423 | { 424 | int addr; 425 | asm volatile ("movl 4(%%ebp), %%eax" : "=a" (addr)); 426 | 427 | sig = (int) unused; /* shut up gcc */ 428 | 429 | siginfo = *info; 430 | siglongjmp (env, addr); 431 | } 432 | 433 | void 434 | handle_all (void) 435 | { 436 | int i; 437 | struct sigaction action; 438 | stack_t ss; 439 | 440 | ss.ss_sp = sighandler_stack_top; 441 | ss.ss_size = SIGHANDLER_STACK_SIZE; 442 | ss.ss_flags = SS_ONSTACK; 443 | 444 | if (sigaltstack (&ss, NULL) == -1) 445 | { 446 | error ("can't setup a safe stack for signal handling\n"); 447 | exit (1); 448 | } 449 | 450 | action.sa_sigaction = generic_sigaction; 451 | action.sa_flags = SA_ONESHOT | SA_SIGINFO | SA_ONSTACK; 452 | 453 | for (i = 0; i < (int) sizeof (failure_sigs) / (int) sizeof (int); i++) 454 | sigaction (failure_sigs[i], &action, NULL); 455 | } 456 | 457 | void 458 | restore_all (void) 459 | { 460 | int i; 461 | struct sigaction action; 462 | 463 | action.sa_handler = SIG_DFL; 464 | action.sa_flags = 0; 465 | 466 | for (i = 0; i < (int) sizeof (failure_sigs) / (int) sizeof (int); i++) 467 | sigaction (failure_sigs[i], &action, NULL); 468 | } 469 | 470 | 471 | void 472 | snippet_enter (struct snippet_call_info *info) 473 | { 474 | int n; 475 | 476 | handle_all (); 477 | 478 | debug ("executing snippet at %p (stack: %p-%p)\n", 479 | info->snippet, 480 | info->context.stack_top, 481 | info->context.stack_top + info->context.stack_size - 1); 482 | 483 | if ((n = sigsetjmp (env, 1)) == 0) 484 | { 485 | __snippet_x86_enter (info); 486 | siglongjmp (env, 1); 487 | } 488 | else if (n == 1) 489 | { 490 | if (print_retval) 491 | notice ("everything ok, snippet returned %d (0x%08x, %uu)\n", 492 | info->retval, info->retval, info->retval); 493 | } 494 | else 495 | { 496 | if (mark_output) 497 | mark (); 498 | 499 | warning ("snippet received signal %d (%s) at 0x%x\n", 500 | siginfo.si_signo, strsignal (siginfo.si_signo), n); 501 | 502 | if (print_siginfo) 503 | { 504 | fprintf (stderr, "\nsiginfo at the time of failure:\n"); 505 | #define STRINGIFY(token) #token 506 | 507 | #define DEBUG_FIELD(field, fmt, type) \ 508 | fprintf (stderr, " ." STRINGIFY (field) " = " fmt "\n", (type) (siginfo.field)) 509 | 510 | DEBUG_FIELD (si_signo, "%d,", int); 511 | DEBUG_FIELD (si_errno, "%d,", int); 512 | DEBUG_FIELD (si_code, "%d,", int); 513 | DEBUG_FIELD (si_pid, "%d,", int); 514 | DEBUG_FIELD (si_uid, "%d,", int); 515 | DEBUG_FIELD (si_status, "%d,", int); 516 | DEBUG_FIELD (si_utime, "%d,", int); 517 | DEBUG_FIELD (si_stime, "%d,", int); 518 | DEBUG_FIELD (si_int, "%d,", int); 519 | DEBUG_FIELD (si_ptr, "%p,", void *); 520 | DEBUG_FIELD (si_addr, "%p,", void *); 521 | DEBUG_FIELD (si_band, "%d,", int); 522 | DEBUG_FIELD (si_fd, "%d", int); 523 | 524 | #undef DEBUG_FIELD 525 | #undef STRINGIFY 526 | } 527 | } 528 | 529 | restore_all (); 530 | } 531 | 532 | void 533 | help (const char *argv0) 534 | { 535 | fprintf (stderr, "Usage:\n"); 536 | fprintf (stderr, " %s [OPTIONS] [type1:arg1 [type2:arg2 [...]]]\n", 537 | argv0); 538 | fprintf (stderr, "\n"); 539 | fprintf (stderr, "%s runs code snippets (probably functions) extracted \n", 540 | argv0); 541 | fprintf (stderr, "from executable files, no matter their format or operating system. \n"); 542 | fprintf (stderr, "\n"); 543 | fprintf (stderr, "OPTIONS\n"); 544 | fprintf (stderr, "\n"); 545 | fprintf (stderr, " -b, --base specifies the base address where the\n" 546 | " snippet should be placed.\n"); 547 | fprintf (stderr, " -f, --bind-function ::
\n"); 548 | fprintf (stderr, " creates a binding to the external\n"); 549 | fprintf (stderr, " function specified by inside\n"); 550 | fprintf (stderr, " at
\n"); 551 | fprintf (stderr, " -e, --show-result displays the return value of the snippet\n"); 552 | fprintf (stderr, " -r. --show-registers displays the state of the registers when\n"); 553 | fprintf (stderr, " the snippet exits normally\n"); 554 | fprintf (stderr, " -m. --show-mark displays a horizontal line before and \n"); 555 | fprintf (stderr, " after the execution.\n"); 556 | 557 | fprintf (stderr, " -s, --show-siginfo when the snippet fails and receives a\n"); 558 | fprintf (stderr, " signal, displays a dump of the siginfo\n"); 559 | fprintf (stderr, " structure.\n"); 560 | fprintf (stderr, " -T, --debug raises SIGTRAP the moment before jumping\n"); 561 | fprintf (stderr, " to the snippet code\n"); 562 | fprintf (stderr, " -v, --verbose enable some debug messages\n"); 563 | fprintf (stderr, " -h, --help this help\n"); 564 | fprintf (stderr, " \n"); 565 | 566 | fprintf (stderr, "Snippet argument syntax:\n"); 567 | fprintf (stderr, "The snippet can be executed with arguments passed on the stack\n"); 568 | fprintf (stderr, "like a standard C function in a few formats. Arguments must\n"); 569 | fprintf (stderr, "have the following syntax:\n\n"); 570 | fprintf (stderr, " type:value\n\n"); 571 | fprintf (stderr, "The format of `value' depends of the type of the argument we're\n"); 572 | fprintf (stderr, "passing to the snippet, and can be one of the following:\n\n"); 573 | fprintf (stderr, " s, str: Passes a pointer to a string to the\n"); 574 | fprintf (stderr, " snippet. Value can be any string\n"); 575 | fprintf (stderr, " literal.\n"); 576 | fprintf (stderr, " i, int: Passes a signed 32-bit integer. Value\n"); 577 | fprintf (stderr, " must be a decimal, octal or hexadecimal\n"); 578 | fprintf (stderr, " number in C format. Hexadecimal values\n\n"); 579 | fprintf (stderr, " beyond 0x7fffffff will be mangled, being.\n\n"); 580 | fprintf (stderr, " converted to 0x7fffffff.\n\n"); 581 | 582 | fprintf (stderr, " u, uint: Passes an unsigned 32-bit decimal\n"); 583 | fprintf (stderr, " x, hex: Passes an unsigned 32-bit hexadecimal,\n"); 584 | fprintf (stderr, " lower or upper case, starting with `0x'\n"); 585 | 586 | 587 | fprintf (stderr, "(c) 2011 BatchDrake \n"); 588 | fprintf (stderr, "(c) 2011 Painsec \n"); 589 | fprintf (stderr, "\n"); 590 | fprintf (stderr, "Copyrighted but free, under the terms of GPL3 license\n"); 591 | } 592 | 593 | int 594 | load_snippet (const char *path, void *addr, void **place) 595 | { 596 | int fd; 597 | int flags; 598 | off_t offset; 599 | int len; 600 | 601 | if ((fd = open (path, O_RDONLY)) == -1) 602 | return -1; 603 | 604 | len = lseek (fd, 0, SEEK_END); 605 | lseek (fd, 0, SEEK_SET); 606 | 607 | flags = (addr != NULL) * MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS; 608 | 609 | offset = (off_t) ((busword_t) addr & PAGE_OFFSET_MASK); 610 | addr = (void *) ((busword_t) addr & ~(busword_t) PAGE_OFFSET_MASK); 611 | 612 | if ((addr = mmap (addr, len + offset + MMAP_EXTRA_BYTES, PROT_EXEC | PROT_READ | PROT_WRITE, flags, 0, 0)) == (void *) -1) 613 | { 614 | error ("mmap filed: %s\n", strerror (errno)); 615 | close (fd); 616 | return -1; 617 | } 618 | 619 | if (read (fd, addr + offset, len) < len) 620 | { 621 | error ("short read: %s\n", strerror (errno)); 622 | munmap (addr, len + offset); 623 | close (fd); 624 | 625 | return -1; 626 | } 627 | 628 | close (fd); 629 | 630 | memset (addr, NOP_BYTE, offset); 631 | *place = addr + offset; 632 | 633 | return len; 634 | } 635 | 636 | void * 637 | lib_open (const char *path) 638 | { 639 | void *handle; 640 | int i; 641 | struct lib_info *new; 642 | 643 | for (i = 0; i < libs_count; i++) 644 | if (strcmp (libs_list[i]->name, path) == 0) 645 | return libs_list[i]->handle; 646 | 647 | if ((handle = dlopen (path, RTLD_LAZY)) == NULL) 648 | return NULL; 649 | 650 | new = xmalloc (sizeof (struct lib_info)); 651 | 652 | new->name = xstrdup (path); 653 | new->handle = handle; 654 | 655 | PTR_LIST_APPEND (libs, new); 656 | 657 | return handle; 658 | } 659 | 660 | 661 | void 662 | queue_trampoline (void *from, void *to) 663 | { 664 | struct trampoline *tramp; 665 | 666 | tramp = xmalloc (sizeof (struct trampoline)); 667 | 668 | tramp->from = from; 669 | tramp->to = to; 670 | 671 | PTR_LIST_APPEND (trampolines, tramp); 672 | } 673 | 674 | void 675 | setup_trampolines (void) 676 | { 677 | int i; 678 | 679 | for (i = 0; i < trampolines_count; i++) 680 | if (trampoline_register (trampolines_list[i]->from, trampolines_list[i]->to) == -1) 681 | { 682 | error ("trampoline allocation failed for binding %p to %p\n", 683 | trampolines_list[i]->from, trampolines_list[i]->to); 684 | exit (1); 685 | } 686 | } 687 | 688 | int 689 | main (int argc, char **argv) 690 | { 691 | int len; 692 | int i; 693 | int n; 694 | char *type, *value; 695 | int index; 696 | 697 | char *name, *lib, *where; 698 | void *addr; 699 | void *handle; 700 | char c; 701 | int params = 0; 702 | 703 | static struct option long_options[] = 704 | { 705 | {"bind-function", 1, 0, 'f'}, 706 | {"base", 1, 0, 'b'}, 707 | {"show-siginfo", 0, 0, 's'}, 708 | {"verbose", 0, 0, 'v'}, 709 | {"debug", 0, 0, 'T'}, 710 | {"show-result", 0, 0, 'e'}, 711 | {"show-registers", 0, 0, 'r'}, 712 | {"show-mark", 0, 0, 'm'}, 713 | 714 | {"help", 1, 0, 'h'}, 715 | 716 | {0, 0, 0, 0} 717 | }; 718 | 719 | base = NULL; 720 | 721 | struct snippet_call_info *info; 722 | 723 | while ((c = getopt_long (argc, argv, "b:f:srvemTh", long_options, &index)) != -1) 724 | switch (c) 725 | { 726 | case 'b': 727 | if (!sscanf (optarg, "%i", (int *) &base)) 728 | { 729 | fprintf (stderr, "%s: wrong base address\n", argv[0]); 730 | exit (1); 731 | } 732 | 733 | break; 734 | 735 | case 'm': 736 | mark_output++; 737 | break; 738 | 739 | case 'T': 740 | do_trap++; 741 | break; 742 | 743 | case 's': 744 | print_siginfo++; 745 | break; 746 | 747 | case 'r': 748 | print_regdump++; 749 | break; 750 | 751 | case 'e': 752 | print_retval++; 753 | break; 754 | 755 | case 'v': 756 | verbose++; 757 | break; 758 | 759 | /* Calling convention missing */ 760 | case 'f': 761 | name = xmalloc (strlen (optarg)); 762 | lib = xmalloc (strlen (optarg)); 763 | 764 | if (sscanf (optarg, "%[^:]:%[^:]:%i", lib, name, (int *) &where) != 3) 765 | { 766 | fprintf (stderr, "%s: wrong function binding syntax\n", argv[0]); 767 | exit (1); 768 | } 769 | 770 | if ((handle = lib_open (lib)) == NULL) 771 | { 772 | fprintf (stderr, "%s: couldn't open %s: %s\n", argv[0], lib, dlerror ()); 773 | exit (1); 774 | } 775 | 776 | /* No NULL symbols accepted, period. */ 777 | if ((addr = dlsym (handle, name)) == NULL) 778 | { 779 | fprintf (stderr, "%s: symbol `%s' either NULL or not found\n", argv[0], name); 780 | exit (1); 781 | } 782 | 783 | queue_trampoline (where, addr); 784 | 785 | free (name); 786 | free (lib); 787 | 788 | break; 789 | 790 | 791 | case 'h': 792 | help (argv[0]); 793 | return 0; 794 | 795 | 796 | case '?': 797 | if (isprint (c)) 798 | { 799 | help (argv[0]); 800 | exit (1); 801 | } 802 | } 803 | 804 | 805 | for (i = optind; i < argc; i++) 806 | { 807 | if (!params++) 808 | { 809 | if ((len = load_snippet (argv[i], base, &base)) == -1) 810 | { 811 | fprintf (stderr, "%s: %s: couldn't load snippet: %s\n", 812 | argv[0], argv[i], strerror (errno)); 813 | 814 | exit (1); 815 | } 816 | 817 | info = snippet_call_info_new (CALL_TYPE_STDCALL); 818 | } 819 | else 820 | { 821 | type = xmalloc (strlen (argv[i])); 822 | value = xmalloc (strlen (argv[i])); 823 | 824 | if (sscanf (argv[i], "%[^:]:%[^:]", type, value) != 2) 825 | { 826 | fprintf (stderr, "%s: invalid argument description\n", argv[0]); 827 | exit (1); 828 | } 829 | 830 | if (strcmp (type, "s") == 0 || strcmp (type, "str") == 0) 831 | snippet_append_argument (info, xstrdup (value)); 832 | else if (strcmp (type, "i") == 0 || strcmp (type, "int") == 0) 833 | { 834 | if (!sscanf (value, "%i", &n)) 835 | { 836 | fprintf (stderr, "%s: invalid integer value \"%s\"\n", argv[0], value); 837 | exit (1); 838 | } 839 | snippet_append_argument (info, (void *) n); 840 | } 841 | else if (strcmp (type, "u") == 0 || strcmp (type, "uint") == 0) 842 | { 843 | if (!sscanf (value, "%u", &n)) 844 | { 845 | fprintf (stderr, "%s: invalid integer value \"%s\"\n", argv[0], value); 846 | exit (1); 847 | } 848 | snippet_append_argument (info, (void *) n); 849 | } 850 | else if (strcmp (type, "x") == 0 || strcmp (type, "hex") == 0) 851 | { 852 | if (!sscanf (value, "0x%x", &n)) 853 | { 854 | fprintf (stderr, "%s: invalid integer value \"%s\"\n", argv[0], value); 855 | exit (1); 856 | } 857 | snippet_append_argument (info, (void *) n); 858 | } 859 | else 860 | { 861 | fprintf (stderr, "%s: invalid argument type `%s' '\n", argv[0], type); 862 | exit (1); 863 | } 864 | 865 | free (type); 866 | free (value); 867 | } 868 | } 869 | 870 | if (params == 0) 871 | { 872 | fprintf (stderr, "%s: no input file given\n", argv[0]); 873 | help (argv[0]); 874 | exit (1); 875 | } 876 | 877 | sighandler_stack_top = xmalloc (SIGHANDLER_STACK_SIZE); 878 | 879 | debug ("alternative stack was allocated: 0x%08x\n", (unsigned int) sighandler_stack_top); 880 | snippet_set_address (info, base, len); 881 | snippet_setup_stack (info, DEFAULT_STACK_SIZE, PROT_READ | PROT_WRITE); 882 | setup_trampolines (); 883 | snippet_enter (info); 884 | 885 | return 0; 886 | } 887 | 888 | --------------------------------------------------------------------------------