PyYaml反序列化源码浅探

0x00

  今天看到关于PyYaml反序列化的一篇文章:https://xz.aliyun.com/t/7923,自己复现时候debug,发现一些和文章不一样的地方,记录一下。

  预备知识先了解一下__init__、__new__和__import__函数。

0x01

__init__和__new__函数

  参考:

  https://www.cnblogs.com/wuxianfeng023/p/17188362.html 

  https://blog.csdn.net/sj2050/article/details/81172022

 

  要点:

  __new__是个静态方法,__init__是实例方法

  __new__需要返回一个实例,并自动执行该实例的__init__。若不返回,则不执行。

 

  关于这两个函数的执行条件,自己做了测试。

  python版本:

  

 

 

  测试1:

class poc:
    def __new__(cls):
    	print('__new__')
        self_in_init = super().__new__(cls)
        return self_in_init
    def __init__(self):
        print('__init__')
        
poc()#先执行__new__,再执行__init__

  

  测试2:

class poc:
    def __new__(cls):
    	print('__new__')
        self_in_init = super().__new__(cls)
    def __init__(self):
        print('__init__')
        
poc() #只执行__new__

 

  测试3:

class poc:
    def __init__(self):
        print('__init__')
        
poc() #只执行__init__

 

  测试4:

class poc:
    def __new__(cls):
        print('__new__')
        self_in_init = super().__new__(cls)
        return self_in_init
    def __init__(self):
        print('__init__')
        
poc.__new__(poc)#只执行__new__

  测试5:

class c(str):
    def __new__(cls, string):

        aa = a()
        return aa

    def __init__(self, string):
        print(id(self))

class a:
    def __init__(self):
        print('a: __init___ ')

c('1') #a: __init___ 

  

 

  总结:

  以括号方式创建实例,单有__new__或__init__都会执行,若两个都有,先执行__new__,然后执行__new__返回的实例的__init__。

  手动用__new__创建实例,只执行他,不执行__init__。

__import__

  import 和 __import__作用差不多,都是用来导包,遇到py代码则执行。

  

  a.py文件

print('aaaaaaa')

  b.py文件:

#import a 这样也可以
__import__('a')

  两个文件要在一个目录下。执行b.py,会输出aaaaaaa。

 

0x02

  复现原文章,自己的PyYaml版本和原文章一致。

  tmp.py:

  

  tmp2.py:

  

 

  先执行tmp,再执行tmp2,两次执行都会弹计算器。

 

  第二次弹计算器,会想到是创建了poc实例,执行了__init__。我们再测试一遍:

  tmp.py:

  

  tmp2.py不修改。

  tmp3.py:

  

  先执行tmp.py,再执行tmp2.py,发现第一次弹计算机,而第二次没有。

 

  接着我自己debug看源码。先说结果:创建对象调的是__new__,而不是__init__。第一次测试的第二次可以弹计算器,不是执行的poc实例的__init__,而是因为__import__('tmp'),把生成yml文件用的代码也导入进去了,所以才弹计算器。

 

0x03

  开始debug。

  

  

  关键部分:

  

 

  先跟进find_python_name看看:

      

  

  可以看到,tmp3.poc根据 '.' 被分割成module_name 和object_name,并执行__import__(module_name)。

  跳出find_python_name函数,继续往下,可以看到执行的是tmp3.poc的__new__函数。

  

 

0x04

  当时debug纯粹是想练习调代码的能力,因为自己平时不怎么调。调了挺多遍,看得稍微有点明白了才罢休。能发现和网上师傅没注意到的点,心里还是有点激动的,嘻嘻嘻。东西不多,纯记录一下,师傅们看个乐就好。

 

posted @ 2023-03-08 21:14  un1n0wn  阅读(68)  评论(0)    收藏  举报