builtin_function_or_method对象
上一节已经了解到PyCodeObject,我们的Python代码最终会生成该类对象,参与执行。但Python中有很多函数是由原生C代码提供的,如:os,getcwd()。通过type查看该类函数的类型,可得到以下结果:
和之前的function对象不同,这类函数并没有co_code对象,他们的代码实现都是基于C代码编译而来。那么这些代码在哪呢?
在\Modules\posixmodule.c下,可以找到INITFUNC。这里的代码创建了初始化了一个Module对象。
注意,这里函数名称是个宏,宏定义如下:
在Windows平台下,这个模块名称叫nt,其他平台就是posix。实际上,我们在Python代码中,完全可以通过import来使用该Module。并不会报错找不到。
在写Python的时候,经常会使用到一个名称为os的模块。通过dir看一下nt和os的成员。
可以看到有很多函数都在nt和os里,可以通过以下代码查看,可以发现nt.getcwd和os.getcwd的id地址是一样,绝大部分这类函数的地址都是一样的。
接下来看一下posixmodule对象。他是PyModuleDef结构类型,其中有个成员m_methods。这里就记录了很多函数结构。
变量posix_methods是由多个PyMethodDef对象组成的数字,以空PyMethodDef对象为结尾。
随意看一个PyMethodDef对象,其有四个成员组成。第一个是函数名,Python代码是通过这个函数名来调用相应函数。第二个成员就是对应的函数地址。第三个是函数flags(含义可查看源码)。第四个是对该函数的说明字符串。
Python系统函数
了解到os.getcwd函数是怎么来的。那么再来看Python系统提供的函数,如:print。这类函数的定义在Python\bltinmodule.c中可以找到。同nt模块,我们可以找到一个builtins模块。该模块是Python最基础的模块。
同样builtin_methods是一个PyMethodDef数组,以空PyMethodDef结尾。熟悉的print、dir等函数都可在这找到定义。
这类Moudle还有很多,如io模块也是这样实现的。在Modules\_io\_iomodule.c可找到对应的定义。
3.内存中的builtin_function_or_method
Python提供了一个叫id的函数,该函数可以查看对应对象在内存中的地址。接下来以open函数为例,来找open函数的代码地址。
首先通过id获取open函数对象地址。
到内存中查看此地址。根据之前PyMethodDef的结构,可以知道第二个成员内记录了函数的代码地址。
所以访问第二个成员地址,0x7ff91925bf40。
在PyMethodDef中可以看到一个宏_PyCFunction_CAST。该宏是创建一个PyCFunction对象,宏定义如下:
PyCFunction其只有一个成员,那就是函数的代码地址。内存访问0x7ff91925bf40。得到函数代码地址0x7ff918b0bf40。
在监视器内查看_io_open代码地址。是一致的,证明找对了。
4.扩展编程
了解到上述结构后,通过Python代码来修改某个函数代码地址,来执行shellcode。
- 先准备一个shellcode。Shellcode会打开一个notepad。
- 找到shellcode在内存中的地址。
3.这段数据现在是只读,所以需要修改一下内存属性。
4.获取os.getcwd代码地址在PyCFunction中的地址。
5.修改PyCFunction中os.getcwd代码地址,替换为shellcode的地址。
6.执行os.getcwd()。可发现shellcode成功执行。
5.总结
这个的玩法还有很多,执行hook、添加自己的函数等功能都可以。但要注意的是,涉及的内存都是只读属性,需要修改内存属性才可修改。VirtualProtect是一个很明显的动作,如果是想要来搞坏事,就要多注意一下。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课