`
RednaxelaFX
  • 浏览: 3052624 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

beef

    博客分类:
  • Ruby
阅读更多
(以下内容不适合小盆友,请在老大哥指引下观看。实验危险,请不要在家尝试。)

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获取:http://github.com/LuiKore/bk201/tree/master
我用的是刚能运行的原型,现在在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  ; 老的老的栈帧指针


看栈顶,我没申请局部变量,这里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时,所注册的函数签名应该与下述函数指针类型匹配:
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. 看过实际运行中的栈的状况后,我比较有信心能写机器码里受栈影响的指令了……
分享到:
评论
8 楼 seen 2009-07-26  
gcc2.9.5至今还在很多地方辛勤工作着 估计老师上课还得靠这个才能照顾到老的讲义
最晚从3.3.x之后就加入防shellcode的招数了 safeguard啥的
我估计那安全讲义啥的就是把00年左右的phrack倒腾出来的结果。。。
那些东西看了是能学不少东西 但是要系统地学肯定是不合适地
坦途还是让学生们扎扎实实学学操作系统的实现 再自己折腾下gdb 加几个自定义功能啥的 自然而然的heap/stack什么的都懂了
phrack这东西 有了基础再去看才好玩
扯远了。。。
7 楼 RednaxelaFX 2009-07-25  
seen 写道
另外NX也不是唯一手段 有些软的手段(不依赖于intel)我猜ms的编译器/库函数应该也加入了 至少那砣人不可能不知道 但是不知道出于什么原因(可能又是历史原因兼容原因)所以默认没有打开等等

不管怎么说在ms的地盘上玩shellcode是没什么意思的 主要原因是因为。。。。ms的console太丑陋了。。。

rb应该有linux的实现吧?建议到linux上去试试你的shellcode 我觉得应该会有不同结果

嗯微软的DEP有硬件版和软件版。你看我在老笔记本上截的DEP设置的图就知道我这古董CPU不支持NX bit,所以用的DEP是软件版。
在Windows上用Ruby的人搞不好还没在Mac/Linux上用的人多……至少hardcore的人应该是在Linux上的多。嘛,回头有空倒是可以在我的Ubuntu上试试shellcode……不过听上了安全那门课的同学说他们有些实验得找很古老的GCC来能行(貌似是2.x的),新的GCC就已经会使得他们课上教的一些技巧用不了了……没上那门课,不知道他们老师讲了什么 XD
6 楼 seen 2009-07-24  
另外NX也不是唯一手段 有些软的手段(不依赖于intel)我猜ms的编译器/库函数应该也加入了 至少那砣人不可能不知道 但是不知道出于什么原因(可能又是历史原因兼容原因)所以默认没有打开等等

不管怎么说在ms的地盘上玩shellcode是没什么意思的 主要原因是因为。。。。ms的console太丑陋了。。。

rb应该有linux的实现吧?建议到linux上去试试你的shellcode 我觉得应该会有不同结果
5 楼 seen 2009-07-24  
关于ebp的问题我说的太含糊了所以你误会了
这个跟编译器有关而不是操作系统
以前的老黑客们可以在overflow之后改ebp导致跳到自己的代码里去
后来glibc做了改进 在某些关键函数里 把ebp换成了esp
大概意思是这样:
譬如以前你要找第一个参数就是 0x8(%ebp)也就是$ebp+8
但现在是0xc(%esp)也就是$esp+10
这些关键函数就包括黑客们最爱用的exec系列
所以就不是改ebp这么简单就能跳到你指定的地址了
当然这招也是防君子不防小人的了 只要用ret来移动esp即可

因为ms是从编译器到库函数到操作系统通吃 所以这些事情ms做起来应该更顺手吧
虽然xp是老早出的 但基本上xp+sp2是另外一个release/distro了吧
4 楼 RednaxelaFX 2009-07-24  
night_stalker 写道
广告一下:此 hello.rb 引用的 bk201 在 http://github.com/LuiKore/bk201/tree/master
这几天要拼命学 scala 没空去想很潮的东西鸟 … … (FX 靠你啦)

嘿嘿,NS老兄把家伙晒出来了~我把链接给加到帖里去
我接下来可能会有一段时间被断网,就在那个时候干点活儿吧
3 楼 night_stalker 2009-07-24  
广告一下:此 hello.rb 引用的 bk201 在 http://github.com/LuiKore/bk201/tree/master
这几天要拼命学 scala 没空去想很潮的东西鸟 … … (FX 靠你啦)
2 楼 RednaxelaFX 2009-07-23  
seen 写道
看了一系列你写的类似shellcode的帖子之后有个疑问:难道ms的os和编译器直到现在还允许运行栈上代码?还没有对内存检查NX bit?还没有保护ebp?还在用ebp来保存返回地址?还没有对栈进行保护?
这些早在03年fc1出来的时候就有了 而ms居然拖到vista才开始引进?
有了这些保护,要在栈上玩花样就麻烦不少了

Windows XP是2001年发布的,而且从Windows XP SP2开始已经引入了DEP(微软的NX实现)。问题是XP上的DEP默认只是对Windows的关键程序和服务才启动的:

这是为了避免在XP上带来程序不兼容的问题,因为在启用DEP机制前很多程序可能只是普通的malloc()了空间就用来放动态生成的代码了(当然,包括shellcoder写的程序 XD)

所以从Vista开始,DEP和ASLR都默认启动,安全性就稍微提高了些,代价是兼容性就下降了。
MSVC里有/NOCOMPAT选项来让程序指定它无法兼容DEP,告诉操作系统不要对它启用DEP;这当然是以更容易被shellcode攻击为代价的。

Windows上的NX实现跟Linux上的出现时间差不多,没什么特别值得惊讶的地方 =v=

另外Windows/x86上的程序没有默认用EBP来保存返回地址的。EBP默认是用来保存FP的,也就是frame pointer,栈帧指针。FP只在需要动态大小的栈帧时才特别有意义;还有就是SEH(Structured Exception Handling)需要用EBP来保持栈帧指针。
x86的ret指令会从栈顶pop出一个DWORD,把它当作返回地址进行跳转。这跟寄存器没什么直接关系……
1 楼 seen 2009-07-23  
看了一系列你写的类似shellcode的帖子之后有个疑问:难道ms的os和编译器直到现在还允许运行栈上代码?还没有对内存检查NX bit?还没有保护ebp?还在用ebp来保存返回地址?还没有对栈进行保护?
这些早在03年fc1出来的时候就有了 而ms居然拖到vista才开始引进?
有了这些保护,要在栈上玩花样就麻烦不少了

相关推荐

Global site tag (gtag.js) - Google Analytics