逆向分析Lua语言特性的设计与实现(一):闭包
来源:互联网 发布:toast js插件 编辑:程序博客网 时间:2024/05/16 07:07
一、Lua闭包
若将一个函数写在另外一个函数内部,那么这个位于内部的函数便可以访问外部函数中的局部变量,这个特性称之为closure,中文翻译为“闭包”。
二、实践分析闭包的实现
(一)、实例1:Lua指令逆向分析
function newCounter()local i = 0 return function() i = i+1 return i endendc1= newCounter()print(c1())print(c1())
ChunkSpry交互模式反编译:
>function newCounter() local i = 0 return function() i = i+1; return i end end; source chunk: (interactive mode); x86 standard (32-bit, little endian, doubles); function [0] definition (level 1); 0 upvalues, 0 params, 2 stacks.function 0 0 2 2.const "newCounter" ; 0; function [0] definition (level 2); 0 upvalues, 0 params, 2 stacks.function 0 0 0 2.local "i" ; 0.const 0 ; 0; function [0] definition (level 3) //匿名函数:closure; 1 upvalues, 0 params, 2 stacks.function 1 0 0 2.upvalue "i" ; 0.const 1 ; 0[1] getupval 0 0 ; i //R(0):=UpValue(0)[2] add 0 0 256 ; 1 //i=i+1[3] setupval 0 0 ; i //UpValue(0):=R(0),将寄存器0中的值写回UpValue(0)[4] getupval 0 0 ; i //再次取出UpValue(0),为解下来的返回i做准备[5] return 0 2[6] return 0 1; end of function[1] loadk 0 0 ; 0[2] closure 1 0 ; 1 upvalues[3] move 0 0[4] return 1 2[5] return 0 1; end of function[1] closure 0 0 ; 0 upvalues[2] setglobal 0 0 ; newCounter[3] return 0 1; end of function>
从上述Lua字节码可以看出,局部变量在闭包函数中是通过upvalue传递的。
在匿名函数中,第一条指令[1]操作是从该函数的upvalue列表中,0号索引位置上得到局部变量i,并拷贝在第0号寄存器中。
在执行期间,upvalue是由CLOSURE设置并由虚拟机维护的。
在pascal中,外层作用域中的变量是通过遍历栈帧找到的。然而,Lua函数是第一类值,可以被赋值给变量在别处引用。Lua虚拟机的解决办法是通过
getupval 和setupval 提供访问upvalue的干净接口,不过upvalue的管理是虚拟机自己处理的。
事实上,Lua编译一个函数时,会为他生成一个原型(prototype),其中包含了函数体对应的虚拟机指令、函数用到的常量值(数,文本字符串等等)和一些调试信息。
在运行时,每当Lua执行一个形如function...end 这样的表达式时,他就会创建一个新的数据对象,其中包含了相应函数原型的引用、环境(environment,用来查找全局变量的表)的引用及一个由所有upvalue引用组成的数组,而这个数据对象就称为闭包。由此可见,函数是编译期概念,是静态的,而闭包是运行期概念,是动态的。
upvalue实际是局部变量,而局部变量是保存在函数堆栈框架上(stack frame)的,所以只要upvalue还没有离开自己的作用域,他就一直生存在函数堆栈上。这种情况下,闭包将通过指向堆栈上的upvalue的引用来访问他们,一旦upvalue即将离开自己的作用域(这也意味着他马上要从堆栈中消失),闭包就会为他分配空间并保存当前的值,以后便可通过指向新分配空间的引用来访问该upvalue。
(二)、实例2:
function func1(n)
local function func2()
print(n)
end
n = n + 1000
return func2
end
g1=func1(1)
g1() //打印出1还是1001?
local function func2()
print(n)
end
n = n + 1000
return func2
end
g1=func1(1)
g1() //打印出1还是1001?
func1将返回内部定义的一个函数
g1=func1(1),使g1得到了第一类值的函数
g1()这条语句实质上执行了一次这个函数,那么print(n)这条语句打印的英国是1呢还是1001呢?为什么呢?
通过运行程序,我们发现奇怪的问题:内嵌函数定义在n = n + 1000这条语句之前,可为什么g1()打印出的却是10001?
upvalue实际是局部变量,而局部变量是保存在函数堆栈框架上(stack frame)的,所以只要upvalue还没有离开自己的作用域,他就一直生存在函数堆栈上。这种情况下,闭包将通过指向堆栈上的upvalue的引用来访问他们,一旦upvalue即将离开自己的作用域(这也意味着他马上要从堆栈中消失),闭包就会为他分配空间并保存当前的值,以后便可通过指向新分配空间的引用来访问该upvalue。当执行到g1(),既func1(1)的n = n + 1000时,闭包已创建了,不过n并没有离开作用域,所以闭包仍然引用堆栈上的n,当return func2完成时,n即将结束生命,此时闭包便将n(已是1001了)复制到自己管理的空间中以便将来访问。弄清晰了内部的秘密后,运行结果就不难解释了。
三、闭包的设计
lua源代码闭包部分欣赏
未完待续...
- 逆向分析Lua语言特性的设计与实现(一):闭包
- Lua语言学习(一)------Lua语言闭包代替范性for语义的迭代器的原理
- Lua实现类与继承的两种方式(复制)and (闭包)
- 语言特性-闭包
- 深入浅出多线程(6)分析并行包线程池的设计与实现
- 深入浅出多线程(6)分析并行包线程池的设计与实现
- 用lua写一个闭包的例子(lua语言)
- lua语法特性(一)
- Android软件安全与逆向分析(一)
- Lua学习之--函数,变长参数,closure(闭包)及select的一些特性
- Lua学习之--函数,变长参数,closure(闭包)及select的一些特性
- 转lua 闭包的实现 思路
- 安卓逆向分析实战(一)--逆向工具介绍与安装
- EventBus设计与实现分析——特性介绍
- lua语言的学习(一)
- lua语言的学习(一.1)
- lua语言的学习(一.2)
- Spring源码分析----IoC容器其他特性的设计与实现
- Thunderbird技巧-回复邮件时内容在最前面
- c语言练习题Training1整数算法训练
- linux 从视频中提取音频
- TCP报文首部各参数简介
- HDU-1232(并查集)
- 逆向分析Lua语言特性的设计与实现(一):闭包
- 跨域的理解与实现
- linux笔记(1)
- IE无法解析XML文件的解决办法
- 左脑原创geoserver学习系列文章 (第一部分代码的检出)。
- Quercus-基于Java的PHP框架
- MFC控件使用总结——CListCtrl
- 我当技术作家的那些日子(2012-11-05结束)[正式发布]
- 警惕:24种榨干时间和精力的生命水蛭