2017-10-12
cy# ?expand 在cycript新版本中失效,可用.toString()代替
寻找UI控件的UI函数(点击它之后的响应函数),给UIView对象加上响应函数一般是通过[UIControl addTarget:action:forControlEvents:]实现的,UIControl提供了一个actionsForTarget:forContentEvent:方法来获得这个UIControl的响应函数,示例(破解搜狐视频下载问题):
cy# button = #0x14798410
.......................(输出0x14798410对象)
cy# [button allTargets]
[NSSet setWithArray:@[#"LongVideoDetailHeaderViewController":0x14609d00]]]
cy# [button allControlEvents]
64
cy# [button actionsForTarget:#0x14609d00 forControlEvent:64]
@["downloadBtnAction"](输出UI函数)
- 通过UI函数寻找目标函数(借助反汇编工具在函数调用链中寻找【目前未使用(原因:对ARM汇编指令不熟,对于代码调用逻层次的逻辑不清晰,后面改善)】)
使用另一种方式一步步寻找目标函数
寻找到UI控件后,循环调用cycript命令[#0x14798410 nextResponder]直到输出该控件的controller(根据MVC设计原则,M与V通过C相连,此命令要么返回它的superView要么最终返回它的controller)
拿到controller后,根据controller的名字在class-dump的headers文件中找到该.h文件,分析文件(由于我们的目的是修改下载权限,这个控件的功能函数势必与download相关(变量命名规范的情况下),然后在.h文件中寻找相关的function或property,通过寻找,初步把范围锁定在enforceLimitButtonsWithDownload、headerViewDidClickDownload、updateDownloadStatus三个function
然后开始通过编写Tweak测试,在xcode中查看日志(比较繁琐的一种方法,后面可改为用cycript、lldb调试)),通过日志输出,可定位到headerViewDidClickDownload函数
然后我们的想法就是怎么修改这个函数的逻辑(后面的事实会证明这个想法的天真,这一步得到的函数不一定是我们真正要切入的函数==【事后发现】),打开hopper,将砸壳后的SOHUVideo.decrypted文件拖进去,进行反汇编,在hopper左侧的functions窗体的search框中输入[LongVideoHeaderDetailViewController: headerViewDidClickDownload] ,右侧会出现该函数的汇编代码,有点晦涩难懂,不过没关系,点击上方的【if(a)..】字样的按钮,转变成可读的伪代码形式
下一步我们开始分析该函数的内部代码逻辑;大致浏览完该函数的伪代码后,会发现一个对我们来说很敏感的一行代码
`if (([r0 canBeDownLoaded] &0x1) == 0x0) gotoloc_1001e9fa8; `
这行代码明显是做视频是否可下载的条件判断,点击
gotoloc_1001e9fa8
,跟踪它的跳转函数:loc_1001e9fa8: [r21release]; goto loc_1001e9fb0`
点击
goto loc_1001e9fb0
继续跟踪:进入AlbumVideo类的(_BOOL)canBeDownloaded方法,好了,看到这,我们应该就能确定它就是我们最终要crack的函数了,然后上演暴力美学,编写Tweak,hook该函数,内部逻辑转换为return true;== 下一步,make package install 一条龙服务,再次运行App,打开刚才的视频下载页面,点击下载按钮,可以download
虽然问题初步得到解决,但是,目前修改的这个好像只能用于当前视频下载界面,所以如果碰到其他由于版权问题不能下载的视频,仍不能有效解决问题(除非再次hook,那也不太现实)
所以说,后面的工作就是熟练使用cycript和lldb,找找看有没有一劳永逸的解决方法
- 解析函数参数(未使用)
description函数能够把对象的内容表示成一个NSString,object_getClassName函数能够把对象的类名表示成一个char*,两者可分别用“%@”和“%s”打印出来
2017-10-17
unzip 命令 unzip -v -q **.zip -d /tmp (-v覆盖解压;-q无提示执行命令;-d指定解压目录)
sshPass :免密码进行ssh交互
libimobiledevice 库:方便的对设备进行数据读写;
ideviceinstaller(.ipa文件安装管理器)
sshpass安装:
- 下载http://sourceforge.net/projects/sshpass/
- tar -zxvf sshpass-版本号.tar.gz
- cd sshpass-版本号
- 检查环境:./configure
- make install
- sshpass(success)
libimobiledevice安装:
- brew install libimobiledevice
brew link libimobiledevice(如若报错:/usr/local/lib/pkgconfig is not writable.执行下一步)
chown apple /usr/local/lib/pkgconfig (修改文件所属用户)
idevice_id (安装success)
ideviceinstaller安装:
- brew install ideviceinstaller
- ideviceinstaller(success)
在使用ideviceinstaller -i .ipa文件 -o 设备UDID 安装ipa文件时遇到 Could not connect to lockdownd 错误,解决方法:
在终端运行:sudo chmod -R 777 /var/db/lockdown :给当前目录下所有子目录及文件添加rwx权限
Clutch 砸壳工具使用:
- 下载https://github.com/KJCracks/Clutch/releases
- 将Clutch文件拷贝到/usr/bin/目录下(通过ifuntool工具或其他)
- cd /usr/bin
- chmod +x Clutch (赋予可执行权限)
- $ Clutch(success)
ios第三方应用脱壳步骤整理:
将手机连接到电脑上后,在手机上打开应用
远程连接手机:ssh root@手机IP地址 (root:以root权限登录手机终端)
获取应用进程号及对应的可执行文件路径:ps -e | grep /var/mobile (-e 输出所有进程信息)
判断应用是否加密:otool -l 应用可执行文件名称 | grep -B 2 crypt (-l 打印loadcommands -B 2 控制显示的行数)
若无加密,执行此步骤:切入进程,获取应用Documents目录路径,cycript -p 进程号,[NSHomeDirectory()]应用根路径打印路径(Documents的上级目录路径)
scp dumpdecrypted.dylib root@手机IP:/Documents路径
cd 到Doucumetns目录下, DYLD__INSERT__LIBRARIES=dumpdecrypted.dylib 可执行文件路径
将.decrypted文件拷贝到电脑上 scp root@手机IP:/.decrypted文件路径 目的路径
class-dump -S -s -H .decrypted -o 目的路径
mac下安装class-dump工具
在根目录下新建bin文件夹
将dmg中的class-dump文件拷贝到bin目录下
vim ~/.bash_profile 在文件末添加export PATH=$HOME/ bin:$PATH
source ~/.bash_profile
iOS判断加密:对运行时应用otool -l HipRock |grep -B2 crypt 没有找到cryptid标志位
LC_ENCRYPTION_INFO:unknown load command
对脱壳后的.decrypted文件分析,对加密前后的文件LC_ENCRYPTION_INFO分析发现,第一幅图中的四个十六进制依次对应加密位置,加密部分大小,加密标志位...
缓冲区溢出及SSP(stack smashing protection)机制:
缓冲区:在c/c++中,指由使用数组或malloc、new等内存分配方法来实现的。
缓冲区溢出:指数据被添加到分配给该缓冲区的内存块之外。
SSP使用一个修改过的编译器,在函数调用中插入一个探测仪以探测堆栈溢出。它对存储局部变量的位置进行重新排序,并复制函数参数中的指针,并复制函数参数中的指针,以便它们也在任何数组之前,它意味着缓冲区溢出不会修改指针值。它不会检测所有函数,只是检测需要保护的函数(主要是使用字符数组的函数)。
SSP由GCC的“-fstack-protector”选项提供,GCC的SSP机制实现了两种类型的栈保护:其一:变量在栈中的顺序发生了改变;其二:启用了canary(金丝雀)值检测栈EIP是否被修改。栈中局部变量的组织方式重排列即编译器将所有的局部数组变量放置在栈的高地址位,尽可能地将所有buffers放置在接近canary的位置上,且尽可能地远离各个变量指针,此时利用数组溢出方式无法覆盖其它关键变量如局部函数指针等。
GCC4.1中三个与堆栈保护有关的编译选项:
-fstack-protector:启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码。
-fstack-protector-all:启用堆栈保护,为所有函数插入保护代码。
-fno-stack-protector:禁用堆栈保护。
iOS中堆栈的理解:https://elliotsomething.github.io/2014/09/18/iOS中堆栈的理解/
xcode5.0之后新建iOS工程,默认都是ARC模式。
Mach-O文件格式解析
windows支持exe,linux支持elf,mac os 支持mach-o;
mach 是一种操作系统内核,在Mach上,一种可执行的文件格是就是Mach-O(Mach Object file format),Mac OS X是Unix的“后代”,但所主要支持的可执行文件格式是Mach-O,其它的还包括脚本文件、胖二进制文件。
Mach-O文件可分为三部分:header、load commands、data。
Mach-O 头(Mach Header):这里描述了 Mach-O 的 CPU 架构、文件类型以及加载命令等信息;
加载命令(Load Command):描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令表示;
数据区(Data):Data 中每一个段(Segment)的数据都保存在此,段的概念和 ELF 文件中段的概念类似,都拥有一个或多个 Section ,用来存放数据和代码。
文件签名(魔数,magic)解析:
根据文件签名可以精确的确定可执行文件的格式。
#!脚本
0xcafebabe/0xbebafeca包含多种架构支持的二进制,又被称为通用二进制或者胖二进制
0xfeedface(32位),0xfeedfacf(64位)Mach-O格式或者叫OSX原生二进制格式
胖二进制(fat):
通用二进制只不过是其支持的各种架构的二进制文件(即mach-o文件)的打包文件,Universal Binary只不过将支持不同架构的Mach-O打包在一起,再在文件起始位置加上Fat Header来说明所包含的Mach-O文件支持的架构和偏移地址信息。
大部分情况下,xxx.app/xxx文件并不是Mach-O格式文件,由于现在需要支持不同CPU架构的iOS设备,所以我们编译打包出来的执行文件是一个Universal Binary格式文件。
- 通用二进制文件的处理工具叫做
lipo
脂肪的意思,对应胖二进制的意思 - 这个工具可以提取,删除和替换通用二进制文件中制定架构的二进制代码,因此可以通过这个工具对二进制文件进行瘦身。
nfat_arch字段指明当前的通用二进制文件中包含了多少个不同架构的 Mach-O 文件。
mach-o
首先通过otool工具查看mach-o文件头:
magic魔数代表32或者64位;
define MH_MAGIC 0xfeedface /* the mach magic number */ # define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
cputype和subcuptype代表cup类型
filetype代表文件类型(可执行文件,库文件,核心转储文件,内核扩展)
ncmds和sizeofcmds用于加载的家在命令的条数和大小
flags代表动态连接器(dyld)的标志
查看所有编译进去的方法:nm HipRock.decrypted
查看所有被使用的方法:otool -s -v __DATA __objc_selrefs HipRock.decrypted
加载命令解析:
Load Commands不仅定义了文件的逻辑结构,还定义了文件在虚拟内存中的布局。他们是Mach-O文件的核心。
LC_SEGMENT_64:(Load command 0-3)定义了一个(64-bit)的segments及其中的sections在文件加载时如何映射到进程地址空间中。
LC_SYMTAB:(Load command 5)定义了文件的符号表(stabs style)和字符表。链接器在链接文件的时候会用到,调试器也可以将它们将符号表映射到与它们相对应的源码文件中,以便于调试问题所在。
LC_DYSYMTAB:(Load command 6)为动态链接器提供了额外的信息,用以如何将符号写进符号表中。
LC_DYLD_INFO_ONLY:(Load command 4)定义了一些额外的与动态链接压缩信息,包含信号的元数据和动态绑定其它事情的操作码。“_ONLY”后缀意味着这条load command需要在程序中运行,因此那些不懂这条load command的旧的链接器将无法运行。
LC_LOAD_DYLINKER:(Load command 7)加载动态链接器,macOS上是( /usr/lib/dyld)
LC_LOAD_DYLIB:(Load command 12)加载动态链接共享库。如:“/usr/lib/libSystem.B.dylib”一个用标准C语言函数库。每一个动态库都会被动态链接器加载进内存,它们内部包含一张符号名称与地址链接在一起的符号表。
LC_LOAD_WEAK_DYLIB:
LC_RPATH:
LC_MAIN:(Load command 11)指定了程序main()函数的入口地址。
LC_UUID:(Load command 8)提供了一个随机的唯一标示符(UUID),通常由静态连接器生成。
LC_VERSION_MIN_IPHONEOS:(Load command 9)该二进制文件所支持的最低iphone系统版本。
LC_SOURCE_VERSION:(Load command 10)用于建立二进制文件的源码版本。
LC_FUNTION_STARTS:(Load command 13)定义了一张包含所有函数开始位置的表。
LC_DATA_IN_CODE:(Load command 14)定义了code segemnt中的一张非指令表。
LC_DYLIB_CODE_SIGN_DRS:(Load command 15)为动态链接库定义了代码签名。
LC-CODE-SIGNATURE 在内核中由load-code-signature 函数处理 (进行数字签名)
LC-ENCRYPTION-INFO 在内核中由 set-code-unprotect 函数处理 (加密二进制文件)
segment:每一个segment定义了一片虚拟内存区域,也就是动态链接器将映射到的进程地址空间。每一个segment都包含零个或更多的sections。segemnt的每一个section包含特定类型的代码和数据。最后一个segment是链接编辑段(link edit segment),它与用户级挂钩。该segment包含链接编辑信息表(tables of link edit information),如:符号表(symbol table),字符表(string table)等等,动态加载器会根据这些表将可执行文件或Mach-O包和与他依赖的库进行链接。
PAGEZERO :作为可执行文件的第一个segment,该segment被放置在虚拟内存开始的位置权限为可读写。PAGEZERO segment在被映射入内存后(在当前的架构对于基于Intel和PowerPC的Mac计算机而言,该空间大小为4096字节用十六进制表示为0x1000)占据虚拟内存一页的空间,而在存储在磁盘上市,__PAGESIZE segment不不占据任何空间,所以他加载进内存中所需要的空间要比存储在磁盘上的空间更大。顺便说一句该segment是隐藏恶意代码的好地方。
TEXT:包含可执行代码和只读数据。为了使得内核方便的将该段直接从可执行文件中映射到共享内存中,静态链接器设置该段在虚拟内存的权限为只读。当该段被映射到内存的时候,它被所有对它感兴趣的进程所共享(这正是framework,bundle和shared library的主要用途,但是它可以在同一个执行程序中运行多个拷贝),只读属性同时意味着由该segment组成的内存页不需要再被写回到磁盘中。当内核需要释放物理内存时,它只需简单地释放一页或更多页的TEXT,然后在需要的时候重新从磁盘将他们读取到内存。
DATA:一个包含可读写数据的segment。静态链接器设置这段虚拟内存的属性为可读写。因为它是可写读写的,共享库的
DATA段会被拷贝到每一个与这个库链接的进程中。当可读写__DATA段被映射到虚拟内存页的时候,内核将他们标记为写时拷贝(copy-on-write);因此当进程向某页写入数据时,该进程将会接受一个该页的私有拷贝。
segment和section通常是通过名称进行访问的,segment的名称由两个底杠加上大写字母组成(如:__TEXT);section的名称由两个底杠加上小写字母组成(如:__text)。这些命名规则都是约定俗成的。
section:http://www.desgard.com/iOS-Source-Probe/C/mach-o/Mach-O 文件格式探索.htm
用途 | |
---|---|
__TEXT.__text |
主程序代码 |
__TEXT.__cstring |
C 语言字符串 |
__TEXT.__const |
const 关键字修饰的常量 |
__TEXT.__stubs |
用于 Stub 的占位代码,很多地方称之为桩代码。本质上是一小段会直接跳入lazybinding的表对应项指针指向的地址的代码 |
__TEXT.__stubs_helper |
当 Stub 无法找到真正的符号地址后的最终指向 |
__TEXT.__objc_methname |
Objective-C 方法名称 |
__TEXT.__objc_methtype |
Objective-C 方法类型 |
__TEXT.__objc_classname |
Objective-C 类名称 |
__DATA.__data |
初始化过的可变数据 |
__DATA.__la_symbol_ptr |
lazy binding 的指针表,表中的指针一开始都指向__stub_helper |
__DATA.nl_symbol_ptr |
非 lazy binding 的指针表,每个表项中的指针都指向一个在装载过程中,被动态链接器搜索完成的符号 |
__DATA.__const |
没有初始化过的常量 |
__DATA.__cfstring |
程序中使用的 Core Foundation 字符串(CFStringRefs ) |
__DATA.__bss |
BSS,存放为初始化的全局变量,即常说的静态内存分配 |
__DATA.__common |
没有初始化过的符号声明 |
__DATA.__objc_classlist |
Objective-C 类列表 |
__DATA.__objc_protolist |
Objective-C 原型 |
__DATA.__objc_imginfo |
Objective-C 镜像信息 |
__DATA.__objc_selfrefs |
Objective-Cself 引用 |
__DATA.__objc_protorefs |
Objective-C 原型引用 |
__DATA.__objc_superrefs |
Objective-C 超类引用 |
__DATA.__mod_init_func | 初始化函数,在main之前调用 |
__DATA.__mod_term_func | 终止函数,在main之后调用 |
__TEXT:
__text:存放可执行机械代码的section;
__stubs:间接地符号存根。代表着lazy引用和no-lazy引用做要跳转的地址值。
__stubs_helper:为延迟加载符号提供帮助。
__cstring:存储只读的C语言格式字符串(如:“hello, wold!0”)的section,链接器会对该section进行去重的处理。
unwind_info:栈展开信息的压缩格式。该section是链接器对‘eh_frame’section里面的信息进行压缩产生的。
__eh_frame:一个标准的用于异常处理的section,它负责为DWARF格式提供栈展开信息。
__DATA:
_nl_symbol_ptr:指向非延迟导入符号(non-lazy imported symbols)的表。
__la_symbol_ptr:指向延迟导入符号(lazy imported symbols)的表。
__OBJC:
一个包含用于Ojbectiv-C语言动态运行时库数据的segment。
__IMPORT:
一个只有在IA-32架构的计算机中才会产生的segment。
__LINKEDIT:
段包含会被动态链接器使用的原生数据,如:符号,字符串和重定位表入口
symbol table :目标文件的符号表包含定位和重定位程序的符号定义和符号引用所需的信息
string table :字符串表部分保存以空字符结尾的字符序列,通常称为字符串。目标文件使用这些字符串来表示符号和部分名称。
字符串表包含符号表中太长内联符号的名称。要读取字符串表,将文件指针放在符号表之后(通常在读取符号后立即读取字符串),然后将四个字节作为一个32位小端字节读取。
mach-o格式文件执行过程: http://www.codexiu.cn/ios/blog/8709/
创建进程
分配虚拟地址空间,加载动态连接器
加载共享库,绑定符号
启动程序