Skip to content

Commit 751a094

Browse files
committed
Updated 'src/reflection.md'.
1 parent 8df60ef commit 751a094

File tree

6 files changed

+34
-19
lines changed

6 files changed

+34
-19
lines changed

scripts/reflection/demo_call_hook.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ local count = 0 -- 步骤计数器
77
local validfunc = {
88
[string.upper] = true,
99
[string.lower] = true,
10-
-- ... -- 其他已授权函数
10+
... -- 其他已授权函数
1111
}
1212

1313
local function hook (ev)
14+
print("Enter hook")
1415
if ev == "call" then
1516
local info = debug.getinfo(2, "fn")
17+
local fn = validfunc[info.name]
18+
print(fn)
19+
1620
if not validfunc[info.func] then
1721
error("正调用不良函数:" .. (info.name or "?"))
1822
end
@@ -26,5 +30,6 @@ end
2630

2731
-- 加载代码块
2832
local f = assert(loadfile(arg[1], "t", {}))
29-
debug.sethook(hook, "", 100) -- 设置钩子
33+
debug.sethook(hook, "call", 100) -- 设置钩子
3034
f() -- 运行代码块
35+
debug.sethook()

scripts/reflection/improved_sandbox.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ end
2525
local f = assert(loadfile(arg[1], "t", {}))
2626

2727
debug.sethook(step, "", 100) -- 设置钩子
28-
2928
f() -- 运行文件
29+
debug.sethook() -- 关闭钩子

scripts/reflection/sandbox_feed.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
local s = "123456789012345"
2+
for i = 1, 36 do s = s .. s end

scripts/reflection/str_man.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
a = "x"
2+
a:rep(2^30)

src/env.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ stack traceback:
421421

422422
```lua
423423
a = 13 -- 全局的
424-
local a = 12
424+
local a = 12
425425
print(a) --> 12 (局部的)
426426
print(_ENV.a) --> 13 (全局的)
427427
```
@@ -432,7 +432,7 @@ print(_ENV.a) --> 13 (全局的)
432432

433433
```lua
434434
a = 13 -- 全局的
435-
local a = 12
435+
local a = 12
436436
print(a) --> 12 (局部的)
437437
print(_G.a) --> 13 (全局的)
438438
```
@@ -702,6 +702,14 @@ env = {}
702702
loadfile("config.lua", "t", env) ()
703703
```
704704

705+
> **译注**
706+
>
707+
> - 这里 `loadfile` 的第二个参数 `"t"`,指的是所加载的字符串模式,可以是文本或二进制模式(即某个预编译块)。字符串模式可以为 `"b"`(仅二进制块)、`"t"`(仅文本块),或 `"bt"`(二进制与文本均可),默认为 `"bt"`
708+
>
709+
> - 第三个参数是要传入的环境。
710+
>
711+
> 参考:[`loadfile ([filename [, mode [, env]]])`](https://www.lua.org/manual/5.4/manual.html#pdf-load)
712+
705713
该配置文件中的所有代码,都将在充当了某种沙盒的空环境 `env` 中运行。特别是,全部的定义都将进入该环境。该配置文件无法影响其他任何东西,即便在失误下。即使是恶意代码,也无法造成太大破坏。他可以进行拒绝服务(DoS)攻击,浪费掉 CPU 时间和内存,但不会造成其他影响。
706714

707715

src/reflection.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -338,21 +338,21 @@ debug.sethook(print, "l")
338338
在这种方案下,分析器可以获取 `arg[1]` 中的文件名,打开钩子并运行该文件:
339339

340340
```lua
341-
{{#include ../scripts/profiler.lua:15:18}}
341+
{{#include ../scripts/reflection/profiler.lua:15:18}}
342342
```
343343

344344
最后一步是显示结果。图 25.3 “获取函数名称” 中的函数 `getname`,会产生某个函数的名字。
345345

346346
<a name="f-25.3"></a> **图 25.3,获取函数名字**
347347

348348
```lua
349-
{{#include ../scripts/profiler.lua:20:32}}
349+
{{#include ../scripts/reflection/profiler.lua:20:32}}
350350
```
351351

352352
由于 Lua 中函数的名字非常不确定,因此我们给各个函数添加了位置,以 `file:line` 对的形式给出。若某个函数没有名字,我们就只使用其位置。对于 C 函数,我们只使用其名字(因为他没有位置)。该定义之后,我们就要打印各个函数及其计数器:
353353

354354
```lua
355-
{{#include ../scripts/profiler.lua:34:}}
355+
{{#include ../scripts/reflection/profiler.lua:34:}}
356356
```
357357

358358
若咱们将咱们的分析器,应用到 [第 19 章 “插曲:马可夫链算法“](markov_chain_algorithm.md) 中开发的 Markov 示例,咱们会得到如下结果:
@@ -396,42 +396,40 @@ nil 1
396396
**Sandboxing**
397397

398398

399-
[`_ENV``load`](env.md#_env-与-load) 小节中,我们曾看到使用 `load` 特性,在受限环境中运行某个 Lua 片段是多么容易。由于 Lua 与外部世界的所有通信,都是通过库函数完成的,因此一旦我们移除这些函数,也就消除了脚本对外部世界产生任何影响的可能性。不过,在脚本浪费大量 CPU 时间或内存下,我们仍然容易受到拒绝服务,denial of service,DoS 的攻击。调试钩子形式下的反射,为遏制此类攻击提供了一种有趣方法。
399+
在称为 [`_ENV``load`](env.md#_env-与-load) 的小节,我们曾看到使用 `load` 功能在受限环境中,运行某个 Lua 片段是多么容易。由于 Lua 与外部世界的所有通信,都是通过库函数完成,因此一旦我们移除这些函数,也就消除了脚本对外部世界产生任何影响的可能性。不过,在脚本浪费大量 CPU 时间或内存下,我们仍然容易受到拒绝服务,denial of service,DoS 攻击。调试钩子形式下的反射,为遏制此类攻击提供了一种有趣方法。
400400

401401

402-
第一步是使用计数钩子,限制某个代码块可以执行的指令数量。图 25.4 “使用钩子的简单沙箱”,展示了一个在这种沙箱中运行给定文件的程序
402+
第一步是使用计数钩子限制某个代码块可执行的指令数量。图 25.4 “使用钩子的简单沙箱”,展示了在这种沙箱中运行给定文件的一个程序
403403

404404
<a name="f-25.4"></a> **图 25.4,使用钩子的简单沙箱**
405405

406406
```lua
407407
{{#include ../scripts/reflection/naive_sandbox.lua}}
408408
```
409409

410-
程序加载给定文件,设置钩子,然后运行该文件。程序将钩子设置为计数钩子,这样 Lua 就会每 100 个指令调用一次钩子。钩子(函数`step`只是递增一个计数器,并将其与一个固定限制进行比较。可能会出什么问题呢
410+
程序加载给定文件,设置钩子,然后运行该文件。程序将其中的钩子设置为了计数钩子,这样 Lua 就会每 100 个指令调用一次钩子。这个钩子(函数`step`只会递增一个计数器,并将其与一个固定限制做检查。那么可能会出什么问题呢
411411

412-
当然,我们必须限制所加载代码块大小:只要加载一个巨大块,就会在加载时耗尽内存。另一个问题是,正如下面这个片段所示,程序可以用少得惊人的指令,消耗大量内存:
412+
当然,我们必须限制加载代码块大小:只要加载某个巨大代码块,就会在加载时耗尽内存。另一个问题是,正如下面这个片段所示,程序可以用少得惊人的指令,消耗大量内存:
413413

414414
```lua
415415
local s = "123456789012345"
416416
for i = 1, 36 do s = s .. s end
417417
```
418418

419-
在不到 150 条指令下,这个小片段就将尝试创建一个 1 TB 的字符串。显然,仅限制步骤和程序大小是不够的
419+
只需不到 150 条指令,这个小片段就将尝试创建出一个 1 TB 的字符串。显然,仅限制步骤数和程序大小是不够的
420420

421-
如图 25.5 “控制内存使用” 所示,一种改进方法是,在 `step` 函数中,检查并限制内存使用。
421+
如图 25.5 “控制内存使用” 所示,一种改进是在 `step` 函数中,检查并限制内存使用。
422422

423423

424424
<a name="f-25.5"></a> **控制内存使用**
425425
```lua
426426
{{#include ../scripts/reflection/improved_sandbox.lua:3:22}}
427-
428-
-- 如前
429427
```
430428

431429

432-
由于在如此少的指令下,内存就能快速增长,我们应该设置一个非常低的限制,或者以小的步骤调用钩子。更具体地说,某个程序可以在 40 条指令内,将某个字符串的大小增加一千倍。因此,我们要么以比每 40 步更高的频率调用钩子,要么将内存限制设为我们真正能承受的千分之一。我(作者)可能会两者兼顾。
430+
由于在如此少的指令下,内存就能快速增长,我们应该设置一个非常低的限制,或者以小的步骤调用钩子。更具体地说,某个程序可以在 40 条指令内,将某个字符串的大小增加一千倍。因此,要么我们以比每 40 步更高的频率调用钩子,要么将内存限制设为我们真正能承受的千分之一。我(作者)可能会两者兼顾。
433431

434-
更微妙的问题便是 Lua 的字符串库。我们可在某个字符串上,以方法方式调用这个库中的任何函数。因此,即使这些函数不在环境中,我们也可以调用他们;字面的字符串会将他们,偷偷地带入我们的沙箱。字符串库中的任何函数,都不会影响外部世界,但他们会绕过我们的步骤计数器。( C 函数的一次调用,会算作 Lua 中的一条指令。)字符串库中的某些函数,可能是非常危险的 DoS 攻击。例如,在一个步骤中调用 `(“x”):rep(2^30)` 一次,就会吞噬 1 GB 的内存。再举个例子,在我(作者)的新机器上,运行下面的调用 Lua 5.2 需要 13 分钟:
432+
更微妙的问题是 Lua 的字符串库。在某个字符串上,我们可以方法的形式调用这个库中的任何函数。因此,即使这些函数不在环境中,我们也可以调用他们;字面的字符串会将他们偷偷地带入我们的沙箱。字符串库中的任何函数都不会影响外部世界,但他们会绕过我们的步骤计数器。(对某个 C 函数的一次调用,算作 Lua 中的一条指令。)字符串库中的某些函数,可能是非常危险的 DoS 攻击。例如,在某个步骤中调用 `(“x”):rep(2^30)` 一次,就会吞噬 1 GB 的内存。再举个例子,在我(作者)的新机器上运行下面的调用,Lua 5.2 需要 13 分钟:
435433

436434

437435
```lua
@@ -440,7 +438,7 @@ s:find(".*.*.*.*.*.*.*.*.*x")
440438
```
441439

442440

443-
限制对字符串库访问的一种有趣方法,是使用调用钩子。每次调用某个函数时,我们都会检查该函数是否经过授权。下图 25.6 “使用钩子禁止对未授权函数的调用”,实现了这一想法。
441+
限制对字符串库访问的一种有趣方法,是使用 `call` 钩子。每次调用某个函数时,我们都要检查该函数是否经过授权。下图 25.6 “使用钩子禁止对未授权函数的调用”,实现了这一想法。
444442

445443

446444
<a name="f-25.6"></a> **使用钩子禁止对未授权函数的调用**

0 commit comments

Comments
 (0)