[size=x-small][color=gray](以下内容不适合小盆友,请在老大哥指引下观看。实验危险,请不要在家尝试。)[/color][/size]
hello.rb:
执行结果:看到beef
(得NS老兄的指示,可以宣传Bk201了:
上面用到的Bk201由night_stalker编写,可由github获取:[url]http://github.com/LuiKore/bk201/tree/master[/url]
我用的是刚能运行的原型,现在在github上的是啥状况我还没看,所以具体用法可能跟我这帖里的有出入
Bk201需要配合Ruby 1.9使用。暂时不支持CRuby以外的实现。)
把第10行开头的注释去掉,插入一个断点。用OllyDbg打开ruby.exe hello.rb,运行,来到断点处。
(注意在OllyDbg主菜单的 选项->调试设置->异常->INT3中断,前面的勾要去掉不选,不然INT3中断会把程序带到Windows默认的中断处理函数里)
看到栈上内容是:
谁调用了生成的代码?
继续观察前面例子中停在断点上的程序的函数调用栈,可以看到直接调用生成的代码的是call_cfunc(),它又是被vm_call_cfunc()所调用的。
vm_insnhelper.c:
上面两个函数都是Ruby 1.9虚拟机里关于方法调用的指令的实现。哪些指令会调用它们呢?
在vm.inc里匹配INSN_ENTRY(...),可以得知Ruby 1.9的虚拟机指令集有:
其中send与invokesuper指令的实现中使用了CALL_METHOD宏。该宏实现如下:
然后里面的vm_call_method()里调用了前面的vm_call_cfunc():
Well, okay. 看过实际运行中的栈的状况后,我比较有信心能写机器码里受栈影响的指令了……
hello.rb:
require File.expand_path(File.dirname __FILE__) + '/../bk201.rb'
def int2fix(i)
i << 1 | 1
end
class A
extend Bk201::Asm
asm_method :int, 'foo', 2 do |a|
#a.code << "\xCC" # int3
a.code << "\xB8" << [ int2fix(0xBEEF) ].pack('I') # mov eax, 00017DDF
end
end
puts '%x' % A.new.foo(3, 4)
执行结果:看到beef
(得NS老兄的指示,可以宣传Bk201了:
上面用到的Bk201由night_stalker编写,可由github获取:[url]http://github.com/LuiKore/bk201/tree/master[/url]
我用的是刚能运行的原型,现在在github上的是啥状况我还没看,所以具体用法可能跟我这帖里的有出入
Bk201需要配合Ruby 1.9使用。暂时不支持CRuby以外的实现。)
把第10行开头的注释去掉,插入一个断点。用OllyDbg打开ruby.exe hello.rb,运行,来到断点处。
(注意在OllyDbg主菜单的 选项->调试设置->异常->INT3中断,前面的勾要去掉不选,不然INT3中断会把程序带到Windows默认的中断处理函数里)
看到栈上内容是:
----------------------------------------------
0022FD58 /0022FD74 ; 老的栈帧指针
0022FD5C |100208B4 ; 返回到 msvcr90-.100208B4
0022FD60 |00AB237C ; self
0022FD64 |00000007 ; Fixnum, value = 3
0022FD68 |00000009 ; Fixnum, value = 4
----------------------------------------------
0022FD6C |00A02CF8 ; 上一层函数保护的寄存器
0022FD70 |00A8FF90 ; 上一层函数保护的寄存器
0022FD74 /0022FD90 ; 老的老的栈帧指针[/code]
看栈顶,我没申请局部变量,这里EBP和ESP都指向0x0022FD58。于是:
1、([EBP + 0])老的栈帧指针
2、([EBP + 4])由call指令压到栈上的函数返回地址
下面可以看到生成的方法被调用的参数:
1、([EBP + 8])指向self的指针
2、([EBP + C])第一个显式参数,Fixnum
3、([EBP + 10])第二个显式参数,Fixnum
这印证了了把C函数注册为Ruby方法时,rb_method_define参数的argc为2时,所注册的函数签名应该与下述函数指针类型匹配:
[code="c">VALUE (*)(VALUE self, VALUE arg1, VALUE arg2)
谁调用了生成的代码?
继续观察前面例子中停在断点上的程序的函数调用栈,可以看到直接调用生成的代码的是call_cfunc(),它又是被vm_call_cfunc()所调用的。
vm_insnhelper.c:
static inline VALUE
call_cfunc(VALUE (*func)(), VALUE recv,
int len, int argc, const VALUE *argv)
{
/* printf("len: %d, argc: %d\n", len, argc); */
if (len >= 0 && argc != len) {
rb_raise(rb_eArgError, "wrong number of arguments(%d for %d)",
argc, len);
}
switch (len) {
case -2:
return (*func) (recv, rb_ary_new4(argc, argv));
break;
case -1:
return (*func) (argc, argv, recv);
break;
case 0:
return (*func) (recv);
break;
case 1:
return (*func) (recv, argv[0]);
break;
case 2:
return (*func) (recv, argv[0], argv[1]);
break;
/* ... */
case 15:
return (*func) (recv, argv[0], argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
argv[11], argv[12], argv[13], argv[14]);
break;
default:
rb_raise(rb_eArgError, "too many arguments(%d)", len);
break;
}
return Qnil; /* not reached */
}
static inline VALUE
vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp,
int num, ID id, ID oid, VALUE recv, VALUE klass,
VALUE flag, const NODE *mn, const rb_block_t *blockptr)
{
VALUE val;
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
{
rb_control_frame_t *cfp =
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC,
recv, (VALUE) blockptr, 0, reg_cfp->sp, 0, 1);
cfp->method_id = oid;
cfp->method_class = klass;
reg_cfp->sp -= num + 1;
val = call_cfunc(mn->nd_cfnc, recv, (int)mn->nd_argc, num, reg_cfp->sp + 1);
if (reg_cfp != th->cfp + 1) {
rb_bug("cfp consistency error - send");
}
vm_pop_frame(th);
}
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
return val;
}
上面两个函数都是Ruby 1.9虚拟机里关于方法调用的指令的实现。哪些指令会调用它们呢?
在vm.inc里匹配INSN_ENTRY(...),可以得知Ruby 1.9的虚拟机指令集有:
nop
getlocal
setlocal
getspecial
setspecial
getdynamic
setdynamic
getinstancevariable
setinstancevariable
getclassvariable
setclassvariable
getconstant
setconstant
getglobal
setglobal
putnil
putself
putobject
putspecialobject
putiseq
putstring
concatstrings
tostring
toregexp
newarray
duparray
expandarray
concatarray
splatarray
checkincludearray
newhash
newrange
pop
dup
dupn
swap
reput
topn
setn
adjuststack
defined
trace
defineclass
send
invokesuper
invokeblock
leave
finish
throw
jump
branchif
branchunless
getinlinecache
onceinlinecache
setinlinecache
opt_case_dispatch
opt_checkenv
opt_plus
opt_minus
opt_mult
opt_div
opt_mod
opt_eq
opt_neq
opt_lt
opt_le
opt_gt
opt_ge
opt_ltlt
opt_aref
opt_aset
opt_length
opt_succ
opt_not
opt_regexpmatch
opt_regexpmatch
opt_call_c_function
bitblt
answer
其中send与invokesuper指令的实现中使用了CALL_METHOD宏。该宏实现如下:
#define CALL_METHOD(num, blockptr, flag, id, me, recv) do { \
VALUE v = vm_call_method(th, GET_CFP(), num, blockptr, flag, id, me, recv); \
if (v == Qundef) { \
RESTORE_REGS(); \
NEXT_INSN(); \
} \
else { \
val = v; \
} \
} while (0)
然后里面的vm_call_method()里调用了前面的vm_call_cfunc():
static inline VALUE
vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp,
int num, const rb_block_t *blockptr, VALUE flag,
ID id, const rb_method_entry_t *me, VALUE recv)
{
VALUE val;
start_method_dispatch:
if (me != 0) {
if ((me->flag == 0)) {
normal_method_dispatch:
switch (me->type) {
/* ... */
case VM_METHOD_TYPE_CFUNC:{
val = vm_call_cfunc(th, cfp, num, recv, blockptr, flag, me);
break;
}
/* ... */
default:{
rb_bug("eval_invoke_method: unsupported method type (%d)", me->type);
break;
}
}
}
else {
/* ... */
}
}
else {
/* method missing */
}
RUBY_VM_CHECK_INTS();
return val;
}
Well, okay. 看过实际运行中的栈的状况后,我比较有信心能写机器码里受栈影响的指令了……