PWN学习随笔

随笔day1

程序 .plt表运行时与ELF文件对比

在这里插入图片描述
左边是运行时GDB调试,右边是静态文件。两边一一对应

bt 函数调用栈

在这里插入图片描述
gdb atttach后,发现离你想要看的目标函数有较远距离,可以用 finish命令,朝你的目标函数一层层迭代返回过去。执行了7次 finish后,当前下一帧到了pwnme()函数:
在这里插入图片描述
然后执行 step,进行单步调试。
在这里插入图片描述

push 0x18

在这里插入图片描述
从下面两张图可以看出 栈中的 0x180x0是怎么得到的:
在这里插入图片描述在这里插入图片描述

  • ds 应该存着.got表的基地址吧
  • dword_表示是数值型,off_表示函数偏移

原本栈数据是我构造的三个参数:

  • 0x1

  • 0x2

  • 0x3
    由于又执行了两次push操作,所以现在栈的数据变为:

  • 0x0

  • 0x18

  • 0x1

  • 0x2

  • 0x3

push eax

在这里插入图片描述
为了用 寄存器,先把寄存器值 push到栈中保存,然后在mov,也就是当前要执行的步骤。
顺便看下eax,查看eax保存的地址的内容

x/10wx  0xff98f7a0

正好有28个A(我构造的输入)
在这里插入图片描述

push ebp

进入 callme_one函数,第一步是 push ebp,这一步目的是把上一帧函数(调用者)的栈底地址(保存在ebp寄存器中),优先push到栈中保存着。
在这里插入图片描述
然后,mov ebp,esp,也就是让ebp寄存器新指向当前栈顶位置,也就是esp值传给ebp,见下图:
在这里插入图片描述
完成上面这一步,才能算从控制权从旧的frame(调用者),移动新的frame里(被调用者)了。
在这里插入图片描述
确实位置没有卡对,执行到 exit
在这里插入图片描述
我查了下exp原先的:

payload  = b'A'*44
payload +=  p32(callme_one) +\
            p32(1) +\
            p32(2) +\
            p32(3)

callme_one函数与参数之间,少了返回地址。

又重新构造

加个了pop操作。

pop3_ret = 0x080488a9
payload  = b'A'*44
payload +=  p32(callme_one) +\
            p32(pop3_ret) +\
            p32(1) +\
            p32(2) +\
            p32(3)

在这里插入图片描述
ebp+8的位置确实等于了0x1现在。执行到 callme_one的ret处,此时栈顶到了返回地址p32(pop3_ret)处,这里我是ROPgadget,要找3个连续pop操作的gad,因为我入栈3个参数,要一次性全部pop出去。
在这里插入图片描述
在这里插入图片描述
执行si一次:
在这里插入图片描述
执行si一次:
在这里插入图片描述
执行si一次:
在这里插入图片描述
再执行,就到ret了,而ret的地址就是callme_two函数:
在这里插入图片描述
前提没有开 canary
在这里插入图片描述

随笔day2

0x14u

v14 = __readgsdword(0x14u);

注释: 0x表示十六进制数,u表示无符号(unsigned long)。

进制转换

方法1:格式化转换 – str.format()

基本语法是通过 {} 和 : 来代替以前的 % 。
【注意:返回的是<class ‘str’> 字符串类型,不能用作计算】

举例:
print("网站名:{name}, 地址 {url}".format(name="菜鸟教程", url="www.runoob.com"))
网站名:菜鸟教程, 地址 www.runoob.com
#十进制 11 转为其他进制
# -----------------------
'{:b}'.format(11)
'{:d}'.format(11)
'{:o}'.format(11)
'{:x}'.format(11)
'{:#x}'.format(11)
'{:#X}'.format(11)	
# -----------------------
1011
11
13
b
0xb
0XB

#十六进制 0x14 转为其他进制
# -----------------------
'{:b}'.format(0x14)
'{:d}'.format(0x14)
'{:o}'.format(0x14)
'{:x}'.format(0x14)
'{:#b}'.format(0x14)
'{:#b}'.format(0x14)
# -----------------------
10100
20
24
14
0b10100
0b10100

#以上也可以简写: 
format(0x14,'d')   --> 20
方法2:内置函数 bin、oct、hex 实现转换

【注意:返回的是<class ‘str’> 字符串类型,不能用作计算】

>>> bin(3) # (10进制的)3转二进制
'0b11'

>>> oct(9) # (10进制的)9转二进制
'0o11'

>>> hex(17) # (10进制的)17转二进制
'0x11'

需要注意的是,该函数的返回值是一个字符串,不应将返回值进行计算。

bin(0x14)
>>> '0b10100'

bin(0x14)+1     # 返回错误
>>> TypeError: can only concatenate str (not "int") to str

如果需要计算,就要用到方法3,int转换下。

方法3:二、八、十六进制字符串格式转为为十进制整型,可用于计算

【返回的是<class ‘int’>整型,可用作十进制计算】

>>> int(bin(729), base = 2)  #base参数不可空,否则会报错;base参数为空时默认为10进制。
729

int('0x14',base=16)
>>>20

int('14',base=16)
>>>20

int('14',base=16) + 3
>>>23

>>> int("11",2) # (2进制的)"11"转十进制
3

>>> int("11",8) # (8进制的)"11"转十进制
9

>>> int("11",16) # (16进制的)"11"转十进制
17

格式化字符串漏洞
printf(&s);

不知道有没有同学感觉很别扭,我们学c语言时老师一般教给我们的是这样的:

printf("%s", s);

哎,他这个和老师教的不一样哎,这样可以吗? 答案是可以的,这样写是可以运行的,但同时这样写也是不允许的,为什么呢,因为这是不安全的,他涉及到格式化字符串漏洞: 一般的printf是这个样子的

printf("格式化字符串",参数...)

它的参数是由格式化说明符与字符串组成的,通过格式化说明符来规定参数用什么格式输出内容。 格式化说明符有这些:

%d - 十进制 - 输出十进制整数
%s - 字符串 - 从内存中读取字符串
%x - 十六进制 - 输出十六进制数
%c - 字符 - 输出字符
%p - 指针 - 指针地址
%n - 到目前为止所写的字符数

我们需要注意的是%n这个格式化字符串,它的功能是将%n之前打印出来的字符个数,赋值给一个变量,例如这样:
在这里插入图片描述
一个很神奇的结果出现在我们眼前,变量a的值被改变了。

%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100×10 n 表 示 将 0 x 64 写 入 偏 移 10 处 保 存 的 指 针 所 指 向 的 地 址 ( 4 字 节 ) , 而 n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而% n0x64104hn表示写入的地址空间为2字节,% h h n 表 示 写 入 的 地 址 空 间 为 1 字 节 , hhn表示写入的地址空间为1字节,% hhn1lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过% h n 或 hn或% hnhhn来适时调整。

%n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。
在这里插入图片描述
原文链接

在这里插入图片描述
在这里插入图片描述

会触发该漏洞的函数很有限,主要就是 printf、sprintf、fprintf 等 print 家族函数,以 printf
函数为例:
printf()函数的一般形式为 printf("format", 输出表列),其第一个参数就是格式化字符串,
用来告诉程序以什么格式进行输出。正常情况下,我们是这样使用的:

char str[100];
scanf("%s",str);
printf("%s",str);

但也会有人这么用:

char str[100];
scanf("%s",str);
printf(str)

上述代码的本意都是希望输入一段字符并且打印出来(e.g. “hello world”)但是二种写
法使得打印的结果被终端输入者控制,而不是程序本身。如果输入的内容中包含%s,%d,%f
这样的内容,printf 函数就会把这些处理成 format 参数。如果没有输出列表,就会将栈上存
储格式化字符串地址解析成在 pritnf 里面对应的 format 参数
在这里插入图片描述
以上图为例,假设调用 printf(str)时的栈是这样的。
1)如 str 就是“hello world”,则直接输出“hello world”;
2)如 str 是 format,比如是%2$x,则输出偏移 2 处的 16 进制数据 0xdeadbeef

常用基本的format格式化字符串参数介绍:
在这里插入图片描述
%x$n–> %x$n中的%x$表示往后偏移x个地址,%n表示前面出现的总共有多少个字符的数目。

payload = p32(pwnme) + 'aaaa' + '%10$n' // #pwnme的地址需要经过32位编码转换,是四位,而pwnme需要等于8,所以‘aaaa’起着凑字数的作用,'%10$n' :表示在prinft()函数 往后偏移10个地址的位置原变量,数值修改为=n(p32(pwnme)+aaaa)=8    

即:

  • 原: pwnme = 0
  • 现: pwnme = 8 n( p32(pwnme)+aaaa )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值