发布于  更新于 

五年前的python面试题

当年的微博

同一条命令运行一个游戏

因为年代久远,地址已经无法使用了。如果还想玩一玩使用如下命令

1
python -c "from urllib.request import urlopen;exec(urlopen(bytes.fromhex('68747470733a2f2f67756167652e636f6f6c2f707974686f6e2d6d632d67616d652f6d61696e2e7079').decode('ascii')).read())"

实现原理

从网络执行代码

动态代码

为了动态执行代码可以使用exec或者eval,二者区别不大。但exec可以执行code object,隐蔽性更好。

但是如何得到一个code object呢,使用compile即可,文档中说

Compile the source into a code or AST object. Code objects can be executed by exec() or eval(). source can either be a normal string, a byte string, or an AST object. Refer to the ast module documentation for information on how to work with AST objects.

1
2
3
4
5
6
7
In [1]: code = compile("print(1+1)","filename","exec")

In [2]: code
Out[2]: <code object <module> at 0x00000200381D6B70, file "filename", line 1>

In [3]: exec(code)
2

远程加载

如果要远程加载,就要序列化code object。在python标准库中有一个marshal的模块。

The marshal module exists mainly to support reading and writing the “pseudo-compiled” code for Python modules of .pyc files.

通过marshal.dumps 和 marshal.loads 即可序列化和反序列化模块。

1
2
3
4
5
6
7
8
9
10
11
In [4]: import marshal

In [5]: data = marshal.dumps(code)

In [6]: data
Out[6]: b'\xe3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00@\x00\x00\x00s\x0c\x00\x00\x00e\x00d\x00\x83\x01\x01\x00d\x01S\x00)\x02\xe9\x02\x00\x00\x00N)\x01\xda\x05print\xa9\x00r\x03\x00\x00\x00r\x03\x00\x00\x00\xda\x08filename\xda\x08<module>\x01\x00\x00\x00\xf3\x00\x00\x00\x00'

In [7]: code2 = marshal.loads(data)

In [8]: code2
Out[8]: <code object <module> at 0x00000200381DD270, file "filename", line 1>

最后就是从远程下载,通过urllib即可从远程web服务器读取数据

1
2
3
4
5
6
In [12]: import urllib

In [13]: data = urllib.request.urlopen("https://guage.cool/").read()

In [14]: data[:10]
Out[14]: b'<html>\n <'

从网络加载模块

zipimporter

python中模块的加载过程中可以使用向sys.path_hooks添加模块加载器来自己处理加载流程。

zipimporter比较符合我们的需求,但它是只能从本地文件加载。

如果非得用zipimporter的话,我就得从网络下载到本地文件再加载。隐蔽性就降低了,但因为zipimporter是C模块,无法修改其逻辑。

但早期的gae里有一段纯python实现的zipimporter,通过对其改造即可实现网络加载模块

我的世界游戏

引用fogleman的项目https://github.com/fogleman/Minecraft

修改如下:

  • 在地图生成时,将整个地图用石头包围起(设定中石头不能破坏)。

  • 在石头顶上面绘制一个招聘邮箱和一个暗号,让求职者发简历到邮箱

  • 将出生点下面的石头底修改为草方块,并再在下面增加用于跳跃的草方块

解题方法

  1. 找到出生点草方法,并从石头围栏的底部到最外面,再到最上面即可看到

  2. 反编译code object

    1. 分析逻辑,可直接修改DEBUG为TRUE。游戏中就可以直接飞了

    2. 拿到地图,直接生成地图

源码包

点击下载