•
Programming
有些测试比较耗时间,而且很少被修改,如果能在测试的时候跳过它们就能让 spec 快不少。
跳过测试的方法很简单,spec 的 describe 方法可以给对应的测试加上标签,例如
describe SalesController, :slow => true do # specs end 接下来只要在 spec/spec_helper.rb 中声明跳过这个标签即可:
RSpec.configure do |config| config.filter_run_excluding :slow => true end 与 filter_run_excluding 相反的是 filter_run,指定会被运行的标签,不包含在这个列表中的测试将被忽略。
参考:http://www.dixis.com/?p=283
阅读全文 →
•
Algorithms
最近要给某个类写一个 hash 方法,这个类包括一些整型和字符串属性,需要把它们都放到 hash 中。担心自己想出来的 hash 算法会造成比较严重的冲突,网上搜了一下,发现 Effective Java中已经介绍过一种简单有效的算法了:
阅读全文 →
•
Tooling
前几天给我的 MacBook Pro 装上了 Lion,不过原来的 Emacs 并不支持在 Lion 下全屏运行。github 上搜了下发现已经有让 Emacs 支持全屏模式的补丁了,Homebrew 中这个补丁也已经被吸收。
直接用 brew install emacs –cocoa –srgb似乎会碰到编译错误:
Finding pointers to doc strings... Finding pointers to doc strings...done Dumping under the name emacs unexec: cannot write section __data --- List of All Regions --- address size prot maxp --- List of Regions to be Dumped --- address size prot maxp --- Header Information --- Magic = 0xfeedfacf CPUType = 16777223 CPUSubType = -2147483645 FileType = 0x2 NCmds = 20 SizeOfCmds = 3464 Flags = 0x00200085 Highest address of load commands in input file: 0x5dd000 Lowest offset of all sections in __TEXT segment: 0x22f0 --- List of Load Commands in Input File --- github issues 上已经有人报告这个问题了,解决方法也很简单,运行 brew edit emacs打开 emacs 的安装脚本,在 def install的后面加上两行:
阅读全文 →
•
Tooling
我平时用的系统是 Windows 7 和 Mac OS X,实验室项目一般都是 ssh 远登到 Ubuntu 和 Linux 上开发的。有时碰到内核和虚拟机等项目编译比较耗时,编译开始后要时不时的看一下编译任务是否完成,或者有没有中途出错,这时候如果有个通知系统就比较方便了。
Google 了一把找到了 netgrowl这个好东东,它是一个开源的 Python 模块,实现了 Growl 协议,可以向 Mac 或 Windows 上的 Growl 服务发送通知。使用也非常方便,先用 GrowlRegistrationPacket 函数注册一个应用,接着就可以用 GrowlNotificationPacket 发送通知了:
notify.py#!/usr/bin/python from netgrowl import * import sys title = "Notification from Ubuntu" desc = "" if len(sys.argv) > 2: title = sys.argv[1] desc = sys.argv[2] addr = ("10.131.251.101", GROWL_UDP_PORT) s = socket(AF_INET,SOCK_DGRAM) p = GrowlRegistrationPacket(application="Ubuntu", password="i") p.addNotification("Ubuntu Notifications", enabled=True) s.sendto(p.payload(), addr) p = GrowlNotificationPacket(application="Ubuntu", notification="Ubuntu Notifications", title=title, description=desc, priority=1, sticky=True, password="i") s.
阅读全文 →
•
Computer System
在上一篇文章中我们介绍了 return-oriented 这种攻击手段,它的强大之处在于攻击者不需要插入恶意代码,通过构造特殊的函数返回栈利用程序中原有的代码即可达到攻击者的目地。
北卡州立大学的学者们提出了一种防止 return-oriented 攻击的思路,思路很简单,一句话概括,就是去掉代码里所有的 ret 指令!
思路很简单,真正做起来还是很复杂的。x86 中的 ret 指令只有一个字节,即 0xc3。要去掉所有的 0xc3,不仅要修改原来代码中的 ret 指令,还要移除其他指令片段中的 0xc3(例如 movl $0xc3, %rax)。接下来我们来看看 EuroSys 10 上的这篇文章是怎么解决这些问题的。
首先是原来就作为 ret 指令用的 0xc3 代码。注意 return-oriented 之所以成功一大原因就是 ret 指令在返回时不会检查栈上的返回地址是否正确。要保证这一点,需要引入一个间接跳转层。传统的调用过程是调用者把返回地址压入栈上,然后被调用函数返回时从栈上得到返回地址并返回。现在我们加入一个新的跳转表,这张表里记录了所有的返回地址,而且它不在栈上,因此不能被攻击者修改。当调用者调用函数时,把返回地址在表中的序号压入栈上;函数返回时,从栈上读出地址序号,再查表得到实际地址,然后返回。通过引入这样一层额外的地址转换机制,攻击者就不能通过修改栈上返回地址让函数返回到任意地址了。
接下来我们要解决其他指令引入的 0xc3,这里面也分几类情况。首先是由于寄存器分配引起的。例如 movl %rax, %rbx 对应的机器码是 48 89 c3,这边就有个 0xc3。对于这一类代码,只需要在编译器做寄存器分配时把有可能产生 0xc3 的情况排除掉即可。
另一类是代码中直接使用了 0xc3 作为直接数。这种情况需要对代码进行适当的修补,以 cmp $0xc3, %ecx 为例,0xc3 这个直接数可以通过 0xc4 - 1 得到,于是这条指令可以被修改为:
mov $0xc4, %reg dec %reg cmp %reg, %ecx 到这里所有包含 0xc3 的代码都已经被修改成具有同等功能的不包含 0xc3 的版本了,也就彻底杜绝了 ret 指令被用来做 return-oriented 攻击的可能。对具体实现细节有兴趣的同学可以读一下这篇论文,作者借助 LLVM 生成了一个没有 0xc3 的 FreeBSD 内核。
阅读全文 →
•
Computer System
众多的安全漏洞中,栈溢出(stack-based buffer overflows)算是非常常见的了。一方面因为程序员的疏忽,使用了 strcpy、sprintf 等不安全的函数,增加了栈溢出漏洞的可能。另一方面,因为栈上保存了函数的返回地址等信息,因此如果攻击者能任意覆盖栈上的数据,通常情况下就意味着他能修改程序的执行流程,从而造成更大的破坏。
对于栈溢出漏洞,传统的攻击方式是嵌入攻击代码,然后修改栈上的返回地址,使它指向攻击代码段,从而执行攻击者指定的代码。本科时候上过一门计算机系统基础,其中的某一个lab就要求学生做这么一件事。
现在的安全技术已经能比较好的防范传统的栈溢出攻击了。常见的技术有这么几种:
随机空间,前面提到攻击者需要修改栈上返回地址,使它指向注入的代码起始地址。但如果用户栈的起始地址是随机分布的,甚至每次新建一个栈帧时的地址都有一定程度的随机波动,要获得准确的返回地址就很困难了。这个技术大大增加了代码嵌入攻击的难度,但是却没用从理论上杜绝成功的可能性。攻击者可以使用大量的空指令(nop),并在可以修改的区域重复添加攻击代码,以此增加攻击成功的几率。
W ^ X,把所有的可读可写页标记为不可执行,也就是说攻击者无法添加或修改可执行代码,这样包含了嵌入代码的页面就无法被攻击者调用执行了。Windows 的 DEP、 Linux 的 PaX 都利用了这一项技术。
此外还有一些诸如 StackGuard 等栈保护手段,不过由于它们对性能影响很大,实际中使用并不广泛。
这些手段使得在受保护的进程中利用栈溢出嵌入恶意代码并执行变得几乎不可能,然而这并不意味着栈溢出漏洞没有利用的价值了。聪明的黑客们想到了另外一种自定义程序行为的途径:利用程序或者动态库中原有的代码。这些代码虽然本身是良性的,但适当利用的话,同样可以产生恶意的效果。
最简单的手段就是著名的 return-to-libc 攻击。libc 中有一些函数可以用于执行其他的进程,例如 execve 和 system。这些函数很容易被攻击者利用,只要找到一个栈溢出漏洞,并适当的构造函数调用参数,并使栈上返回地址指向这些函数的起始地址,攻击者就能以这个程序的权限执行任意其他程序了!注意这里所有执行的代码都是合法的,所以前面提到的W^X技术对此就无能为力了。
return-to-libc 这种攻击方式也有一个局限,就是需要代码库中有 system 这样符合要求的函数,如果对于内核代码,或是检查调用来源的库,return-to-libc 就不那么给力了。于是另一种理论上更强大、也更难构造的攻击方式浮出水面,也就是标题的 return-oriented 攻击。
关于 return-oriented 攻击,我之前的一篇博文已经介绍过这个概念了,这里再解释一下。
一般程序中都包含着大量的返回指令(ret),它们通常位于一个函数的结尾,或是中途返回的地方。而这些返回指令之前的一两条指令,成为了 return-oriented 攻击指令的来源。攻击者要做的就是把这些零零碎碎的指令拼接起来,拼成一段恶意的代码。这里的难点有两个地方,一是怎么找到符合要求的代码片段,二是找到之后怎么拼接。
先来看第一个问题,可用的代码片段虽然多,但是都是固定的。这就意味着原来的一条指令现在可能需要多条指令执行后得到相同的效果了。举例来说,要把一个寄存器赋值为 4 的话,可能没有现成的直接赋值的代码片段,需要一条赋值为 1 的指令,和三条寄存器加 1 的指令拼凑而成。这样通过拼凑,受限的指令可以完成一些基本的操作,再由这些基本的操作,组成一段有实际意义的攻击代码。这里涉及到不少编译相关的知识,具体细节就不赘述了。
关于第二个问题,因为前面找到的代码片段都是以 ret 指令结尾的,所以只要把栈上的返回地址改成片段1的起始地址,代码片段1执行之后就会通过 ret 指令返回,此时读取的返回地址还是在被攻击的栈上,所以攻击者只要把对应位置的值改成代码片段2的起始地址,就能紧接着执行代码片段2了,如此循环,只要栈够大,就可以把攻击片段跑完。
对于 Linux 内核、glibc 这些庞大的程序来说,ret 指令前面一两条指令组成的代码库非常巨大,基本上可以达到图灵完备的要求了,也就是说,只要栈够大,任何程序都能由这些代码片段表达出来。另外这里为什么强调“一两条指令”呢?当然四五条甚至十几条指令的复用也是可以的,只是这样会大大增加搜索空间,要通过这些可能的代码片段生成一个程序需要太多的时间了。
那么如何防范 return-oriented 攻击呢?之后的博文里,我会介绍一些和它相关的国外研究。
阅读全文 →
•
Tooling
我经常使用的几台电脑中的Caps Lock 键都被我改成了 Ctrl 键,这样修改以后用起 Emacs 来就顺手多了。
最近在 Windows 上用 VMware Remote Control 远登虚拟机调试内核的时候,问题就出来了:可能是这个浏览器插件的 bug,有时键盘的 Caps Lock 会被莫名打开。然后我的这个键盘键位又比较少,不想再让 Caps Lock 键替换另一个用得更少的按键了,于是想到了软件关闭的方法。
搜了下 Stackoverflow 找到个很好用的 Python 库SendKeys,只要两行代码就能在 Windows 下模拟 Caps Lock 按键了:
import SendKeys SendKeys.SendKeys("{CAPSLOCK}") 另外在 Linux 要模拟按键,可以直接访问 /dev/console:
import fcntl import os KDSETLED = 0x4B32 console_fd = os.open('/dev/console', os.O_NOCTTY) # Turn on caps lock fcntl.ioctl(console_fd, KDSETLED, 0x04) # Turn off caps lock fcntl.ioctl(console_fd, KDSETLED, 0) 原问题地址
阅读全文 →
•
Programming
这几天看KVM代码的时候看到里面有个内联汇编的语法很陌生(下面的代码截取了部分内联汇编片段):
asm ( "mov %c[rax](%3), %%rax \n\t" "mov %c[rbx](%3), %%rbx \n\t" "mov %c[rdx](%3), %%rdx \n\t" "mov %c[rsi](%3), %%rsi \n\t" "mov %c[rdi](%3), %%rdi \n\t" : "=q" (fail) : "r"(vcpu->launched), "d"((unsigned long)HOST_RSP), "c"(vcpu), [rax]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RAX])), [rbx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RBX])), [rcx]"i"(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RCX])) : "cc", "memory" ); stackoverflow上问了下才知道这是gcc的operand substitution语法,%c后面跟上常量名,就能在内联汇编中使用这个常量了。
以这段代码为例,vcpu是struct kvm_vcpu类型,[rax]“i”(offsetof(struct kvm_vcpu, regs[VCPU_REGS_RAX])这句话把vcpu->regs[VCPU_REGS_RAX]相对于vcpu的偏移赋值给了rax这一常量。接下来回到第一行mov %crax, %%eax,%c[rax]等于前面得到的偏移量,加上vcpu并取值后就是vcpu->regs[VCPU_REGS_RAX]中保存的值了,这个指令会把这个值保存在%rax寄存器中,从而完成了vcpu的rax寄存器恢复工作。
阅读全文 →
•
Tooling
网上找了不少设置方法,终于翻出来一个可行的,和大家分享下。
1. 安装 corkscrew ArchLinux 和 Ubuntu 的源里就有,也可以从 http://www.agroman.net/corkscrew/ 下载源码编译一个。
2. 修改 ~/.ssh/config Host gitproxy User git Hostname ssh.github.com Port 443 ProxyCommand corkscrew proxy.example.com 3128 %h %p IdentityFile /home/username/.ssh/id_rsa 修改其中的 proxy.example.com 和 3128 为代理 IP 和端口,如果代理需要帐号密码,就在 ProxyCommand 这一行的最后加上密码文件,内容为用户名:密码。
参数 IdentitiFile 指定相应帐号的私钥文件地址。
另外 @cyfdecyf 同学指出只要把这里的 Host 改成 github.com,就可以直接用
[email protected]:user/repository 访问 GitHub 了。
3. 使用 git@gitproxy 访问 GitHub 例如要把 foo/bar.git 拖下来,执行 git clone git@gitproxy:foo/bar.git 即可。
原文地址:http://www.wetware.co.nz/blog/2010/03/cant-access-github-behind-proxy-or-firewall/
更新: 由于 GitHub 现在支持 HTTPS 协议了,所以更简单的方法是使用 GitHub 提供的 HTTPS 地址,然后用 git config --add http.
阅读全文 →
•
Computer System
Blueprint: Robust prevention of cross-site scripting attacks for existing browsers
这篇论文提出了一种防范是跨站脚本攻击(XSS)的新的方法,发在IEEE S&P 2009上,作者是UIUC的Mike Ter Louw。
所谓跨站脚本攻击,简单地说就是在网页中注入非法的脚本代码,从而达到攻击的效果。比较著名的例子有当年在MySpace上泛滥的Samy蠕虫,通过特殊的脚本注入手段,每一位访问Samy主页的用户,他们的主页都会被修改加上一段Samy is my hero文字,并且他们的主页也会被植入攻击代码,从而把这段脚本扩散给更多的用户。
通常防范跨站脚本攻击的方式有两种。一种做在服务器端,为每一段用户上传的内容做检查,并剔除恶意代码。但这种方式很难保证能过滤掉所有的恶意字符串,一方面攻击方法防不甚防,有兴趣的朋友可以参考下XSS Cheat Sheet,上面给出了很多一般人很难想到的攻击代码的组合方式。另一方面由于现在大多数论坛和博客都支持一些基本的文本修饰标签,所以简单的标签剔除或者重新编码都不可行。
另一种方法是做在浏览器端,但是由于浏览器无法区分某一段脚本到底是来源于不可信的用户还是可信的站点,所以这种方法实现起来也有很大的困难。
这里实现防范措施的一个难点在于,Web应用把生成HTML的返回给浏览器后,就不参与浏览器的HTML解析工作了。这样浏览器就不知道哪部分出现脚本是安全的,哪部分出现是不安全的。
BluePrint就着眼于这个点,提出了一种让Web应用“参与”HTML解析工作的设计。下面通过论文里面的一个例子,简单介绍下它的防范机制。
假如一位恶意的用户在一个博客上上传了这样一段含有恶意代码的留言:
<p> Here is a page you might find <b """><script>doEvil(. . .)</script>">very</b> interesting: <a href="  javasc
ript:doEvil(. . .);"> Link</a> </p><p style="nop:expres/*xss*/sion(doEvil(. . .))"> Respectfully, Eve </p> 可以看到,这段代码里包含了很多可能引发脚本执行的代码,而要在服务器端把这些所有隐藏的攻击可能找出来是一件比较困难的事。那么BluePrint是怎么在不知道这段代码是否含有恶意代码的前提下处理的呢?
首先,这种由用户上传的不可信的字符串会先在服务器端被解析成一棵树,就像HTML在浏览器中被解析一样,这棵HTML解析树可以用一些简单的DOM API来生成,例如appendChild, createElement等。这些描述如何生成HTML解析树的方法会和数据值(URL、标签属性等)一起,通过特殊的编码(Base64)传递给浏览器。例如上面这段代码,最后在浏览器接收到的HTML中,会变成这样:
<code style="display:none;" id="__bp1"> =Enk/sCkhlcmUgaXMgYSBwYWdlIHlvdSBta... =SkKICAgICI+dmVyeQ===C/k/QIGhlbHBmd... =ECg===C/Enk/gCiAgUmVzcGVjdGZ1bGx5L... </code><script id="__bp1s"> __bp__.cxPCData("__bp1", "__bp1s"); </script> 在浏览器端,这段特殊的代码会被JS库解析成自定义的命令和数据格式,并由前面提到的DOM API动态生成这些HTML结点,从而达到和传统的方式一样的显示效果。当然可信的HTML代码,例如文章正文,还是按传统的方式传输的。
阅读全文 →