浅析house of cat(中)

前言

这篇是浅析house of cat(上)-CSDN博客的续写,主要想从源码调试的角度去讲解一下具体流程。

这里选择的例题是强网杯2022的houseofcat,这里我只是使用已经写好的脚本去调试,讲解一些上篇中没指出来,但需要我们绕过的地方。

调试

calloc-->>__malloc_assert

这里假设我们已经能够触发__malloc_assert()函数了,开始进入分配函数calloc():

下断点在sysmalloc函数:

开始验证条件:

因为topchunk没有页对齐,所以开始调用__malloc_assert()函数:

之后就是正常去调用__fxprintf:

 __fxprintf -->> __vfxprintf 

这一步是没有什么问题的,正常进入。

 进入到__vfxprintf函数之后就到了我们特别需要注意的地方:

我们来看一下这个函数的源码:

#define _IO_flockfile(_fp) if (((_fp)->_flags & _IO_USER_LOCK) == 0) _IO_lock_lock (*(_fp)->_lock)

_IO_lock_lock的源码:

#define _IO_lock_lock(_name) \
  do {									      \
    void *__self = THREAD_SELF;						      \
    if (SINGLE_THREAD_P && (_name).owner == NULL)			      \
      {									      \
	(_name).lock = LLL_LOCK_INITIALIZER_LOCKED;			      \
	(_name).owner = __self;						      \
      }									      \
    else if ((_name).owner != __self)					      \
      {									      \
	lll_lock ((_name).lock, LLL_PRIVATE);				      \
	(_name).owner = __self;						      \
      }									      \
    else								      \
      ++(_name).cnt;							      \
  } while (0)

看到网上有人说这里:“可以看到进了之后就是一个无限循环,死锁。所以我们要绕过这个,通过上面的代码可以得到条件”,这种说法其实是不对的,do{}whlie(0)其实只是一种特殊的宏写法,表面上在这里一点意义都没有,只执行一次而已。但是锁的说法是对的:

这个宏 _IO_lock_lock,用于处理锁的逻辑。逐步分析这个宏的功能:

  1. 宏定义:_IO_lock_lock(_name) 是一个宏,它接收一个参数 _name,这个参数代表要操作的锁。

  2. 获取当前线程:void *__self = THREAD_SELF; 这行代码获取当前线程的标识,并将其存储在局部变量 __self 中。THREAD_SELF 也是一个宏,用于获取当前线程的标识符。

  3. 检查锁的状态:if ((_name).owner != __self) 检查当前线程是否已经拥有这个锁。如果锁的拥有者 (owner) 不是当前线程 (__self),则执行以下操作:

    • lll_lock ((_name).lock, LLL_PRIVATE);:调用 lll_lock 函数来锁定 _name 指定的锁。LLL_PRIVATE 是一个参数,表示锁的类型或锁定方式。
    • (_name).owner = __self;:将锁的拥有者设置为当前线程。
  4. 增加锁的计数:++(_name).cnt; 增加锁的计数。这可能是为了跟踪锁被同一线程重复获取的次数。 

想要绕过这个死锁就必须让if语句判断不成功,那就得让 ((_fp)->_flags & _IO_USER_LOCK) == 0不成立。但是很不行,这个判断很难绕过去,但是无伤大雅,我们只需要让fp->_lock是一个可以指向的地址,能够让他完成这些操作就可以了。

 locked_vfxprintf -> vfprintf_internal

接下来就是正常的调用链了,程序进入locked_vfxprintf函数,之后去调用vfprintf_internal函数。

之后走vtable检查:

 只要通过vtable检查,我们就可以通过return vtable来返回到我们需要的函数, 在上篇中我们了解到他希望去调用vtable指针_IO_wfile_jumps所指向的结构体中的_IO_file_xsputn,但是我们将_IO_wfile_jumps+0x10,他依然按照vtable+0x38的方式去调用,那么就会去调用到_IO_wfile_jumps+0x48,也就是__GI__IO_wfile_seekoff处:

接下来还有一个问题,我们必须让was_writing不为0才行,

所以我们必须让fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base 

跳转到我们设置好的函数:

 之后就是栈迁移+orw了:

 总结:

这里给出所有需要绕过的地方:

  1. old_size >= 0x20; old_top.prev_inuse = 0; old_top页对齐(三个违法一个就行)
  2. fp.mode != 0
  3. 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)

  4. fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值