首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

Ceph分布式存储学习指南1.7 Ceph对象存储

1.7 Ceph对象存储 对象存储是一种以对象形式而不是传统文件和块形式存储数据的方法。基于对象的存储已经引起了行业界的大量关注。为灵活地使用它们的巨量数据,这些组织正快速采用对象存储解决方案。Ceph是一个众所周知的真正的对象存储系统。 Ceph是一个分布式对象存储系统,通过它的对象网关(object gateway),也就是RADOS网关(radosgw)提供对象存储接口。RADOS网关利用librgw(RADOS网关库)和librados这些库,允许应用程序跟Ceph对象存储建立连接。Ceph通过RESTful API提供可访问且最稳定的多租户对象存储解决方案之一。 RADOS网关提供RESTful接口让用户的应用程序将数据存储到Ceph集群中。RADOS网关接口满足以下特点。 兼容Swift:这是为OpenStack Swift

优秀的个人博客,低调大师

Ceph分布式存储学习指南1.5 Ceph块存储

1.5 Ceph块存储 块存储是存储区域网络中使用的一个数据存储类别。在这种类型中,数据以块的形式存储在卷里,卷会挂接到节点上。它可以为应用程序提供更大的存储容量,并且可靠性和性能都更高。这些块形成的卷会映射到操作系统中,并被文件系统层控制。 Ceph引入了一个新的RBD协议,也就是Ceph块设备(Ceph Block Device)。RBD为客户端提供了可靠、分布式、高性能的块存储。RBD块呈带状分布在多个Ceph对象之上,而这些对象本身又分布在整个Ceph存储集群中,因此能够保证数据的可靠性以及性能。RBD已经被Linux内核支持,换句话说,RBD驱动程序在过去的几年里已经很好地跟Linux内核集成。几乎所有的Linux操作系统发行版都支持RBD。除了可靠性和性能之外,RBD也支持其他的企业级特性,例如完整和增量式快照,精简的配置

优秀的个人博客,低调大师

Ceph分布式存储学习指南3.1 Ceph存储架构

第3章 Ceph架构和组件 本章将涵盖以下几大主题: Ceph存储架构 Ceph RADOS Ceph对象存储设备(OSD) Ceph monitor(MON) librados Ceph块存储 Ceph对象网关 Ceph MDS和CephFS 3.1 Ceph存储架构 Ceph存储集群由几个不同的软件守护进程组成,每个守护进程负责Ceph的一个独特功能并将值添加到相应的组件中。每个守护进程是彼此独立的。这是保持Ceph存储集群比黑匣子似的商业存储系统更加便宜的诸多特性中的一个。 下图简要阐述了每个Ceph组件的功能。 可靠、自动、分布式对象存储(Reliable Autonomic Distributed Object Store,RADOS)是Ceph存储集群的基础。Ceph中的一切都以对象的形式存储,而RADOS就负责存储这些对象,而不考虑它们的数据类

优秀的个人博客,低调大师

Android:学习AIDL,这一篇文章就够了(上)

原文网址:http://blog.csdn.net/luoyanglizi/article/details/51980630 前言 在决定用这个标题之前甚是忐忑,主要是担心自己对AIDL的理解不够深入,到时候大家看了之后说——你这是什么玩意儿,就这么点东西就敢说够了?简直是坐井观天不知所谓——那样就很尴尬了。不过又转念一想,我辈年轻人自当有一种一往无前的锐气,标题大气一点岂不更好?并且大家都是文明人,总归更多的是理解与补充而不是侮辱与谩骂?所以最终还是厚颜用了这么一个不怎么有耻的标题。 好了,接下来进入正题,谈谈我对AIDL的理解和认识。 正文 1,概述 AIDL是一个缩写,全称是AndroidInterface Definition Language,也就是Android接口定义语言。是的,首先我们知道的第一点就是:AIDL是一种语言。既然是一种语言,那么相应的就很自然的衍生出了一些问题: 为什么要设计出这么一门语言? 它有哪些语法? 我们应该如何使用它? 再深入一点,我们可以思考,我们是如何通过它来达到我们的目的的? 更深入一点,为什么要这么设计这门语言?会不会有更好的方式来实现我们的目的? 接下来,我们就一步步的来解答上面的这些问题。 ps:1,在研究AIDL相关的东西之前,一些必要的知识储备是要有的。一方面是关于Android中service相关的知识,要了解的比较通透才行,关于这方面的东西可以参考Android中的Service:默默的奉献者 (1),Android中的Service:Binder,Messenger,AIDL(2)这两篇博文。另一方面是关于Android中序列化的相关知识,这方面的东西文中会简单提及,但是如果想要深入的研究一下的话最好还是去找一些这方面的资料看一下。2,我的编译环境为Android Studio2.1.2,SDK Version 23,JDK 1.7。 2,为什么要设计这门语言? 设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。 每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。每个进程之间都你不知我,我不知你,就像是隔江相望的两座小岛一样,都在同一个世界里,但又各自有着自己的世界。而AIDL,就是两座小岛之间沟通的桥梁。相对于它们而言,我们就好像造物主一样,我们可以通过AIDL来制定一些规则,规定它们能进行哪些交流——比如,它们可以在我们制定的规则下传输一些特定规格的数据。 总之,通过这门语言,我们可以愉快的在一个进程访问另一个进程的数据,甚至调用它的一些方法,当然,只能是特定的方法。 但是,如果仅仅是要进行跨进程通信的话,其实我们还有其他的一些选择,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行,在有些要求多进程的情况下不适用——这种时候就需要使用 AIDL 了。如果想要了解它们更详细的区别的话,可以去我的另一篇博文看看:Android中的Service:Binder,Messenger,AIDL(2) 3,它有哪些语法? 其实AIDL这门语言非常的简单,基本上它的语法和Java是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好——所以在这里我就着重的说一下它和 Java 不一样的地方。主要有下面这些点: 文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java。 数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下——在 Java 中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做Book.java,另一个叫做BookManager.aidl,它们都在com.lypeer.aidldemo包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上import com.lypeer.aidldemo.Book;哪怕 .java 文件和 .aidl 文件就在一个包下。 默认支持的数据类型包括: Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。 String 类型。 CharSequence类型。 List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。 Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。 定向tag:这是一个极易被忽略的点——这里的“被忽略”指的不是大家都不知道,而是很少人会正确的使用它。在我的理解里,定向 tag 是这样的:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。具体的分析大家可以移步我的另一篇博文:你真的理解AIDL中的in,out,inout么? 另外,Java 中的基本类型和 String ,CharSequence 的定向 tag默认且只能是 in。还有,请注意,请不要滥用定向 tag,而是要根据需要选取合适的——要是不管三七二十一,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的。 两种AIDL文件:在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。 注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。 下面是两个例子,对于常见的AIDL文件都有所涉及: // Book.aidl //第一类AIDL文件的例子 //这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用 //注意:Book.aidl与Book.java的包名应当是一样的 package com.lypeer.ipcclient; //注意parcelable是小写 parcelable Book; 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 // BookManager.aidl //第二类AIDL文件的例子 package com.lypeer.ipcclient; //导入所需要使用的非默认支持数据类型的包 import com.lypeer.ipcclient.Book; interface BookManager { //所有的返回值前都不需要加任何东西,不管是什么数据类型 List<Book> getBooks(); Book getBook(); int getBookCount(); //传参时除了Java基本类型以及String,CharSequence之外的类型 //都需要在前面加上定向tag,具体加什么量需而定 void setBookPrice(in Book book , int price) void setBookName(in Book book , String name) void addBookIn(in Book book); void addBookOut(out Book book); void addBookInout(inout Book book); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 4,如何使用AIDL文件来完成跨进程通信? 在进行跨进程通信的时候,在AIDL中定义的方法里包含非默认支持的数据类型与否,我们要进行的操作是不一样的。如果不包含,那么我们只需要编写一个AIDL文件,如果包含,那么我们通常需要写 n+1 个AIDL文件( n 为非默认支持的数据类型的种类数)——显然,包含的情况要复杂一些。所以我接下来将只介绍AIDL文件中包含非默认支持的数据类型的情况,至于另一种简单些的情况相信大家是很容易从中触类旁通的。 4.1,使数据类实现 Parcelable 接口 由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们不能像平时那样,传一个句柄过去就完事了——句柄指向的是一个内存区域,现在目标进程根本不能访问源进程的内存,那把它传过去又有什么用呢?所以我们必须将要传输的数据转化为能够在内存之间流通的形式。这个转化的过程就叫做序列化与反序列化。简单来说是这样的:比如现在我们要将一个对象的数据从客户端传到服务端去,我们就可以在客户端对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。 而通常,在我们通过AIDL进行跨进程通信的时候,选择的序列化方式是实现 Parcelable 接口。关于实现 Parcelable 接口之后里面具体有那些方法啦,每个方法是干嘛的啦,这些我就不展开来讲了,那並非这篇文章的重点,我下面主要讲一下如何快速的生成一个合格的可序列化的类(以Book.java为例)。 注:若AIDL文件中涉及到的所有数据类型均为默认支持的数据类型,则无此步骤。因为默认支持的那些数据类型都是可序列化的。 4.1.1,编译器自动生成 我当前用的编译器是Android Studio 2.1.2,它是自带了 Parcelable 接口的模板的,只需要我们敲几下键盘就可以轻松的生成一个可序列化的 Parcelable 实现类。 首先,创建一个类,正常的书写其成员变量,建立getter和setter并添加一个无参构造,比如: public class Book{ public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } private String name; private int price; public Book() {} } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 然后 implements Parcelable ,接着 as 就会报错,将鼠标移到那里,按下 alt+enter(as默认的自动解决错误的快捷键,如果你们的as有修改过快捷键的话以修改后的为准) 让它自动解决错误,这个时候它会帮你完成一部分的工作: 在弹出来的框里选择所有的成员变量,然后确定。你会发现类里多了一些代码,但是现在还是会报错,Book下面仍然有一条小横线,再次将鼠标移到那里,按下 alt+enter 让它自动解决错误: 这次解决完错误之后就不会报错了,这个 Book 类也基本上实现了 Parcelable 接口,可以执行序列化操作了。 但是请注意,这里有一个坑:默认生成的模板类的对象只支持为 in 的定向 tag 。为什么呢?因为默认生成的类里面只有writeToParcel()方法,而如果要支持为 out 或者 inout 的定向 tag 的话,还需要实现readFromParcel()方法——而这个方法其实并没有在 Parcelable 接口里面,所以需要我们从头写。具体为什么大家可以去看看:你真的理解AIDL中的in,out,inout么? 那么这个readFromParcel()方法应当怎么写呢?这样写: @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(price); } /** * 参数是一个Parcel,用它来存储与传输数据 * @param dest */ public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 name = dest.readString(); price = dest.readInt(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 像上面这样添加了readFromParcel()方法之后,我们的 Book 类的对象在AIDL文件里就可以用 out 或者 inout 来作为它的定向 tag 了。 此时,完整的 Book 类的代码是这样的: package com.lypeer.ipcclient; import android.os.Parcel; import android.os.Parcelable; /** * Book.java * * Created by lypeer on 2016/7/16. */ public class Book implements Parcelable{ public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } private String name; private int price; public Book(){} public Book(Parcel in) { name = in.readString(); price = in.readInt(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel in) { return new Book(in); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(price); } /** * 参数是一个Parcel,用它来存储与传输数据 * @param dest */ public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 name = dest.readString(); price = dest.readInt(); } //方便打印数据 @Override public String toString() { return "name : " + name + " , price : " + price; } } 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 至此,关于AIDL中非默认支持数据类型的序列化操作就完成了。 4.1.2,插件生成 我不是很清楚 Eclipse 或者较低版本的 as 上会不会像 as 2.1.2 这样帮我们在实现 Parcelable 接口的过程中做如此多的操作,但是就算不会,我们还有其他的招数——通过插件来帮我们实现 Parcelable 接口。 具体的实现方式和实现过程大家可以参见这篇文章:告别手写parcelable 4.2,书写AIDL文件 首先我们需要一个 Book.aidl 文件来将 Book 类引入使得其他的 AIDL 文件其中可以使用 Book 对象。那么第一步,如何新建一个 AIDL 文件呢?Android Studio已经帮我们把这个集成进去了: 鼠标移到app上面去,点击右键,然后 new->AIDL->AIDL File,按下鼠标左键就会弹出一个框提示生成AIDL文件了。生成AIDL文件之后,项目的目录会变成这样的: 比起以前多了一个叫做 aidl 的包,而且他的层级是和 java 包相同的,并且 aidl 包里默认有着和 java 包里默认的包结构。那么如果你用的是 Eclipse 或者较低版本的 as ,编译器没有这个选项怎么办呢?没关系,我们也可以自己写。打开项目文件夹,依次进入 app->src->main,在 main 包下新建一个和 java 文件夹平级的 aidl 文件夹,然后我们手动在这个文件夹里面新建和 java 文件夹里面的默认结构一样的文件夹结构,再在最里层新建 .aidl 文件就可以了: 注意看图中的文件目录。 Ok,如何新建AIDL文件说的差不多了,接下来就该写AIDL文件的内容了。内容的话如果上一节有认真看的话基本上是没什么问题的。在这里,我们需要两个AIDL文件,我是这样写的: // Book.aidl //第一类AIDL文件 //这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用 //注意:Book.aidl与Book.java的包名应当是一样的 package com.lypeer.ipcclient; //注意parcelable是小写 parcelable Book; 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 // BookManager.aidl //第二类AIDL文件 //作用是定义方法接口 package com.lypeer.ipcclient; //导入所需要使用的非默认支持数据类型的包 import com.lypeer.ipcclient.Book; interface BookManager { //所有的返回值前都不需要加任何东西,不管是什么数据类型 List<Book> getBooks(); //传参时除了Java基本类型以及String,CharSequence之外的类型 //都需要在前面加上定向tag,具体加什么量需而定 void addBook(in Book book); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 注意:这里又有一个坑!大家可能注意到了,在 Book.aidl 文件中,我一直在强调:Book.aidl与Book.java的包名应当是一样的。这似乎理所当然的意味着这两个文件应当是在同一个包里面的——事实上,很多比较老的文章里就是这样说的,他们说最好都在 aidl 包里同一个包下,方便移植——然而在 Android Studio 里并不是这样。如果这样做的话,系统根本就找不到 Book.java 文件,从而在其他的AIDL文件里面使用 Book 对象的时候会报 Symbol not found 的错误。为什么会这样呢?因为 Gradle 。大家都知道,Android Studio 是默认使用 Gradle 来构建 Android 项目的,而 Gradle 在构建项目的时候会通过 sourceSets 来配置不同文件的访问路径,从而加快查找速度——问题就出在这里。Gradle 默认是将 java 代码的访问路径设置在 java 包下的,这样一来,如果 java 文件是放在 aidl 包下的话那么理所当然系统是找不到这个 java 文件的。那应该怎么办呢? 又要 java文件和 aidl 文件的包名是一样的,又要能找到这个 java 文件——那么仔细想一下的话,其实解决方法是很显而易见的。首先我们可以把问题转化成:如何在保证两个文件包名一样的情况下,让系统能够找到我们的 java 文件?这样一来思路就很明确了:要么让系统来 aidl 包里面来找 java 文件,要么把 java 文件放到系统能找到的地方去,也即放到 java 包里面去。接下来我详细的讲一下这两种方式具体应该怎么做: 修改 build.gradle 文件:在 android{} 中间加上下面的内容: sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } } 1 2 3 4 5 1 2 3 4 5 也就是把 java 代码的访问路径设置成了 java 包和 aidl 包,这样一来系统就会到 aidl 包里面去查找 java 文件,也就达到了我们的目的。只是有一点,这样设置后 Android Studio 中的项目目录会有一些改变,我感觉改得挺难看的。 把 java 文件放到 java 包下去:把 Book.java 放到 java 包里任意一个包下,保持其包名不变,与 Book.aidl 一致。只要它的包名不变,Book.aidl 就能找到 Book.java ,而只要 Book.java 在 java 包下,那么系统也是能找到它的。但是这样做的话也有一个问题,就是在移植相关 .aidl 文件和 .java 文件的时候没那么方便,不能直接把整个 aidl 文件夹拿过去完事儿了,还要单独将 .java 文件放到 java 文件夹里去。 我们可以用上面两个方法之一来解决找不到 .java 文件的坑,具体用哪个就看大家怎么选了,反正都挺简单的。 到这里我们就已经将AIDL文件新建并且书写完毕了,clean 一下项目,如果没有报错,这一块就算是大功告成了。 4.3,移植相关文件 我们需要保证,在客户端和服务端中都有我们需要用到的 .aidl 文件和其中涉及到的 .java 文件,因此不管在哪一端写的这些东西,写完之后我们都要把这些文件复制到另一端去。如果是用的上面两个方法中的第一个解决的找不到 .java 文件的问题,那么直接将 aidl 包复制到另一端的 main 目录下就可以了;如果是使用第二个方法的话,就除了把把整个 aidl 文件夹拿过去,还要单独将 .java 文件放到 java 文件夹里去。 4.4,编写服务端代码 通过上面几步,我们已经完成了AIDL及其相关文件的全部内容,那么我们究竟应该如何利用这些东西来进行跨进程通信呢?其实,在我们写完AIDL文件并 clean 或者 rebuild 项目之后,编译器会根据AIDL文件为我们生成一个与AIDL文件同名的 .java 文件,这个 .java 文件才是与我们的跨进程通信密切相关的东西。事实上,基本的操作流程就是:在服务端实现AIDL中定义的方法接口的具体逻辑,然后在客户端调用这些方法接口,从而达到跨进程通信的目的。 接下来我直接贴上我写的服务端代码: /** * 服务端的AIDLService.java * <p/> * Created by lypeer on 2016/7/17. */ public class AIDLService extends Service { public final String TAG = this.getClass().getSimpleName(); //包含Book对象的list private List<Book> mBooks = new ArrayList<>(); //由AIDL文件生成的BookManager private final BookManager.Stub mBookManager = new BookManager.Stub() { @Override public List<Book> getBooks() throws RemoteException { synchronized (this) { Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString()); if (mBooks != null) { return mBooks; } return new ArrayList<>(); } } @Override public void addBook(Book book) throws RemoteException { synchronized (this) { if (mBooks == null) { mBooks = new ArrayList<>(); } if (book == null) { Log.e(TAG, "Book is null in In"); book = new Book(); } //尝试修改book的参数,主要是为了观察其到客户端的反馈 book.setPrice(2333); if (!mBooks.contains(book)) { mBooks.add(book); } //打印mBooks列表,观察客户端传过来的值 Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString()); } } }; @Override public void onCreate() { super.onCreate(); Book book = new Book(); book.setName("Android开发艺术探索"); book.setPrice(28); mBooks.add(book); } @Nullable @Override public IBinder onBind(Intent intent) { Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString())); return mBookManager; } } 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 整体的代码结构很清晰,大致可以分为三块:第一块是初始化。在 onCreate() 方法里面我进行了一些数据的初始化操作。第二块是重写 BookManager.Stub 中的方法。在这里面提供AIDL里面定义的方法接口的具体实现逻辑。第三块是重写 onBind() 方法。在里面返回写好的 BookManager.Stub 。 接下来在 Manefest 文件里面注册这个我们写好的 Service ,这个不写的话我们前面做的工作都是无用功: <service android:name=".service.AIDLService" android:exported="true"> <intent-filter> <action android:name="com.lypeer.aidl"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service> 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 到这里我们的服务端代码就编写完毕了,如果你对里面的一些地方感觉有些陌生或者根本不知所云的话,说明你对 Service 相关的知识已经有些遗忘了,建议再去看看这两篇博文:Android中的Service:默默的奉献者 (1),Android中的Service:Binder,Messenger,AIDL(2)。 4.5,编写客户端代码 前面说过,在客户端我们要完成的工作主要是调用服务端的方法,但是在那之前,我们首先要连接上服务端,完整的客户端代码是这样的: /** * 客户端的AIDLActivity.java * 由于测试机的无用debug信息太多,故log都是用的e * <p/> * Created by lypeer on 2016/7/17. */ public class AIDLActivity extends AppCompatActivity { //由AIDL文件生成的Java类 private BookManager mBookManager = null; //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中 private boolean mBound = false; //包含Book对象的list private List<Book> mBooks; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_aidl); } /** * 按钮的点击事件,点击之后调用服务端的addBookIn方法 * * @param view */ public void addBook(View view) { //如果与服务端的连接处于未连接状态,则尝试连接 if (!mBound) { attemptToBindService(); Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show(); return; } if (mBookManager == null) return; Book book = new Book(); book.setName("APP研发录In"); book.setPrice(30); try { mBookManager.addBook(book); Log.e(getLocalClassName(), book.toString()); } catch (RemoteException e) { e.printStackTrace(); } } /** * 尝试与服务端建立连接 */ private void attemptToBindService() { Intent intent = new Intent(); intent.setAction("com.lypeer.aidl"); intent.setPackage("com.lypeer.ipcserver"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStart() { super.onStart(); if (!mBound) { attemptToBindService(); } } @Override protected void onStop() { super.onStop(); if (mBound) { unbindService(mServiceConnection); mBound = false; } } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(getLocalClassName(), "service connected"); mBookManager = BookManager.Stub.asInterface(service); mBound = true; if (mBookManager != null) { try { mBooks = mBookManager.getBooks(); Log.e(getLocalClassName(), mBooks.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { Log.e(getLocalClassName(), "service disconnected"); mBound = false; } }; } 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 同样很清晰,首先建立连接,然后在 ServiceConnection 里面获取 BookManager 对象,接着通过它来调用服务端的方法。 4.6,开始通信吧! 通过上面的步骤,我们已经完成了所有的前期工作,接下来就可以通过AIDL来进行跨进程通信了!将两个app同时运行在同一台手机上,然后调用客户端的addBook()方法,我们会看到服务端的 logcat 信息是这样的: //服务端的 log 信息,我把无用的信息头去掉了,然后给它编了个号 1,on bind,intent = Intent { act=com.lypeer.aidl pkg=com.lypeer.ipcserver } 2,invoking getBooks() method , now the list is : [name : Android开发艺术探索 , price : 28] 3,invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333] 1 2 3 4 1 2 3 4 客户端的信息是这样的: //客户端的 log 信息 1,service connected 2,[name : Android开发艺术探索 , price : 28] 3,name : APP研发录In , price : 30 1 2 3 4 1 2 3 4 所有的 log 信息都很正常并且符合预期——这说明我们到这里为止的步骤都是正确的,按照上面说的来做是能够正确的使用AIDL来进行跨进程通信的。 结语 这一篇文章主要介绍了我们在概述里提到的前三个问题,即: 为什么要设计AIDL语言? AIDL的语法是什么? 如何使用AIDL语言完成跨进程通信? 本来我是准备在这篇文章里把我那五个问题都讲完的,结果写到这里发现篇幅已经有些长了,再写的话可能就少有人有这个耐性读下去了——那么写在后面的这些又有什么意义呢?于是就干脆从这里截断,将AIDL的工作原理和它的设计思想以及我对于它的这种设计的一些看法放在下一篇博文里来讲述——刚好,有那么点基础篇和提高篇的意思,哈哈。 文中相关代码可点击传送门下载。

优秀的个人博客,低调大师

【Spark Summit East 2017】为Walmart Search学习使用Streaming和DataFrames

更多精彩内容参见云栖社区大数据频道https://yq.aliyun.com/big-data;此外,通过Maxcompute及其配套产品,低廉的大数据分析仅需几步,详情访问https://www.aliyun.com/product/odps。 本讲义出自Nirmal Sharma与Yan Zheng在Spark Summit East 2017上的演讲,主要介绍了Walmart使用Spark Streaming和DataFrames构建的搜索产品的情况,Walmart Lab目前已经能够成功地使用多个微型批处理spark streaming管道对于可获取的产品信息进行近乎实时的更新,并分享了仅依靠Spark Data Frames建立的可伸缩的异常检测框架,该框架能够用于检测异常搜索信息。最后,还分享了Walmart Lab得出的观点:Spark Streaming与Data Frames是处理大规模实时数据流的关键技术。

优秀的个人博客,低调大师

Nginx学习日记第四篇 -- 反向代理及缓存功能

一、Nginx反向代理 Nginx中的ngx_http_proxy_module模块可以实现后端服务器的反向代理功能,这样就可以实现客户端请求的动静分离以及负载均衡功能。 1、实验场景 Nginx主机作为反向代理服务器将客户端请求发往node1主机web服务器 Nginx主机IP:192.168.0.110 node1主机IP:192.168.0.40 2、Nginx主机配置 grep-Ev"#|^$"server.conf server{ listen80; server_namelocalhost; location/{ proxy_passhttp://192.168.0.40; proxy_set_headerHost$host; proxy_set_headerX-Real-IP$remote_addr; } error_page500502503504/50x.html; location=/50x.html{ roothtml; } } [root@Nginxconf]#nginx-t nginx:theconfigurationfile/usr/local/nginx/conf/nginx.confsyntaxisok nginx:configurationfile/usr/local/nginx/conf/nginx.conftestissuccessful [root@Nginxconf]#nginx-sreload 3、node1主机配置为apache服务器 [root@node1~]#servicehttpdstart Startinghttpd:httpd:Couldnotreliablydeterminetheserver'sfullyqualifieddomainname,using192.168.0.15forServerName [OK] [root@node1~]#curl192.168.0.40 Thisisnode1 4、测试反向代理 5、注意事项 如果是/uri反向代理到后端,那么/uri在后端可以为/newuri; 如果/uri是使用模式匹配的,则直接补在代理链接后面 如果proxy之前有定义rewrite的话,那么proxy将使用rewrite后的uri做代理 proxy_pass http://192.168.0.40; proxy_set_header Host $host; 自定义客户端请求的首部的值 proxy_set_header X-Real-IP $remote_addr; 自定义头部信息加入客户端IP 6、自定义头部信息后的日志信息 [root@node1~]#vim/etc/httpd/conf/httpd.conf LogFormat"%{X-Real-IP}i%l%u%t\"%r\"%>s%b\"%{Referer}i\"\"%{User-Agent}i\""combined [root@node1~]#servicehttpdreload [root@node1~]#cat/etc/httpd/logs/access_log 192.168.0.109--[06/Feb/2017:13:56:20+0800]"GET/user/HTTP/1.0"304-"-""Mozilla/5.0(WindowsNT6.1;WOW64;rv:51.0)Gecko/20100101Firefox/51.0" 192.168.0.109--[06/Feb/2017:13:56:21+0800]"GET/user/HTTP/1.0"304-"-""Mozilla/5.0(WindowsNT6.1;WOW64;rv:51.0)Gecko/20100101Firefox/51.0" 7、部分代理(动静分离) Nginx部分 [root@Nginxconf]#!grep grep-Ev"#|^$"server.conf server{ listen80; server_namelocalhost; location/{ roothtml/xn1; indexindex.html; } location/user{ proxy_passhttp://192.168.0.40; proxy_set_headerHost$host; proxy_set_headerX-Real-IP$remote_addr; } error_page500502503504/50x.html; location=/50x.html{ roothtml; } } [root@Nginxconf]#nginx-sreload node1部分 [root@node1~]#mkdir/var/www/html/user [root@node1~]#echo"Thisisnode1user">>/var/www/html/user/index.html [root@node1~]#servicehttpdreload 测试: 本地处理请求的部分 反向代理的部分: 更多的proxy指令,详见:http://nginx.org/en/docs/http/ngx_http_proxy_module.html 二、proxy缓存功能 当Nginx将客户端请求反向代理至后端服务器时,建立的是keep-alive连接;代理服务器与客户端,代理服务器与后端web server都建立长链,这会降低Nginx性能,所以这时候,proxy模块的缓存功能就派上用场了,代理服务器与客户端之间依旧保持长链,而代理服务器与后端web server之间请求结束后,代理服务器将内容缓存在本地,与后端不建立长链,大大节省了系统资源;同时,当客户端请求来时,代理服务器会直接去缓存中寻找并返回给客户端。代理服务器将缓存存在内存中,以key-value形式存储,value存储的是指向本地文件系统中存储的URL的哈希值。 1、配置缓存功能 1.创建缓存目录 [root@Nginxconf]#mkdir-pv/cache/nginx/ mkdir:已创建目录"/cache" mkdir:已创建目录"/cache/nginx/" [root@Nginxconf]#chown-Rnginx:nginx/cache/nginx 2.在配置文件的http段定义缓存目录 [root@Nginxconf]#vimnginx.conf proxy_cache_path/cache/nginx/keys_zone=mycache:32m; 3.在server或location区段均可调用,根据实际情况使用,此处在location区段调用 [root@Nginxconf]#!grep grep-Ev"#|^$"server.conf server{ listen80; server_namelocalhost; location/{ roothtml/xn1; indexindex.html; } location/user{ proxy_cachemycache; proxy_cache_valid2003h; proxy_cache_valid30130210m; proxy_cache_validall1m; proxy_cache_use_staleerrortimeouthttp_500http_502http_503; proxy_passhttp://192.168.0.40; proxy_set_headerHost$host; proxy_set_headerX-Real-IP$remote_addr; } error_page500502503504/50x.html; location=/50x.html{ roothtml; } } [root@Nginxconf]#nginx-sreload 4.请求后,缓存目录中出现缓存 [root@Nginxconf]#ll/cache/nginx/ 总用量4 -rw-------1nginxnginx4622月617:09a8d7f3cb1968f4e6056774a5a3a73468 5.cache语句 proxy_cache_path/cache/nginx/keys_zone=mycache:32m; 定义缓存在文件系统中的保存路径,定义key值在内存中的变量名与大小,其余诸多选项有默认配置,定义在哪个位置,就有哪些配置可使用缓存 proxy_cachemycache; 使用mycache缓存 proxy_cache_valid2003h; 以响应状态码定义缓存保存时长,可定义多个 proxy_cache_use_staleerrortimeouthttp_500http_502http_503; 定义在遇到什么情况下可以使用过期缓存响应客户端 2、补充说明 缓存功能可以定义在http,server,location区段,定义在哪个区段表示有哪些请求可以使用缓存;一般来说,我们把缓存路径定义在http段,调用缓存根据具体情况配置。 其他cache语句:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache

优秀的个人博客,低调大师

流媒体技术学习笔记之(九)减少VLC 延迟的方法

之前写过一篇关于在Linux平台上编译Android平台上VLC播放器源代码的文章,vlc这款播放器非常优秀而且是开源的,它的核心是开源视频编解码库ffmpeg。而且这款播放器还支持RTSP协议,这个主要是用开源的live555来实现的,live555这个库以后还需要认真研习。 一、PC端(Windows平台)VLC播放RTSP视频延迟问题解决 前几天用PC平台上的VLC播放RTSP流媒体时延迟时间较长,存在1s左右的延迟,效果不是很好,后来查了资料,发现这个延迟时间是可以修改的。 找到工具->首选项,然后参数设置左下角选择“全部”,左边选择“输入编解码”->“网络缓存”选项,可以根据具体需要加以修改,具体见下图不过这个值不要太小,否则缓存太小,播放视频的过程中会很卡 如果网络环境较好,300ms以内效果很好,实时性较好。 二:PC端(Linux平台)VLC播放RTSP视频延迟问题解决 这里我的测试平台是ubuntu,linux平台上的vlc可以以命令行的方式来运行,而命令行是可以带参数的,我们只需要在参数里面指定这个延迟时间就可以了,下面是具体的命令行形式,大家可以根据各自需要加以调整 vlc rtsp://192.168.1.1:8556/test.avi --newwork-caching=300 rtsp的地址要根据自己需要播放的视频地址修改,网络延迟时间也可以根据需要修改,这个值越小,实时性越好,当然太小了播放会很卡,或者设置的值失效,一般建议在100-300之间 三.Android平台VLC播放RTSP视频延迟问题解决 首先在Android平台上的VLC播放器不像windows平台上UI里提供了设置延迟时间这一项功能,其次也不像Linux平台上可以以命令行的方式来运行,我们也就不能通过设置参数的方式来修改这个延迟时间,怎么办呢,只剩下最后一条路,修改源代码,将延迟时间写死在源码里,有了前面linux平台下设置参数的经验,就可以在源码里面通过grep工具来查找”network-caching”,发现有很多文件包含这个值,下面是搜索的结果,点击(此处)折叠或打开 vlc/src/libvlc-module.c: add_integer("network-caching",CLOCK_FREQ/1000, vlc/NEWS: - --network-caching specifies cachingfornetwork resources, vlc/modules/gui/macosx/simple_prefs.m: TestCaC("network-caching",10/3); vlc/modules/gui/macosx/simple_prefs.m: CaC("network-caching",10/3); vlc/modules/gui/qt4/components/open_panels.cpp: emit methodChanged(qfu("network-caching")); vlc/modules/gui/qt4/components/simple_preferences.cpp: TestCaC("network-caching",10/3); vlc/modules/gui/qt4/components/simple_preferences.cpp: CaC("network-caching",10/3); vlc/modules/access/file.c:*pi_64=var_InheritInteger(p_access,"network-caching"); vlc/modules/access/rtp/rtp.c:*v=INT64_C(1000)*var_InheritInteger(demux,"network-caching"); vlc/modules/access/ftp.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/live555.cpp:*var_InheritInteger(p_demux,"network-caching"); vlc/modules/access/http.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/sftp.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/udp.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/smb.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/tcp.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/rtsp/access.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/mms/mmsh.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/mms/mmstu.c:*var_InheritInteger(p_access,"network-caching"); vlc/modules/access/vnc.c:*var_InheritInteger(p_demux,"network-caching"); vlc-android/jni/libvlcjni.c: libvlc_media_add_option(p_md,":network-caching=1500"); 对这些文件的代码进行了一些分析,最后发现目标在vlc/src/libvlc-module.c这个文件,于是修改这个文件的1832行,如下图所示,将CLOCK_FREQ / 1000改为CLOCK_FREQ / 3000,这里可以根据各自的需要加以修改,分母越大,这个值就越小,实时性也就越好,当然跟前面所说一样,不能太小点击(此处)折叠或打开 1832//add_integer("network-caching",CLOCK_FREQ/1000, 1833//NETWORK_CACHING_TEXT,NETWORK_CACHING_LONGTEXT,true) 1834 add_integer("network-caching",CLOCK_FREQ/3000, 1835 NETWORK_CACHING_TEXT,NETWORK_CACHING_LONGTEXT,true) 最后再编译运行发现延迟时间大概在200ms左右,比修改之前实时性有明显改善,实时性很好,希望这篇文章能给需要相关资料的朋友提供一些帮助。

优秀的个人博客,低调大师

Android 渗透测试学习手册 第八章 ARM 利用

第八章 ARM 利用 作者:Aditya Gupta 译者:飞龙 协议:CC BY-NC-SA 4.0 在本章中,我们将了解 ARM 处理器的基础知识,和 ARM 世界中存在的不同类型的漏洞。 我们甚至会继续利用这些漏洞,以便对整个场景有个清晰地了解。 此外,我们将研究不同的 Android root 攻击和它们在漏洞利用中的基本漏洞。 考虑到目前大多数 Android 智能手机都使用基于 ARM 的处理器,对于渗透测试人员来说,了解 ARM 及其附带的安全风险至关重要。 8.1 ARM 架构导论 ARM 是基于精简指令集(RISC)的架构,这意味着其指令比基于复杂指令集(CISC)的机器少得多。 ARM 处理器几乎遍布我们周围的所有设备,如智能手机,电视,电子书阅读器和更多的嵌入式设备。 ARM 总共有 16 个可见的通用寄存器,为 R0-R15。 在这 16 个中,有 5 个用于特殊目的。 以下是这五个寄存器及其名称: R11: 帧指针 (FP) R12: 过程内寄存器 (IP) R13: 栈指针 (SP) R14: 链接寄存器 (LR) R15: 程序计数器 (PC) 下面的图展示了 ARM 架构: 在五个里面,我们会特别专注于这三个,它们是: 堆栈指针(SP):这是保存指向堆栈顶部的指针的寄存器 链接寄存器(LR):当程序进入子过程时存储返回地址 程序计数器(PC):存储要执行的下一条指令 注意 这里要注意的一点是,PC 将总是指向要执行的指令,而不是简单地指向下一条指令。 这是由于被称为流水线的概念,指令按照以下顺序操作:提取,解码和执行。 为了控制程序流,我们需要控制 PC 或 LR 中的值(后者最终引导我们控制 PC)。 执行模式 ARM 有两种不同的执行模式: ARM 模式:在 ARM 模式下,所有指令的大小为 32 位 Thumb 模式:在 Thumb 模式下,指令大部分为 16 位 执行模式由 CPSR 寄存器中的状态决定。 还存在第三模式,即 Thumb-2 模式,它仅仅是 ARM 模式和 Thumb 模式的混合。 我们在本章不会深入了解 ARM 和 Thumb 模式之间的区别,因为它超出了本书的范围。 8.2 建立环境 在开始利用 ARM 平台的漏洞之前,建议你建立环境。 即使 Android SDK 中的模拟器可以通过模拟 ARM 平台来运行,大多数智能手机也是基于 ARM 的,我们将通过配置 QEMU(它是一个开源硬件虚拟机和模拟器)开始 ARM 漏洞利用。 为了在 Android 模拟器/设备上执行以下所有步骤,我们需要下载 Android NDK 并使用 Android NDK 中提供的工具为 Android 平台编译我们的二进制文件。 但是,如果你使用 Mac 环境,安装 QEMU 相对容易,可以通过键入brew install qemu来完成。 现在让我们在 Ubuntu 系统上配置 QEMU。 遵循以下步骤: 第一步是通过安装依赖来下载并安装 QEMU,如图所示: sudo apt-get build-dep qemu wget http://wiki.qemu-project.org/download/qemu- 1.7.0.tar.bz2 接下来,我们只需要配置QEMU,指定目标为 ARM,最后充分利用它。 因此,我们将简单地解压缩归档文件,访问该目录并执行以下命令: ./configure --target-list=arm-softmmu make && make install 一旦QEMU成功安装,我们可以下载 ARM 平台的 Debian 镜像来进行利用练习。 所需下载列表位于http://people.debian.org/~aurel32/qemu/armel/。 这里我们将下载格式为qcow2的磁盘映像,它是基于 QEMU 的操作系统映像格式,也就是我们的操作系统为debian_squeeze_armel_standard.qcow2。 内核文件应该是vmlinuz-2.6.32-5-versatile,RAM 磁盘文件应该是initrd.img-2.6.32-versatile。 一旦我们下载了所有必要的文件,我们可以通过执行以下命令来启动 QEMU 实例: qemu-system-arm -M versatilepb -kernel vmlinuz-2.6.32-5- versatile -initrd initrd.img-2.6.32-5-versatile -hda debian_squeeze_armel_standard.qcow2 -append "root=/dev/sda1" --redir tcp:2222::22 redir命令只是在登录远程系统时使用端口 2222 启用 ssh。 一旦配置完成,我们可以使用以下命令登录到 Debian 的 QEMU 实例: ssh root@[ip address of Qemu] -p 2222 登录时会要求输入用户名和密码,默认凭据是root:root。一旦我们成功登录,我们将看到类似如下所示的屏幕截图: 8.3 基于栈的简单缓冲区溢出 简单来说,缓冲区是存储任何类型的数据的地方。 当缓冲区中的数据超过缓冲区本身的大小时,会发生溢出。 然后攻击者可以执行溢出攻击,来获得对程序的控制和执行恶意载荷。 让我们使用一个简单程序的例子,看看我们如何利用它。 在下面的截图中,我们有一个简单的程序,有三个函数:weak,ShouldNotBeCalled和main。 以下是我们试图利用的程序: 在整个程序运行期间,从不调用ShouldNotBeCalled函数。 漏洞函数简单地将参数复制到名为buff的缓冲区,大小为 10 字节。 一旦我们完成程序编写,我们可以使用gcc编译它,如下一个命令所示。 此外,我们将在这里禁用地址空间布局随机化(ASLR),只是为了使场景稍微简单一些。 ASLR 是由 OS 实现的安全技术,来防止攻击者有效地确定载荷的地址并执行恶意指令。 在 Android 中,ASLR 的实现始于 4.0。 你可以访问http://www.duosecurity.com/blog/exploit-mitigations-in-android-jelly-bean-4-1了解所有 Android 安全实施。 echo 0 > /proc/sys/kernel/randomize_va_space gcc -g buffer_overflow.c -o buffer_overflow 接下来,我们可以简单将二进制文件加载到 GNU 调试器,简称 GDB,然后开始调试它,如下面的命令所示: gdb -q buffer_overflow 现在我们可以使用disass命令来反汇编特定的函数,这里是ShouldNotBeCalled,如下面的截图所示: 正如我们在上面的截图中可以看到的,ShouldNotBeCalled函数从内存地址0x00008408开始。 如果我们查看main函数的反汇编,我们看到漏洞函数在0x000084a4被调用并在0x000084a8返回。 因此,由于程序进入漏洞函数并使用易受攻击的strcpy,函数不检查要复制的字符串的大小,并且如果我们能够在程序进入漏洞函数时控制子过程的 LR ,我们就能够控制整个程序流程。 这里的目标是估计何时 LR 被覆盖,然后放入ShouldNotBeCalled的地址,以便调用ShouldNotBeCalled函数。 让我们开始使用一个长参数运行程序,如下面的命令所示,看看会发生什么。 在此之前,我们还需要在漏洞函数和strcpy调用的地址设置断点。 b vulnerable b *<address of the strcpy call> 一旦我们设置了断点,我们可以使用参数AAAABBBBCCCC来运行我们的程序,看看它是如何被覆盖的。 我们注意到它在漏洞函数的调用处命中了第一个断点,之后在strcpy调用处命中了下一个断点。 一旦它到达断点,我们可以使用x命令分析堆栈,并指定来自 SP 的地址,如下面的截图所示: 我们可以看到,堆栈已经被我们输入的缓冲区覆盖(ASCII:41 代表 A,42 代表 B,等等)。 从上面的截图中,我们看到,我们仍然需要四个更多的字节来覆盖返回地址,在这种情况下是0x000084a8。 所以,最后的字符串是 16 字节的垃圾,然后是ShouldNotBeCalled的地址,如下面的命令所示: r `printf "AAAABBBBCCCCDDDD\x38\x84"` 我们可以在下面的截图中看到,我们已经将IShouldNeverBeCalled的起始地址添加到了参数中: 请注意,由于这里是小端结构,字节以相反的顺序写入。 一旦我们运行它,我们可以看到程序ShouldNotBeCalled函数被调用,如下面的截图所示: 8.4 返回导向编程 在大多数情况下,我们不需要调用程序本身中存在的另一个函数。 相反,我们需要在我们的攻击向量中放置 shellcode,这将执行我们在 shellcode 中指定的任何恶意操作。 但是,在大多数基于 ARM 平台的设备中,内存中的区域是不可执行的,这会阻止我们放置并执行 shellcode。 因此,攻击者必须依赖于所谓的返回导向编程(ROP),它是来自内存不同部分的指令片段的简单链接,最终它会执行我们的 shellcode。 这些片段也称为 ROP gadget。 为了链接 ROP gadget,我们需要找到存在跳转指令的 gadget,这将允许我们跳到另一个位置。 例如,如果我们在执行程序时反汇编seed48(),我们将注意到以下输出: 如果我们查看反汇编,我们将注意到它包含一个 ADD 指令,后面跟着一个 POP 和 BX 指令,这是一个完美的 ROP gadget。 这里,攻击者可能会想到,为了将其用作 ROP gadget,首先跳到控制 r4 的 POP 指令,然后将比/bin/sh的地址小 6 的值放入 r4 中,将 ADD 指令的值放入 LR 中。 因此,当我们跳回到 ADD 也就是R0 = R4 + 6时,我们就拥有了/bin/sh的地址,然后我们可以为 R4 指定任何垃圾地址并且为 LR 指定system()的地址。 这意味着我们将最终跳转到使用参数/bin/sh的system(),这将执行 shell。 以同样的方式,我们可以创建任何 ROP gadget,并使其执行我们所需要的任何东西。 由于 ROP 是开发中最复杂的主题之一,因此强烈建议你自己尝试,分析反汇编代码并构建漏洞。 8.5 Android root 利用 从早期版本的 Android 开始,Android root 漏洞开始出现于每个后续版本和不同的 Android 设备制造商的版本中。 Android root 简单来说是获得对设备的访问特权,默认情况下设备制造商不会将其授予用户。 这些 root 攻击利用了 Android 系统中存在的各种漏洞。 以下是其中一些的列表,带有漏洞利用所基于的思想: Exploid:基于 udev 中的 CVE-2009-1185 漏洞,它是 Android 负责 USB 连接的组件,它验证 Netlink 消息(一种负责将 Linux 内核与用户连接的消息)是否源自原始来源或是由攻击者伪造。因此,攻击者可以简单地从用户空间本身发送 udev 消息并提升权限。 Gingerbreak:这是另一个漏洞,基于 vold 中存在的漏洞,类似于 Exploid 中的漏洞。 RageAgainstTheCage:此漏洞利用基于RLIMIT_NPROC,它指定在调用setuid函数时可为用户创建的进程的最大数目。 adb 守护程序以 root 身份启动;然后它使用setuid()调用来解除特权。但是,如果根据RLIMIT_NPROC达到了最大进程数,程序将无法调用setuid()来解除特权,adb 将继续以 root 身份运行。 Zimperlich:使用与 RageAgainstTheCage 的相同概念,但它依赖于 zygote 进程解除 root 权限。 KillingInTheNameOf:利用了一个称为ashmem(共享内存管理器)接口的漏洞,该漏洞用于更改ro.secure的值,该值确定设备的 root 状态。 这些是一些最知名的 Android 漏洞利用,用于 root Android 设备。 总结 在本章中,我们了解了 Android 利用和 ARM 利用的不同方式。 希望本章对于任何想要更深入地利用 ARM 的人来说,都是一个好的开始。 在下一章中,我们将了解如何编写 Android 渗透测试报告。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册