前言

以前在一些大型比赛就遇到这种题,一直没时间去研究,现在康复训练下:)

生成器介绍

生成器(Generator)是Python中一种特殊的迭代器,它可以在迭代过程中动态生成值,而不需要一次性将所有值存储在内存中。

Simple Demo

这里定义一个生成器函数, 生成器使用yield语句来产生值,每次调用生成器的next()方法时,生成器会执行直到遇到下一个yield语句为止,然后返回yield语句后面的值,也就是a的值

def f():
a = 1
while True:
yield a
a+=1
f = f()
print(next(f)) # 1
print(next(f)) # 2

也可以遍历获取所有的自增值

def f():
a = 1
for i in range(1, 100):
yield a
a+=1
f = f()
for value in f:
print(value)

生成器表达式

生成器表达式是一种在 Python 中创建生成器的紧凑形式。类似于列表推导式,生成器表达式允许你使用简洁的语法来定义生成器,而不必显式地编写一个函数。生成器表达式的语法与列表推导式类似,但是使用圆括号而不是方括号。生成器表达式会逐个生成值,而不是一次性生成整个序列。这有利于提高内存的利用率

f = (i+1 for i in range(100))
# 可以用next一步步获取值
print(next(f))
# 也可以遍历的形式获取全部值
for value in f:
print(value)

生成器属性

gi_code: 生成器对应的code对象。

gi_frame: 生成器对应的frame(栈帧)对象。

gi_running: 生成器函数是否在执行。生成器函数在yield以后、执行yield的下一行代码前处于frozen状态,此时这个属性的值为0。

gi_yieldfrom:如果生成器正在从另一个生成器中 yield 值,则为该生成器对象的引用;否则为 None。

gi_frame.f_locals:一个字典,包含生成器当前帧的本地变量。

gi_frame使用

gi_frame 是一个与生成器(generator)和协程(coroutine)相关的属性。它指向生成器或协程当前执行的帧对象(frame object),如果这个生成器或协程正在执行的话。帧对象表示代码执行的当前上下文,包含了局部变量、执行的字节码指令等信息。

def my_generator():
yield 1
yield 2
yield 3 gen = my_generator() # 获取生成器的当前帧信息
frame = gen.gi_frame # 输出生成器的当前帧信息
print("Local Variables:", frame.f_locals)
print("Global Variables:", frame.f_globals)
print("Code Object:", frame.f_code)
print("Instruction Pointer:", frame.f_lasti)

以上例子展示了如何获取生成器的帧信息

栈帧(frame)介绍

在 Python 中,栈帧(stack frame),也称为帧(frame),是用于执行代码的数据结构。每当 Python 解释器执行一个函数或方法时,都会创建一个新的栈帧,用于存储该函数或方法的局部变量、参数、返回地址以及其他执行相关的信息。这些栈帧会按照调用顺序被组织成一个栈,称为调用栈。(跟c/c++中的栈类似,懂点逆向知识应该很好理解)

栈帧包含了以下几个重要的属性:

f_locals: 一个字典,包含了函数或方法的局部变量。键是变量名。

f_globals: 一个字典,包含了函数或方法所在模块的全局变量。

f_code: 一个代码对象(code object),包含了函数或方法的字节码指令、常量、变量名等信息。

f_lasti: 整数,表示最后执行的字节码指令的索引。

f_back: 指向上一级调用栈帧的引用,用于构建调用栈。

利用栈帧(frame)逃逸沙箱

原理就是通过生成器的栈帧对象通过f_back(返回前一帧)从而逃逸出去获取globals符号表,例如:

key = "this is flag"
codes='''
def waff():
def f():
yield g.gi_frame.f_back
g = f() #生成器
frame = next(g) #获取到生成器的栈帧对象
b = frame.f_back.f_back.f_globals['key'] #返回并获取前一级栈帧的globals
return b
b=waff()
'''
locals={}
code = compile(codes, "", "exec")
exec(code, locals, None)
print(locals["b"]) # this is flag

逃逸出来我们就可以调用沙箱外的方法来执行恶意命令了

globals中的__builtins__字段

__builtins__ 模块是 Python 解释器启动时自动加载的,其中包含了一系列内置函数、异常和其他内置对象。

当代码这么设计时:

key = "this is flag"
codes='''
def waff():
def f():
yield g.gi_frame.f_back
g = f() #生成器
frame = next(g) #获取到生成器的栈帧对象
b = frame.f_back.f_back.f_globals['key'] #返回并获取前一级栈帧的globals
return b
b=waff()
'''
locals={"__builtins__": None}
code = compile(codes, "", "exec")
exec(code, locals, None)
print(locals["b"])

这里将沙箱中的__builtins__置为空,也就是说沙箱中不能调用内置方法了,那我们这段代码运行就会报错了(next方法不能使用),那么该如何代替next方法来拿到生成器的值,还记得上面说可以遍历的形式来获取生成器的值:

key = "this is flag"
codes='''
def waff():
def f():
yield g.gi_frame.f_back
g = f() #生成器
frame = [i for i in g][0] #获取到生成器的栈帧对象
b = frame.f_back.f_back.f_back.f_globals['key'] #返回并获取前一级栈帧的globals
return b
b=waff()
'''
locals={"__builtins__": None}
code = compile(codes, "", "exec")
exec(code, locals, None)
print(locals["b"])

这样可以成功拿到key的值,不过这里需要注意的是在给b赋值时,多加了一个f_back,因为我们用这种列表推导式拿到生成器的值,它的code对象是不同的:

frame = next(g)
<frame at 0x00000235C9718440, file '', line 7, code waff> frame = [i for i in g][0]
<frame at 0x000002708F2ED8C0, file '', line 9, code <listcomp>>

列表推到式拿到的生成器的code对象是listcomp,所以我们还得拿上一个栈帧,所以需要再f_back一下

一些简便写法

例如:

q = (q.gi_frame.f_back.f_back.f_globals for _ in [1])
g = [*q][0]
  • 第一行生成器创建时,并不会直接执行,只是存储在内存中。
  • 第二行对生成器解包,解包的同时会触发生成器调用,此时才开始执行q.gi_frame.f_back.f_back.f_globals。
  • exec调用时,创建一个新栈帧;生成器q被执行时,又创建一个新栈帧。所以当我们拿到q.gi_frame时,需要回溯两次才到达exec之外。
  • 再拿一个f_globals,就得到了沙箱外的的globals

初探python栈帧逃逸的更多相关文章

  1. 深入理解python虚拟机:程序执行的载体——栈帧

    深入理解python虚拟机:程序执行的载体--栈帧 栈帧(Stack Frame)是 Python 虚拟机中程序执行的载体之一,也是 Python 中的一种执行上下文.每当 Python 执行一个函数 ...

  2. CTF必备技能丨Linux Pwn入门教程——调整栈帧的技巧

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  3. Java虚拟机栈--栈帧

    栈帧的内部结构 每个栈帧中存储着 1.局部变量表(Local Variables) 2.操作数栈(Operand Stack)(或表达式栈) 3.动态链接(Dynamic Linking)(或执行&q ...

  4. 栈帧的内部结构--动态链接 (Dynamic Linking)

    每个栈帧中包含: 局部变量表(Local Variables) 操作数栈(Opreand Stack) 或表达式栈 动态链接 (Dynamic Linking) (或指向运行时常量的方法引用) 动态返 ...

  5. 图解JVM字节码执行引擎之栈帧结构

    一.执行引擎      “虚拟机”的概念是相对于“物理机”而言的,这两种“机器”都有执行代码的能力.物理机的执行引擎是直接建立在硬件处理器.物理寄存器.指令集和操作系统层面的:而“虚拟机”的执行引擎是 ...

  6. 栈帧%ebp,%esp详解

    首先应该明白,栈是从高地址向低地址延伸的.每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息.寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部( ...

  7. c函数调用过程原理及函数栈帧分析

    转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...

  8. Linux下追踪函数调用,打印栈帧

    事情的起因是这样的,之前同事的代码有一个内存池出现了没有回收的情况.也就是是Pop出来的对象没有Push回去,情况很难复现,所以在Pop里的打印日志,跟踪是谁调用了它,我想在GDB调试里可以追踪调用的 ...

  9. Linux - 函数的栈帧

    栈帧(stack frame),机器用栈来传递过程参数,存储返回信息,保存寄存器用于以后恢复,以及本地存储.为单个过程(函数调用)分配的那部分栈称为栈帧.栈帧其实是两个指针寄存器, 寄存器%ebp为帧 ...

  10. JAVA栈帧

    简介 Java栈是一块线程私有的内存空间.java堆和程序数据相关,java栈就是和线程执行密切相关的,线程的执行的基本行为是函数调用,每次函数调用的数据都是通过java栈来传递的. Java栈与数据 ...

随机推荐

  1. CSP_J2023总结

    维护中 T1 [CSP-J 2023] 小苹果 题目描述 小 Y 的桌子上放着 $n$ 个苹果从左到右排成一列,编号为从 $1$ 到 $n$. 小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果. 每 ...

  2. union在重构代码中的使用技巧

    http://blog.chinaunix.net/uid-23629988-id-158156.html 在产品的开发过程中,无论是代码的重构,还是添加新的功能时,都不可避免的有对现有结构体的修改, ...

  3. DPDK简介

    DPDK简介 DPDK(Data Plane Development Kit)数据平面开发工具包,是一个开源软件项目.DPDK通过维护一系列能够加速多核CPU数据包处理的库,提供数据处理框架.DPDK ...

  4. KNN算法 0基础小白也能懂(附代码)

    KNN算法 0基础小白也能懂(附代码) 原文链接 1.K近邻是啥 1968年,Cover 和 Hart 提出了最初的近邻法,思路是--未知的豆离哪种豆最近,就认为未知豆和该豆是同一种类. 近邻算法的定 ...

  5. 操作 JAR 文件

    列出 JAR 文件内容 使用 jar 命令来列出 JAR 文件的内容: jar tf myapp.jar -t 选项表示列出文件,-f 表示指定 JAR 文件. 解压 JAR 文件 使用 jar 命令 ...

  6. 前端使用 Konva 实现可视化设计器(22)- 绘制图形(矩形、直线、折线)

    本章分享一下如何使用 Konva 绘制基础图形:矩形.直线.折线,希望大家继续关注和支持哈! 请大家动动小手,给我一个免费的 Star 吧~ 大家如果发现了 Bug,欢迎来提 Issue 哟~ git ...

  7. SNERT内部集训-WEB

    SNERT内部集训-WEB Day1-2 文件上传 靶场搭建 docker安装,docker pull c0ny1/upload-labs,docker run -it -d -p 8080:80 a ...

  8. 合合信息扫描全能王亮相静安区3·15活动,AI扫描带来绿色消费新体验

    保护消费者的合法权益,是全社会的共同责任.为优化消费环境.促进品质消费高地建设,打造安全优质和谐的消费环境,上海静安区消保委于3月15日举办静安区2024年"3·15"国际消费者权 ...

  9. Spring —— AOP(面向切面编程)

    AOP(Aspect Oriented Programming)简介 面向切面编程,一种编程范式,指导开发者如何组织程序结构 作用:在不惊动原始设计的基础上为其进行功能增强 Spring理念:无入侵式 ...

  10. [namespace hdk] Balanced_tree 整合

    代码 #include<bits/stdc++.h> using namespace std; namespace hdk{ namespace balanced_tree{ const ...