首页 > 其他分享 >浅析house of cat(上)

浅析house of cat(上)

时间:2024-08-15 12:55:50浏览次数:15  
标签:fp wide int house cat base IO data 浅析

前言

原本以为只学习house of apple2就足以应对所有高版本了,但是还是被白名单制裁了,这里浅析一下house of cat以弥补apple2的缺陷。

house of cat预计分三个部分来讲解,这篇是源码原理分析。中篇主要讲解源码调试和一些在本篇里没有讲解到的小细节绕过,下篇主要讲解例题。

我个人认为house of cat最大的用处是在程序没有exit函数和使用白名单过滤,并且我们只能利用largbinattack的时候。本次源码分析也是基于glibc-2.35。

house of cat

前置知识:

_IO_wfile_jumps

const struct _IO_jump_t _IO_wfile_jumps =
{
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, _IO_new_file_finish),
  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),
  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
  JUMP_INIT(xsputn, _IO_wfile_xsputn),
  JUMP_INIT(xsgetn, _IO_file_xsgetn),
  JUMP_INIT(seekoff, _IO_wfile_seekoff),
  JUMP_INIT(seekpos, _IO_default_seekpos),
  JUMP_INIT(setbuf, _IO_new_file_setbuf),
  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
  JUMP_INIT(doallocate, _IO_wfile_doallocate),
  JUMP_INIT(read, _IO_file_read),
  JUMP_INIT(write, _IO_new_file_write),
  JUMP_INIT(seek, _IO_file_seek),
  JUMP_INIT(close, _IO_file_close),
  JUMP_INIT(stat, _IO_file_stat),
  JUMP_INIT(showmanyc, _IO_default_showmanyc),
  JUMP_INIT(imbue, _IO_default_imbue)
};

 在house of apple2内我们利用的是:

JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow)

house of cat利用的则是: 

JUMP_INIT(seekoff, _IO_wfile_seekoff),

我们来看看这个函数: 

_IO_wfile_seekoff 

off64_t
_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
{
  off64_t result;
  off64_t delta, new_offset;
  long int count;

  /*短路变成一个单独的功能。 我们不想混合任何功能,也不想触及 FILE 对象内部的任何内容。*/
  if (mode == 0)
    return do_ftell_wide (fp);

  /* POSIX.1 8.2.3.7 says that after a call the fflush() the file
     offset of the underlying file must be exact.  */
  int must_be_exact = ((fp->_wide_data->_IO_read_base
			== fp->_wide_data->_IO_read_end)
		       && (fp->_wide_data->_IO_write_base
			   == fp->_wide_data->_IO_write_ptr));

  bool was_writing = ((fp->_wide_data->_IO_write_ptr
		       > fp->_wide_data->_IO_write_base)
		      || _IO_in_put_mode (fp));

  /*刷新未写入的字符。(如果我们在缓冲区内查找,这可能会执行不必要的写入。但是为了能够切换到阅读,我们需要将 egptr 设置为 pptr。 这在当前的设计中是无法做到的,它假设 file_ptr() 是 eGptr。 无论如何,由于我们可能在close()时最终刷新,因此没有太大区别。FIXME:模拟内存映射文件。*/
  if (was_writing && _IO_switch_to_wget_mode (fp))
    return WEOF;

  if (fp->_wide_data->_IO_buf_base == NULL)
    {
      /* It could be that we already have a pushback buffer.  */
      if (fp->_wide_data->_IO_read_base != NULL)
	{
	  free (fp->_wide_data->_IO_read_base);
	  fp->_flags &= ~_IO_IN_BACKUP;
	}
      _IO_doallocbuf (fp);
      _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
      _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
      _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
		 fp->_wide_data->_IO_buf_base);
      _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
		 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
    }

  switch (dir)
    {
      struct _IO_codecvt *cv;
      int clen;

    case _IO_seek_cur:
      /*调整预读(字节是缓冲区)。 为此,我们必须找出外部缓冲区中的哪个位置对应于内部缓冲区中的当前位置。*/
      cv = fp->_codecvt;
      clen = __libio_codecvt_encoding (cv);

      if (mode != 0 || !was_writing)
	{
	  if (clen > 0)
	    {
	      offset -= (fp->_wide_data->_IO_read_end
			 - fp->_wide_data->_IO_read_ptr) * clen;
	      /* Adjust by readahead in external buffer.  */
	      offset -= fp->_IO_read_end - fp->_IO_read_ptr;
	    }
	  else
	    {
	      int nread;

	      delta = (fp->_wide_data->_IO_read_ptr
		       - fp->_wide_data->_IO_read_base);
	      fp->_wide_data->_IO_state = fp->_wide_data->_IO_last_state;
	      nread = __libio_codecvt_length (cv,
					      &fp->_wide_data->_IO_state,
					      fp->_IO_read_base,
					      fp->_IO_read_end, delta);
	      fp->_IO_read_ptr = fp->_IO_read_base + nread;
	      fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_read_ptr;
	      offset -= fp->_IO_read_end - fp->_IO_read_base - nread;
	    }
	}

      if (fp->_offset == _IO_pos_BAD)
	goto dumb;

      /* Make offset absolute, assuming current pointer is file_ptr(). */
      offset += fp->_offset;

      dir = _IO_seek_set;
      break;
    case _IO_seek_set:
      break;
    case _IO_seek_end:
      {
	struct __stat64_t64 st;
	if (_IO_SYSSTAT (fp, &st) == 0 && S_ISREG (st.st_mode))
	  {
	    offset += st.st_size;
	    dir = _IO_seek_set;
	  }
	else
	  goto dumb;
      }
    }

  _IO_free_wbackup_area (fp);

  /* At this point, dir==_IO_seek_set. */

  /*如果目标在当前缓冲区内,请优化:*/
  if (fp->_offset != _IO_pos_BAD && fp->_IO_read_base != NULL
      && !_IO_in_backup (fp))
    {
      off64_t start_offset = (fp->_offset
                              - (fp->_IO_read_end - fp->_IO_buf_base));
      if (offset >= start_offset && offset < fp->_offset)
	{
	  _IO_setg (fp, fp->_IO_buf_base,
		    fp->_IO_buf_base + (offset - start_offset),
		    fp->_IO_read_end);
	  _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
	  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
		     fp->_wide_data->_IO_buf_base,
		     fp->_wide_data->_IO_buf_base);
	  _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
		     fp->_wide_data->_IO_buf_base);

	  if (adjust_wide_data (fp, false))
	    goto dumb;

	  _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
	  goto resync;
	}
    }

  if (fp->_flags & _IO_NO_READS)
    goto dumb;

  /*尝试寻求块边界,以改善内核页面管理。*/
  new_offset = offset & ~(fp->_IO_buf_end - fp->_IO_buf_base - 1);
  delta = offset - new_offset;
  if (delta > fp->_IO_buf_end - fp->_IO_buf_base)
    {
      new_offset = offset;
      delta = 0;
    }
  result = _IO_SYSSEEK (fp, new_offset, 0);
  if (result < 0)
    return EOF;
  if (delta == 0)
    count = 0;
  else
    {
      count = _IO_SYSREAD (fp, fp->_IO_buf_base,
			   (must_be_exact
			    ? delta : fp->_IO_buf_end - fp->_IO_buf_base));
      if (count < delta)
	{
	  /*我们不被允许阅读,但试着寻找剩下的。*/
	  offset = count == EOF ? delta : delta-count;
	  dir = _IO_seek_cur;
	  goto dumb;
	}
    }
  _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base + delta,
	    fp->_IO_buf_base + count);
  _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
  _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
	     fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
  _IO_wsetp (fp, fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);

  if (adjust_wide_data (fp, true))
    goto dumb;

  fp->_offset = result + count;
  _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
  return offset;
 dumb:

  _IO_unsave_markers (fp);
  result = _IO_SYSSEEK (fp, offset, dir);
  if (result != EOF)
    {
      _IO_mask_flags (fp, 0, _IO_EOF_SEEN);
      fp->_offset = result;
      _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
      _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base);
      _IO_wsetg (fp, fp->_wide_data->_IO_buf_base,
		 fp->_wide_data->_IO_buf_base, fp->_wide_data->_IO_buf_base);
      _IO_wsetp (fp, fp->_wide_data->_IO_buf_base,
		 fp->_wide_data->_IO_buf_base);
    }
  return result;

resync:
  /*我们需要这样做,因为内核中的文件偏移量可能会在我们背后发生更改。当我们打开一个文件,然后进行分叉时,可能会发生这种情况。
  一个进程可能会访问该文件,内核文件的偏移量将发生变化。*/
  if (fp->_offset >= 0)
    _IO_SYSSEEK (fp, fp->_offset, 0);

  return offset;
}

这里我们只关注两个地方就可以了:

,也就是说我们只要让传入的mode不为0,就能正常进行到_IO_switch_to_wget_mode (fp)这个函数。

 _IO_switch_to_wget_mode()

int
_IO_switch_to_wget_mode (FILE *fp)
{
  if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
    if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)
      return EOF;
  if (_IO_in_backup (fp))
    fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_backup_base;
  else
    {
      fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_buf_base;
      if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_read_end)
	fp->_wide_data->_IO_read_end = fp->_wide_data->_IO_write_ptr;
    }
  fp->_wide_data->_IO_read_ptr = fp->_wide_data->_IO_write_ptr;

  fp->_wide_data->_IO_write_base = fp->_wide_data->_IO_write_ptr
    = fp->_wide_data->_IO_write_end = fp->_wide_data->_IO_read_ptr;

  fp->_flags &= ~_IO_CURRENTLY_PUTTING;
  return 0;
}

学习过house of apple2过后,我们应该能看到一个我们非常喜爱的函数:

 就是说只需要满足fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base就能控制执行流了。

__malloc_assert

因为存在exit函数,那不如去打apple2,所以这里只讨论一下其他触发方式。

_malloc_assert函数,他会根据vtable表如_IO_xxx_jumps调用IO等相关函数;该函数最终会根据stderr这个IO结构体进行相关的IO操作。

static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
		 const char *function)
{
  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
		     __progname, __progname[0] ? ": " : "",
		     file, line,
		     function ? function : "", function ? ": " : "",
		     assertion);
  fflush (stderr);
  abort ();
}

__fxprintf() 

来看看 __fxprintf()这个函数,

int
__fxprintf (FILE *fp, const char *fmt, ...)
{
  va_list ap;
  va_start (ap, fmt);
  int res = __vfxprintf (fp, fmt, ap, 0);
  va_end (ap);
  return res;
}

__vfxprintf() 

接着走下去,看__vfxprintf()函数,

int
__vfxprintf (FILE *fp, const char *fmt, va_list ap,
	     unsigned int mode_flags)
{
  if (fp == NULL)
    fp = stderr;
  _IO_flockfile (fp);
  int res = locked_vfxprintf (fp, fmt, ap, mode_flags);
  _IO_funlockfile (fp);
  return res;
}

 locked_vfxprintf

static int
locked_vfxprintf (FILE *fp, const char *fmt, va_list ap,
		  unsigned int mode_flags)
{
  if (_IO_fwide (fp, 0) <= 0)
    return __vfprintf_internal (fp, fmt, ap, mode_flags);

  /* We must convert the narrow format string to a wide one.
     Each byte can produce at most one wide character.  */
  wchar_t *wfmt;
  mbstate_t mbstate;
  int res;
  int used_malloc = 0;
  size_t len = strlen (fmt) + 1;

  if (__glibc_unlikely (len > SIZE_MAX / sizeof (wchar_t)))
    {
      __set_errno (EOVERFLOW);
      return -1;
    }
  if (__libc_use_alloca (len * sizeof (wchar_t)))
    wfmt = alloca (len * sizeof (wchar_t));
  else if ((wfmt = malloc (len * sizeof (wchar_t))) == NULL)
    return -1;
  else
    used_malloc = 1;

  memset (&mbstate, 0, sizeof mbstate);
  res = __mbsrtowcs (wfmt, &fmt, len, &mbstate);

  if (res != -1)
    res = __vfwprintf_internal (fp, wfmt, ap, mode_flags);

  if (used_malloc)
    free (wfmt);

  return res;
}

这里我们让他去走__vfprintf_internal这个函数就好。 

 __vfprintf_internal:

这个函数实际上是__vfprintf_internal.c文件内的vfprintf。具体原因我也不是很清楚,有大佬知道的话可以评论区讲解一下。

这个函数的实现有点长,不想细读的可以看下一个代码块。

int
vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
{
  /* The character used as thousands separator.  */
  THOUSANDS_SEP_T thousands_sep = 0;

  /* The string describing the size of groups of digits.  */
  const char *grouping;

  /* Place to accumulate the result.  */
  int done;

  /* Current character in format string.  */
  const UCHAR_T *f;

  /* End of leading constant string.  */
  const UCHAR_T *lead_str_end;

  /* Points to next format specifier.  */
  const UCHAR_T *end_of_spec;

  /* Buffer intermediate results.  */
  CHAR_T work_buffer[WORK_BUFFER_SIZE];
  CHAR_T *workend;

  /* We have to save the original argument pointer.  */
  va_list ap_save;

  /* Count number of specifiers we already processed.  */
  int nspecs_done;

  /* For the %m format we may need the current `errno' value.  */
  int save_errno = errno;

  /* 1 if format is in read-only memory, -1 if it is in writable memory,
     0 if unknown.  */
  int readonly_format = 0;

  /* Orient the stream.  */
#ifdef ORIENT
  ORIENT;
#endif

  /* Sanity check of arguments.  */
  ARGCHECK (s, format);

#ifdef ORIENT
  /* Check for correct orientation.  */
  if (_IO_vtable_offset (s) == 0
      && _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
      != (sizeof (CHAR_T) == 1 ? -1 : 1))
    /* The stream is already oriented otherwise.  */
    return EOF;
#endif

  if (UNBUFFERED_P (s))
    /* Use a helper function which will allocate a local temporary buffer
       for the stream and then call us again.  */
    return buffered_vfprintf (s, format, ap, mode_flags);

  /* Initialize local variables.  */
  done = 0;
  grouping = (const char *) -1;
#ifdef __va_copy
  /* This macro will be available soon in gcc's <stdarg.h>.  We need it
     since on some systems `va_list' is not an integral type.  */
  __va_copy (ap_save, ap);
#else
  ap_save = ap;
#endif
  nspecs_done = 0;

#ifdef COMPILE_WPRINTF
  /* Find the first format specifier.  */
  f = lead_str_end = __find_specwc ((const UCHAR_T *) format);
#else
  /* Find the first format specifier.  */
  f = lead_str_end = __find_specmb ((const UCHAR_T *) format);
#endif

  /* Lock stream.  */
  _IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
  _IO_flockfile (s);

  /* Write the literal text before the first format.  */
  outstring ((const UCHAR_T *) format,
	     lead_str_end - (const UCHAR_T *) format);

  /* If we only have to print a simple string, return now.  */
  if (*f == L_('\0'))
    goto all_done;

  /* 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;

  /* Process whole format string.  */
  do
    {
      STEP0_3_TABLE;
      STEP4_TABLE;

      int is_negative;	/* Flag for negative number.  */
      union
      {
	unsigned long long int longlong;
	unsigned long int word;
      } number;
      int base;
      union printf_arg the_arg;
      CHAR_T *string;	/* Pointer to argument string.  */
      int alt = 0;	/* Alternate format.  */
      int space = 0;	/* Use space prefix if no sign is needed.  */
      int left = 0;	/* Left-justify output.  */
      int showsign = 0;	/* Always begin with plus or minus sign.  */
      int group = 0;	/* Print numbers according grouping rules.  */
      /* Argument is long double/long long int.  Only used if
	 double/long double or long int/long long int are distinct.  */
      int is_long_double __attribute__ ((unused)) = 0;
      int is_short = 0;	/* Argument is short int.  */
      int is_long = 0;	/* Argument is long int.  */
      int is_char = 0;	/* Argument is promoted (unsigned) char.  */
      int width = 0;	/* Width of output; 0 means none specified.  */
      int prec = -1;	/* Precision of output; -1 means none specified.  */
      /* This flag is set by the 'I' modifier and selects the use of the
	 `outdigits' as determined by the current locale.  */
      int use_outdigits = 0;
      UCHAR_T pad = L_(' ');/* Padding character.  */
      CHAR_T spec;

      workend = work_buffer + WORK_BUFFER_SIZE;

      /* Get current character in format string.  */
      JUMP (*++f, step0_jumps);

      /* ' ' flag.  */
    LABEL (flag_space):
      space = 1;
      JUMP (*++f, step0_jumps);

      /* '+' flag.  */
    LABEL (flag_plus):
      showsign = 1;
      JUMP (*++f, step0_jumps);

      /* The '-' flag.  */
    LABEL (flag_minus):
      left = 1;
      pad = L_(' ');
      JUMP (*++f, step0_jumps);

      /* The '#' flag.  */
    LABEL (flag_hash):
      alt = 1;
      JUMP (*++f, step0_jumps);

      /* The '0' flag.  */
    LABEL (flag_zero):
      if (!left)
	pad = L_('0');
      JUMP (*++f, step0_jumps);

      /* The '\'' flag.  */
    LABEL (flag_quote):
      group = 1;

      if (grouping == (const char *) -1)
	{
#ifdef COMPILE_WPRINTF
	  thousands_sep = _NL_CURRENT_WORD (LC_NUMERIC,
					    _NL_NUMERIC_THOUSANDS_SEP_WC);
#else
	  thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
#endif

	  grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
	  if (*grouping == '\0' || *grouping == CHAR_MAX
#ifdef COMPILE_WPRINTF
	      || thousands_sep == L'\0'
#else
	      || *thousands_sep == '\0'
#endif
	      )
	    grouping = NULL;
	}
      JUMP (*++f, step0_jumps);

    LABEL (flag_i18n):
      use_outdigits = 1;
      JUMP (*++f, step0_jumps);

      /* Get width from argument.  */
    LABEL (width_asterics):
      {
	const UCHAR_T *tmp;	/* Temporary value.  */

	tmp = ++f;
	if (ISDIGIT (*tmp))
	  {
	    int pos = read_int (&tmp);

	    if (pos == -1)
	      {
		__set_errno (EOVERFLOW);
		done = -1;
		goto all_done;
	      }

	    if (pos && *tmp == L_('$'))
	      /* The width comes from a positional parameter.  */
	      goto do_positional;
	  }
	width = va_arg (ap, int);

	/* Negative width means left justified.  */
	if (width < 0)
	  {
	    width = -width;
	    pad = L_(' ');
	    left = 1;
	  }
      }
      JUMP (*f, step1_jumps);

      /* Given width in format string.  */
    LABEL (width):
      width = read_int (&f);

      if (__glibc_unlikely (width == -1))
	{
	  __set_errno (EOVERFLOW);
	  done = -1;
	  goto all_done;
	}

      if (*f == L_('$'))
	/* Oh, oh.  The argument comes from a positional parameter.  */
	goto do_positional;
      JUMP (*f, step1_jumps);

    LABEL (precision):
      ++f;
      if (*f == L_('*'))
	{
	  const UCHAR_T *tmp;	/* Temporary value.  */

	  tmp = ++f;
	  if (ISDIGIT (*tmp))
	    {
	      int pos = read_int (&tmp);

	      if (pos == -1)
		{
		  __set_errno (EOVERFLOW);
		  done = -1;
		  goto all_done;
		}

	      if (pos && *tmp == L_('$'))
		/* The precision comes from a positional parameter.  */
		goto do_positional;
	    }
	  prec = va_arg (ap, int);

	  /* If the precision is negative the precision is omitted.  */
	  if (prec < 0)
	    prec = -1;
	}
      else if (ISDIGIT (*f))
	{
	  prec = read_int (&f);

	  /* The precision was specified in this case as an extremely
	     large positive value.  */
	  if (prec == -1)
	    {
	      __set_errno (EOVERFLOW);
	      done = -1;
	      goto all_done;
	    }
	}
      else
	prec = 0;
      JUMP (*f, step2_jumps);

      /* Process 'h' modifier.  There might another 'h' following.  */
    LABEL (mod_half):
      is_short = 1;
      JUMP (*++f, step3a_jumps);

      /* Process 'hh' modifier.  */
    LABEL (mod_halfhalf):
      is_short = 0;
      is_char = 1;
      JUMP (*++f, step4_jumps);

      /* Process 'l' modifier.  There might another 'l' following.  */
    LABEL (mod_long):
      is_long = 1;
      JUMP (*++f, step3b_jumps);

      /* Process 'L', 'q', or 'll' modifier.  No other modifier is
	 allowed to follow.  */
    LABEL (mod_longlong):
      is_long_double = 1;
      is_long = 1;
      JUMP (*++f, step4_jumps);

    LABEL (mod_size_t):
      is_long_double = sizeof (size_t) > sizeof (unsigned long int);
      is_long = sizeof (size_t) > sizeof (unsigned int);
      JUMP (*++f, step4_jumps);

    LABEL (mod_ptrdiff_t):
      is_long_double = sizeof (ptrdiff_t) > sizeof (unsigned long int);
      is_long = sizeof (ptrdiff_t) > sizeof (unsigned int);
      JUMP (*++f, step4_jumps);

    LABEL (mod_intmax_t):
      is_long_double = sizeof (intmax_t) > sizeof (unsigned long int);
      is_long = sizeof (intmax_t) > sizeof (unsigned int);
      JUMP (*++f, step4_jumps);

      /* Process current format.  */
      while (1)
	{
#define process_arg_int() va_arg (ap, int)
#define process_arg_long_int() va_arg (ap, long int)
#define process_arg_long_long_int() va_arg (ap, long long int)
#define process_arg_pointer() va_arg (ap, void *)
#define process_arg_string() va_arg (ap, const char *)
#define process_arg_unsigned_int() va_arg (ap, unsigned int)
#define process_arg_unsigned_long_int() va_arg (ap, unsigned long int)
#define process_arg_unsigned_long_long_int() va_arg (ap, unsigned long long int)
#define process_arg_wchar_t() va_arg (ap, wchar_t)
#define process_arg_wstring() va_arg (ap, const wchar_t *)
	  process_arg ();
	  process_string_arg ();
#undef process_arg_int
#undef process_arg_long_int
#undef process_arg_long_long_int
#undef process_arg_pointer
#undef process_arg_string
#undef process_arg_unsigned_int
#undef process_arg_unsigned_long_int
#undef process_arg_unsigned_long_long_int
#undef process_arg_wchar_t
#undef process_arg_wstring

	LABEL (form_float):
	LABEL (form_floathex):
	  {
	    if (__glibc_unlikely ((mode_flags & PRINTF_LDBL_IS_DBL) != 0))
	      is_long_double = 0;

	    struct printf_info info =
	      {
		.prec = prec,
		.width = width,
		.spec = spec,
		.is_long_double = is_long_double,
		.is_short = is_short,
		.is_long = is_long,
		.alt = alt,
		.space = space,
		.left = left,
		.showsign = showsign,
		.group = group,
		.pad = pad,
		.extra = 0,
		.i18n = use_outdigits,
		.wide = sizeof (CHAR_T) != 1,
		.is_binary128 = 0
	      };

	    PARSE_FLOAT_VA_ARG_EXTENDED (info);
	    const void *ptr = &the_arg;

	    int function_done = __printf_fp_spec (s, &info, &ptr);
	    if (function_done < 0)
	      {
		done = -1;
		goto all_done;
	      }
	    done_add (function_done);
	  }
	  break;

	LABEL (form_unknown):
	  if (spec == L_('\0'))
	    {
	      /* The format string ended before the specifier is complete.  */
	      __set_errno (EINVAL);
	      done = -1;
	      goto all_done;
	    }

	  /* If we are in the fast loop force entering the complicated
	     one.  */
	  goto do_positional;
	}

      /* The format is correctly handled.  */
      ++nspecs_done;

      /* Look for next format specifier.  */
#ifdef COMPILE_WPRINTF
      f = __find_specwc ((end_of_spec = ++f));
#else
      f = __find_specmb ((end_of_spec = ++f));
#endif

      /* Write the following constant string.  */
      outstring (end_of_spec, f - end_of_spec);
    }
  while (*f != L_('\0'));

  /* Unlock stream and return.  */
  goto all_done;

  /* 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);

 all_done:
  /* Unlock the stream.  */
  _IO_funlockfile (s);
  _IO_cleanup_region_end (0);

  return done;
}

 上面这个函数我们只需要知道他最终是去调用了vtable指针_IO_wfile_jumps所指向的结构体中的_IO_file_xsputn,那我们只需要将vtable处的_IO_wfile_jumps+0x10就能顺利调用到_IO_wfile_seekoff 这个函数了。

后面就是类似于apple2的执行流程,通过vtable处的_IO_wfile_jumps去跳转执行_IO_wfile_seekoff 这个函数, 最终执行到_IO_switch_to_wget_mode()内的_IO_WOVERFLOW的宏定义。

总结:

函数执行流:

__malloc_assert -->> __fxprintf() -->> __vfxprintf()  -->>  locked_vfxprintf()
-->> _IO_wfile_jumps -->> _IO_wfile_seekoff -->>  _IO_switch_to_wget_mode()
-->> _IO_WOVERFLOW

绕过总结:

  1. fp.mode != 0
  2. fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base                                  或者         _IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)

  3. fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

当然这里面有个小细节:

从FSOP的角度去看,我们希望去调用到 _IO_wfile_seekoff这个函数,那么我们对于vtable的设置应该设置为_IO_wfile_jumps+0x30,但是在走__malloc_assert这条调用链时,我们只需要设置为_IO_wfile_jumps+0x10

怎么触发__malloc_assert?

 这个函数的调用链为:_int_malloc --->> sysmalloc --->> __malloc_assert

_int_malloc这个也是一个大家伙,只看调用sysmalloc的地方:

 点进去查看sysmalloc函数内的调用__malloc_assert()代码段:

检测条件

  1. old_size >= 0x20;
  2. old_top.prev_inuse = 0;
  3. old_top页对齐

 也就是说,我们只要满足这三个条件中的任何一个就可以触发了。

到这里上篇就结束了。如有错误,欢迎评论区指出,谢谢!

标签:fp,wide,int,house,cat,base,IO,data,浅析
From: https://blog.csdn.net/2301_79327647/article/details/141166770

相关文章

  • 学习009-01 Create a Standalone Web API Application(创建一个独立的 Web API 应用程
    CreateaStandaloneWebAPIApplication(创建一个独立的WebAPI应用程序)Thistopiccontainsstep-by-stepinstructionsonhowtocreateanapplicationwiththeWebAPI.FormoreinformationontheWebAPI,seethefollowingtopic:BackendWebAPIService......
  • ADALORA: ADAPTIVE BUDGET ALLOCATION FOR PARAMETER-EFFICIENT FINE-TUNING 笔记
    ADALORA的前世今生......
  • Linux Web项目部署过程命令 Tomcat组件
    LinuxWeb项目部署过程命令Tomcat,本文涉及的是打成jar包方式的前后端分离项目,打成war包的方式也有解释介绍。1.连接服务器2.利用lsof命令查看web后端部署端口进程,然后kill杀死进程,                            ......
  • Sorting By Multiplication
    严谨证明其实真的很难设每次操作为\((l,r,x)\),其中\(l,r\)表示操作的左右端点,\(x\)表示乘以的值首先我们知道,最后由于严格升序,所以数列分成三段,第一段为负数,第二段为\(0\),第三段为正数;操作之间的顺序无关紧要;操作之间不会跨段:如果有跨段,那么一定是跨了三段(只跨两段的话,有一段就......
  • 简单的spring boot tomcat版本升级
    简单的springboottomcat版本升级1.需求我们使用的springboot版本为2.3.8.RELEASE,对应的tomcat版本为9.0.41,公司tomcat对应版本发现攻击者可发送不完整的POST请求触发错误响应,从而可能导致获取其他用户先前请求的数据,造成信息泄露的bug,行方要求对tomcat版本进行升级,受......
  • CentOS7系统安装Tomcat
    系统:Windows11家庭中文版应用:VMwareWorkstationPro  FinalShell4.3.10使用包:apache-tomcat-8.5.53.tar.gz目录:1.安装Tomcat     2.修改端口号     3.如何使用浏览器访问tomcat        4.配置Tomcat服务安装        前提......
  • cats 的电脑中毒
    要把二进制数的“每一位”取反,用^((1<<n)-1),(~运算会得到一个负数,而且也没有取出前n位)点击查看代码#include<bits/stdc++.h>usingnamespacestd;strings[5];intcnt[10],tot[10];//1表示可以通过改变这一位使得tot+1voidchange(intx){ cnt[x]--; for(inti=0;i<3;......
  • 一、Tomcat基础知识与运行原理
    本章节为介绍如何安装Tomcat工具以及其主要架构知识概念,深入浅出让新人玩家理解为什么选择该容器以及该容器的优点web服务器概念服务器:安装了服务器软件的计算机服务器软件:接收用户的请求,处理请求,做出响应web服务器软件:接收用户的请求,处理请求,做出响应。在web服务器软件......
  • 2024“钉耙编程”中国大学生算法设计超级联赛(8)1006 cats 的最小生成树
    题目大意:给出有\(n\)个点\(m\)条边的图,接下来进行若干次操作,每次操作取出当前图的最小生成树,然后删去这些构成最小生成树的边,知道该图不连通,输出每条边在第几次操作时被删除思路:由于构成最小生成树的边数是\(n-1\)条边,所以最多操作次数为\(\lfloor\frac{m}{n-1}\rfloor\),每次......
  • 【C++ Allocator】 详解C++的空间配置器和vector的底层实现以及push_back()和empalce_
    空间配置器用于管理动态内存分配和释放,STL容器类(如std::vector,std::list,std::map等)都使用配置器来管理内存。它有非常重要的特点:将容器的内存开辟和对象构造分离开将容器的对象析构和内存释放分离开这样能够高效的插入元素以及删除元素vectorSTL中典型的容器vec......