Quantcast
Channel: Essence Sharing | 干货分享 - iOSRE
Viewing all 301 articles
Browse latest View live

Tweak 全局开启任意 App 的 WebView 调试

$
0
0

@ChiChou wrote:

iOS 7 之后的 Safari 提供了远程调试设备上网页的功能。在设备和 mac 端的 Safari 上均开启开发者功能之后,可以用 USB 连接手机,然后在 Develop 菜单中选择对应的页面打开 WebInspector:

b401ef22702ede3ec6e03fbf499dad95

先说明另一种屡试不爽的办法,砸壳 -> MonkeyDev 重打包。

然后我们来看越狱设备下如何全局地开启。

App 是否支持 WebInspector 是通过 entitlement 控制的。已知将 com.apple.security.get-task-allow 设置为 true 之后会允许调试 WebView。Xcode 编译出来的调试版本 App 都会带上这个 entitlement,这也是 lldb 真机调试必须的配置。

MobileSafari 肯定不允许 lldb 调试,不过可以看到(iOS 11.1.2)它注册了一个这样 entitlement:

在 iOS 设备上启用了 WebInspector 之后会出现一个 webinspectord 的守护进程。关于远程调试实现的一些技术细节可以参考 Webkit远程调试协议实战。几年前就有的文章,在此膜拜一下。

上面提到的文章同样也没有解决 entitlement 的条件,还是需要自己逆向一下。这个进程的代码只有一点点:

其实是放在链接库里了。

把 dyld_shared_cache 拖回来分析。

  • com.apple.private.webinspector.allow-remote-inspection
  • com.apple.security.get-task-allow

很快定位到字符串表:

通过交叉引用来到如下函数:

bool __cdecl -[RWIRelayDelegateIOS _allowApplication:bundleIdentifier:](id a1, SEL a2, struct {unsigned int var0[8];} *a3, id a4)
{
  __int128 *v4; // x21
  id v5; // x20
  __int64 v6; // x19
  char v7; // w20
  __int128 v9; // [xsp+0h] [xbp-80h]
  __int128 v10; // [xsp+10h] [xbp-70h]
  __int128 v11; // [xsp+20h] [xbp-60h]
  __int128 v12; // [xsp+30h] [xbp-50h]
  __int128 v13; // [xsp+40h] [xbp-40h]
  __int128 v14; // [xsp+50h] [xbp-30h]

  v4 = (__int128 *)a3;
  v5 = a1;
  v6 = MEMORY[0x18F5A5488](a4, a2);
  if ( qword_1B0981AD0 != -1 )
    dispatch_once(&qword_1B0981AD0, &unk_1AC56C870);
  if ( byte_1B0981AC8 )
    goto LABEL_14;
  v14 = v4[1];
  v13 = *v4;
  if ( MEMORY[0x18F5A547C](v5, selRef__hasRemoteInspectorEntitlement_[0], &v13) & 1 ) // 开启了 allow-remote-inspection
    goto LABEL_14;
  if ( qword_1B0981AE0 != -1 )
    dispatch_once(&qword_1B0981AE0, &unk_1AC56C8B0);
  if ( byte_1B0981AD8
    && (v12 = v4[1], v11 = *v4, MEMORY[0x18F5A547C](v5, selRef__hasCarrierRemoteInspectorEntitlement_[0], &v11) & 1) )
  { // 特定条件下检查的是 com.apple.private.webinspector.allow-carrier-remote-inspection
LABEL_14:
    v7 = 1;
  }
  else
  {
    v10 = v4[1];
    v9 = *v4;
    v7 = MEMORY[0x18F5A547C](v5, selRef__usedDevelopmentProvisioningProfile_[0], &v9); // 开发版本 App 同样放行
  }
  MEMORY[0x18F5A5484](v6);
  return v7;
}

这正是检查是否允许调试的关键函数。

使用 frida hook 框架简单验证一下:

➜  passionfruit git:(master) ✗ frida -U webinspectord
     ____
    / _  |   Frida 10.6.61 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at http://www.frida.re/docs/home/

[iPad 4::webinspectord]-> Interceptor.attach(ObjC.classes.RWIRelayDelegateIOS['- _allowApplication:bundleIdentifier:'].implementation, {
                            onEnter: function(args) {
                              this.bundleId = new ObjC.Object(args[3]);
                            },
                            onLeave: function(retVal) {
                              const allow = !retVal.equals(NULL)
                              console.log(this.bundleId + (allow ? ' allows' : ' does not allow') + ' WebInspect')
                              if (!allow) {
                                console.log('now patch it');
                                retVal.replace(ptr(1));
                              }
                            }
                          });

{}
[iPad 4::webinspectord]-> com.tencent.mipadqq does not allow WebInspect
now patch it
com.mx.MxBrowser-iPhone does not allow WebInspect
now patch it
com.apple.WebKit.WebContent allows WebInspect
com.mx.MxBrowser-iPhone does not allow WebInspect
now patch it
com.apple.WebKit.WebContent allows WebInspect
com.mx.MxBrowser-iPhone does not allow WebInspect
now patch it

每次启动新应用的时候都会调用这个函数做一次判断,将其返回值 patch 为 TRUE,第三方浏览器出现在了 Safari 的调试列表中:

另外 macOS 上的 WebInspector 也有类似函数 __int64 __fastcall -[RWIRelayDelegateMac _allowApplication:bundleIdentifier:],检查的 entitlement 键名略有不同。

用 THEOS 写成 Tweak,简单粗暴:

{ Filter = { Bundles = ( "com.apple.webinspectord" ); }; }

Tweak.xm

%hook RWIRelayDelegateIOS

- (BOOL)_allowApplication:(void *)ignored bundleIdentifier:(NSString *)bundleId {
  %log;

  NSLog(@"Force WebInspect enable for %@", bundleId);
  return TRUE;
}

%end

更新

本文在 11.1.2 和 10.3.3 上测试通过。

有同学反馈 10.0.2 没有 RWIRelayDelegateIOS 类,我验证了一下 10.0.3 的 IPSW 固件,函数是一样的。只不过直接编译到 webinspectord 而不是链接 WebInspector.framework。拆分链接库应该是 iOS 11 开始的。

在 iOS 9.3.3 上类名不一样,应该对 WebInspectorRelayDelegateIOS 的 - _allowApplication:bundleIdentifier: 进行 hook。其他 iOS 版本的兼容性还有待进一步分析。

P.S.
顺便求问一下这样需要判断兼容性来 hook 不同类名的 Tweak 应该怎么写呢?

Posts: 10

Participants: 3

Read full topic


逆网易云音乐 vip下载

$
0
0

@weiliang.soon wrote:

网易云音乐是一款专注于…(官网摘的)

干正事:
网易云音乐有些曲目听是可以的,但是下载需要VIP,不出意外,是可以拿到试听地址添加到下载地址上的。

下载歌曲:
数据模型:downloadUrlInfo、playUrlInfo; 我们悄悄的把downloadUrlInfo 换成 playUrlInfo 当然还要处理一些BOOL值,例如:canDownloadMusic之类。

去除广告…
NMAppDelegate里面有一些初始化广告的动作…

检测版本…
也是在NMAppDelegate里面,如果懂汉语拼音很容易猜出来。

网易云有包名检测,不正确的包名会登录不了。

还有其他的,我忘记了

附上可以直接运行的工程:https://github.com/sunweiliang/NeteaseMusicCrack

最后,希望大家可以多多支持网易云。

Posts: 23

Participants: 12

Read full topic

PP助手官网直接下载越狱版本ipa的方法

$
0
0

@iblue wrote:

简书 https://www.jianshu.com/p/c789f52b1cdf

Mac上连接了越狱的手机,pp助手才会进行一键安装,不太方便。这里介绍一种,直接从pp助手官网下载越狱版本ipa的方法。

以小米运动为例,搜索框输入“小米运动”:

1、查看网网源码,找到搜索相关的代码:

<a href="https://www.25pp.com/ios/search_app_0/小米运动/" id="search-app" data-href="https://www.25pp.com/ios/search_app_0/" 
class="search-link act-link" data-stat-pos="appSearch" data-stat-exp="page=default;search_type=0;search_keyword=小米运动">
包含“<span class="key-word">小米运动</span>”的应用</a>

2、过滤出搜索请求: https://www.25pp.com/ios/search_app_0/小米运动/

3、点击请求,跳转到搜索结果页面:

继续查看网页源码,找到小米运动详情相关的代码:

<a class="app-icon" href="https://www.25pp.com/ios/detail_1571646/" target="_blank" onclick="showAppDetail(this)" 
data-id="1571646" data-iid="938688461" title="苹果版小米运动下载" data-stat-act="det" data-stat-pos="list">
<img src="https://img.25pp.com/uploadfile/app/icon/20180328/1522248388516301.jpg@120w_120h" width="70" height="70" alt="苹果版小米运动下载" title="苹果版小米运动下载"></a>

过滤出详情网页地址 https://www.25pp.com/ios/detail_1571646/

4、进入详情界面,通过关键字“下载越狱版”找到红框所在的网页源码

<a href="javascript:void(0);" class="btn-install-x" apptype="app" data-id="1571646" data-iid="938688461" appname="小米运动" appversion="3.3.1"
appdownurl="aHR0cDovL3IxMS4yNXBwLmNvbS9zb2Z0LzIwMTgvMDMvMjgvMjAxODAzMjhfMTI5NjhfMjE4OTgwMDUwOTgzLmlwYQ==" closetimer="-1" 
onclick="return ppOneKeySetup(this)" data-stat-act="jb" data-stat-pos="install">下载越狱版</a>

5、通过appdownurl及 点击事件处理ppOneKeySetup,找到pp_onekey-d17d98b4.js中的关键代码:

(C = h.href, E = h.getAttribute("appdownurl"), E && E.length > 0 && (C = o.base64decode(o.utf8to16(E)))

可见js只是对传入的appdownurl作了简单的base64解码,转化后得到真实的下载地址:

http://r11.25pp.com/soft/2018/03/28/20180328_12968_218980050983.ipa


为方便获取下载地址,将上面的步骤用Python3实现:

import urllib.request
import urllib.parse
import re
import ssl
import base64

#关闭SSL验证
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
ssl._create_default_https_context = ssl._create_unverified_context
print("Close certificate verify...")

def getSearchResult():
    keyword = input("Input the search key word: ")

    #将中文转换成url编码
    keyword = urllib.parse.quote(keyword)

    searchUrl = "https://www.25pp.com/ios/search_app_0/" + keyword + "/"
    content = getHtmlStringByUrl(searchUrl)
    detailUrl = getSearchDetailUrl(content)
    content = getHtmlStringByUrl(detailUrl)
    downUrl = getAppdownUrlByHtmlContent(content)
    return downUrl


# 根据url 获取网页内容
def getHtmlStringByUrl(url):
    try:
        request = urllib.request.Request(url, headers=headers)
        response = urllib.request.urlopen(request)
        content = response.read().decode('utf-8')  # gbk
        return content

    except urllib.request.URLError as e:
        if hasattr(e, "code"):
            print(e.code)
        if hasattr(e, "reason"):
            print(e.reason)

    return ""

# 根据网页内容获取详情链接
def getSearchDetailUrl(content):
    pattern = re.compile('href="https://www.25pp.com/ios/detail_.*?"', re.S) #href = "https://www.25pp.com/ios/detail_3491226/"
    items = re.findall(pattern, content)
    for item in items:
        #print(item)
        values = item.split('"')
        result = values[1]
        print("Detail url: " + result)
        return result

    return ""

# 根据网页内容获取ipa的下载链接
def getAppdownUrlByHtmlContent(content):
    pattern = re.compile('appdownurl=".*?"', re.S)  # appdownurl="aHR0cDovL3IxMS4yNXBwLmNvbS9zb2Z0LzIwMTgvMDEvMDkvMjAxODAxMDlfNjI0NThfMjE1MDYwOTY4Nzc4LmlwYQ=="
    items = re.findall(pattern, content)
    for item in items:
        values = item.split('"')
        result = values[1]
        print("Orgin download url: " + result)

        # Base64Decode
        output = base64.standard_b64decode(result)
        output = output.__str__()
        return output

    return ""


downUrl = getSearchResult()
print("Down url: " + downUrl)

Posts: 15

Participants: 6

Read full topic

iOS 多开检测,反多开检测,反反多开检测

$
0
0

@Magic_Unique wrote:

前言

随着 iOS 逆向门槛越来越低,国内灰产也越来越多。自动抢红包则成为了 iOS 逆向入门级项目,GitHub 上也出现了技术水平参差不齐的微信插件的 Repo。而这些插件,往往都是基于一个多开微信。

写这篇文章最主要的目的还是提醒部分厂商,加强 App 安全防护意识,减少灰产的可乘之机。

如何多开

在 iOS 设备上安装多个同一个 App 的方式只有一种,修改 Info.plist 中的 CFBundleIdentifier

在整个 iOS 生态中,Bundle Identifier 是作为一个 App 的唯一标识符存在。

对于拥有相同的 Bundle Identifier 的 App,无论 Binary 和资源文件都多大的差异,iOS 都会将它们视为同一个 App。

对于拥有不同的 Bundle Identifier 的 App,也无论 Binary 与和资源文件是否一致,iOS 会将它们视为不同 App。

Info.plist 文件,是整个 App 的信息、配置、权限的信息整合文件,其在 App 中起到至关重要的作用。

为了防止 Info.plist 被恶意篡改,iOS 提供一种数字签名技术。通过该技术,计算出 Info.plist 文件的 Hash 值,加密后存入到签名文件中。在安装时与安装后,可通过该签名文件存的 Hash 值进行文件签名校验。也因此,App 签名后无法修改 Info.plist 文件;而即使是已安装 App 的 Info.plist 文件,修改后也会导致 App 闪退。

所以可以得出结论,对于已安装的 App 的 Info.plist 文件的 CFBundleIdentifier 值不会被修改

如何检测多开

通过上述结论,由于 Info.plist 文件的不可修改性质,我们可以在 App 运行时来读取 Info.plist 文件中的值来判断该值是否与出产时候相同,从而判断当前进程是否是一个多开 App。

Foundation.framework 提供了几种获取 App 的 Bundle Identifier 方法,基本如下:

NSBundle.mainBundle.bundleIdentifier;
[NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleIdentifier"];
NSBundle.mainBundle.infoDictionary[@"CFBundleIdentifier"];
[NSDictioanry dictionaryWithContentsOfFile:@"Info.plist"][@"CFBundleIdentifier"]

使用上面几种的任意一种,就可以获取到当前 App 的 Bundle Identifier 值,之后通过 -[NSString isEqualToString:] 方法来判断是否分身。

如何反检测多开

在已经知道如何检测多开的时候,就可以知道如何防止 App 检测多开了。

简单的来说,就是干掉上面的几个方法,强制返回一个原来的值即可。

1. 检测不到多开

具体思路是判断返回值是不是真实的 Bundle Identifier,如果是则返回原来的 Bundle Identifier。这样做的目的防止影响到别的对象以及别的 key 对应的值。

由于 NSDictionary 的特殊性质,通过 key 获取 value 的方式有多种,这几种方法也需要被干掉:

info[@"CFBundleIdentifier"]; // -[NSDictionary objectForKeyedSubscript:]
[info objectForKey:@"CFBundleIdentifier"];
[info valueForKey:@"CFBundleIdentifier"];

在此安利一个我自己写的比较轻量级的非越狱平台 hook 工具——MUHook。仿照 Captain Hook 写的宏集合,同时比 Captain Hook 更加方便快捷,也无需 CydiaSubstrate.dylib。(功能还在完善中)

以微信为例,代码如下:

/*
 * 假设当前分身的 Bundle Identifier 值为 com.unique.xin
 */

#import <Foundation/Foundation.h>

#define CFBundleIdentifier @"CFBundleIdentifier"
#define ORIG_VALUE			@"com.tencent.xin"
#define REAL_VALUE			@"com.unique.xin"

/*
 * 这里干掉了 -[NSBundle bundleIdentifier] 方法
 */
MUHInstanceImplementation(NSBundle, bundleIdentifier, NSString *) {
	NSString *orig = MUHOrig(NSBundle, bundleIdentifier);
	if ([orig isEqualToString:REAL_VALUE]) {
		orig = ORIG_VALUE;
	}
	return orig;
}

/*
 * 这里干掉了 -[NSBundle objectForInfoDictionaryKey:] 方法
 *
 * MUHInstanceImplementation 注释:定义一个 Hook 的实现体
 *		第一个参数是要 Hook 的类
 *	 	第二个参数是自己取的方法名,会在下面的 MUHMain 和 MUHOrig 用到,与实际方法名可以不一致,但是要保证唯一性
 *	  	第三个参数是返回值类型
 *	   	第四个参数以及之后的参数是 Hook 方法的实际参数表。
 *
 * 如果你要 Hook 一个 Class Method 比如 +[UIImage imageNamed]
 * 请使用 MUHClassImplementation,具体用法同上
 */
MUHInstanceImplementation(NSBundle, objForInfoKey, id, NSString *key) {
	/*
	 * MUHOrig 注释:调用原方法
	 * 		第一个参数是原方法的类
	 * 	 	第二个参数是自己取的方法名
	 * 	  	第三个参数以及之后的参数是传入的实际参数
	 */
	NSString *orig = MUHOrig(NSBundle, objForInfoKey, key);
	if ([key isKindOfClass:[NSString class]]) {
		if ([key isEqualToString:CFBundleIdentifier]) {
            if ([orig isEqualToString:REAL_VALUE]) {
            	orig = ORIG_VALUE;
            }
        }
    }
	return orig;
}

MUHInstanceImplementation(NSBundle, infoDictionary, NSDictionary *) {
	NSMutableDictionary *info = [MUHOrig(NSBundle, infoDictionary) mutableCopy];
	if (self == NSBundle.mainBundle) {
		info[CFBundleIdentifier] = REAL_VALUE;
	}
	return [info copy];
}

MUHInstanceImplementation(NSDictionary, objectForKey, id, NSString *key) {
	id orig = MUHOrig(NSDictionary, objectForKey, key);
	if ([key isKindOfClass:[NSString class]]) {
		if ([key isEqualToString:CFBundleIdentifier]) {
			if ([orig isEqualToString:REAL_VALUE]) {
				id = ORIG_VALUE;
			}
		}
	}
	return orig
}

MUHInstanceImplementation(NSDictionary, valueForKey, id, NSString *key) {
	id orig = MUHOrig(NSDictionary, valueForKey, key);
	if ([key isKindOfClass:[NSString class]]) {
		if ([key isEqualToString:CFBundleIdentifier]) {
			if ([orig isEqualToString:REAL_VALUE]) {
				id = ORIG_VALUE;
			}
		}
	}
	return orig
}

MUHInstanceImplementation(NSDictionary, objectForKeyedSubscript, id, NSString *key) {
	id orig = MUHOrig(NSDictionary, objectForKeyedSubscript, key);
	if ([key isKindOfClass:[NSString class]]) {
		if ([key isEqualToString:CFBundleIdentifier]) {
			if ([orig isEqualToString:REAL_VALUE]) {
				id = ORIG_VALUE;
			}
		}
	}
	return orig
}


/*
 *	MUHMain 注释:定义一个拥有 constructor 属性的函数。
 *	当 dyld 加载此二进制文件到内存中的时候会自动调用此函数,完成运行前的 hook 工作
 */
void MUHMain() {
	/*
	 *	MUHHookInstanceMessage 注释:让上面定义的某个 Instance Hook 实现体生效
	 * 		第一个参数是上面实现体的类
	 * 	 	第二个参数是上面实现体自己取的方法名
	 * 	  	第三个参数是对应的 SEL
	 *
	 *	如果要让一个 Class Hook 生效,可以使用
	 * 		MUHHookClassMessage()
	 * 	 	使用方式同上
	 */
    MUHHookInstanceMessage(NSBundle, bundleIdentifier, bundleIdentifier);
	MUHHookInstanceMessage(NSBundle, infoDictionary, infoDictionary);
    MUHHookInstanceMessage(NSBundle, objectForInfoDictionaryKey, objectForInfoDictionaryKey:);
	MUHHookInstanceMessage(NSDictionary, objectForKey, objectForKey:);
	MUHHookInstanceMessage(NSDictionary, valueForKey, valueForKey:);
	MUHHookInstanceMessage(NSDictionary, objectForKeyedSubscript, objectForKeyedSubscript:);
}

同时,微信也可以通过 IDFA、DeviceName 来判断是否是同一台设备登录不同的微信,所以以下两个方法也要被干掉:

#import <UIKit/UIKit.h>
#import <AdSupport/AdSupport.h>

MUHInstanceImplementation(ASIdentifierManager, advertisingIdentifier, NSUUID *) {
	NSString *idfa = [userDefaults stringForKey:@"com.unique.idfa"];
	if(!idfa)
	{
		idfa = [NSUUID UUID].UUIDString;
		[userDefaults setObject:idfa forKey:@"com.unique.idfa"];
		[userDefaults synchronize];
	}
	NSUUID *udid = [[NSUUID alloc] initWithUUIDString:idfa];
	return udid;
}

/*
 *	强制返回一个不容易被怀疑的大众型名字
 */
MUHInstanceImplementation(UIDevice, name, NSString *) {
	return @"iPhone";
}

void MUHMain() {
	MUHHookInstanceMessage(ASIdentifierManager, advertisingIdentifier, advertisingIdentifier);
	MUHHookInstanceMessage(UIDevice, name, name);
}

2. 只让微信检测不到多开

写到这里,基本上对 NSBundle 的调用都被干掉了。但是这里面存在着一个潜在的问题。

在 App 运行时,除微信主二进制文件外,随着被加载到内存中的二进制还有:微信内置 Framework,微信所用到的系统 Framework,插件自身 dylib。

而我们并不能保证系统 Framework 是否会调用、何时会调用 NSBundle 相关方法。如果系统 Framework 调用了相关方法,得到了假的 Bundle ID,则有可能出现无法预计的问题,甚至是出现了也找不到问题的bug。所以我们必须保证如果是系统调用的方法,要返回真实的 Bundle ID。

同时,如果插件自身想要获取 Bundle ID,也应该要返回一个真实的 Bundle ID。

于是提出需求:如果是微信调用的方法,返回假值,否则返回真值。

我们可以通过 dyld 的 dladdr() 函数配合当前调用栈地址来判断调用者来自哪个二进制文件。具体代码如下:

#include <dlfcn.h>

BOOL MUIsCallFromWeChat() {
	NSArray *address = [NSThread callStackReturnAddresses];
	Dl_info info = {0};
	if(dladdr((void *)[address[2] longLongValue], &info) == 0) return NO;
	NSString *path = [NSString stringWithUTF8String:info.dli_fname];
	if ([path hasPrefix:NSBundle.mainBundle.bundlePath]) {
		//	二进制来自 ipa 包内
		if ([path.lastPathComponent isEqualToString:@"MyPlugin.dylib"]) {
			//	二进制是插件本身
    		return NO;
    	} else {
    		//	二进制是微信
    		return YES;
    	}
	} else {
		//	二进制是系统或者越狱插件
    	return NO;
	}
}

// 以 -[NSBundle bundleIdentifier] 为例,其余自己扩展
MUHInstanceImplementation(NSBundle, bundleIdentifier, NSString *) {
	NSString *orig = MUHOrig(NSBundle, bundleIdentifier);
	if (MUIsCallFromWeChat() == NO) {
		return orig;
	}
	if ([orig isEqualToString:REAL_VALUE]) {
		orig = ORIG_VALUE;
	}
	return orig;
}

如何反反检测多开

1. 使用 CoreFoundation 检测(不一定靠谱)

Foundation 相对应,CoreFoundation 也有一套关于 CFBundleRef 操作的 C 语言 API。大部分的厂商一般都使用 NSBundle 来获取 Bundle ID。所以可以通过这一套 C 语言 API 来获取 CFBundleIdentifier 继而判断是否多开。

但由于这一套毕竟是苹果公开的 API,插件依旧可以使用 fishhook 来完成 C 函数的 hook。所以个人认为这种方法并不是完全靠谱

2. 使用 Appex、Watch 检测(不一定靠谱)

由于 iOS 规定,所有 PlugIn 和 Watch App 的 Bundle ID 的前缀必须和 mainBundle 的 Bundle ID 一致,所以如果多开的 App 改了 mainBundle 的 Bundle ID,同时也要修改 PlugIns 和 Watch App 的 Bundle ID。

也因此可以通过获取 PlugIn 和 Watch App 的 Bundle ID 来判断 mainBundle 是否被修改过。

但是一般的微信插件都会删除 PlugIns 和 Watch 文件夹,所以这个方法也不是很靠谱。

3. 使用 FILE + 加密 + 混淆 检测(我认为靠谱)

是否多开最终还是判断 Info.plist 文件中的 CFBundleIdentifier 值是否被修改。上述获取该值的方法都是通过大家都知道的苹果提供的 API 来获取。实际上可以自己实现一套获取方法,绕过耳熟能详的 API,再进行一些字符串加密以及代码混淆即可实现更加安全的多开检测方式。


#define EncryptStr(str) str // 加密算法
#define DecryptStr(str) str // 解密算法

static BOOL ZheBuShiYiGeDuoKaiJianCe() 
    int result = 1;
    NSString *filePath = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:DecryptStr(@"此处写死加密的Info.plist"];
    NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:filePath];
    filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp.txt"];
    [dictionary writeToFile:filePath atomically:YES];
    // 不知道为什么 Demo 中的 Info.plist 不能直接 UTF8 来读取,必须经过字典 read write 后才能读。
    // 这里可能会有漏洞,可以直接 hook -[NSDictionary dictionaryWithContentsOfFile:]
    // 但是为什么直接读会出来乱码我也不太清楚,有大神知道的话可以指导我一下。
    
    unsigned long long size = [NSFileManager.defaultManager attributesOfItemAtPath:filePath error:nil].fileSize;
    FILE *file = fopen(filePath.UTF8String, "r");
    if (file != NULL) {
        char *content = malloc(size + 1);memset(content, 0, size + 1);
        fread(content, size, sizeof(char), file);
        char *beginIndex = strstr(content, DecryptStr("此处写死加密的CFBundleIdentifier"));
        beginIndex = strstr(beginIndex, DecryptStr("此处写死加密的<string>")) + 8;
        if (beginIndex != NULL) {
            char *endIndex = strstr(beginIndex, DecryptStr("此处写死加密的</string>"));
            if (endIndex != NULL) {
                unsigned long bidsize = endIndex - beginIndex;
                char *endbid = malloc(bidsize + 1);memset(content, 0, bidsize + 1);
                strlcpy(endbid, beginIndex, bidsize + 1);
                result = strcmp(EncryptStr(endbid),"此处写死加密的BundleID");
                //	此处直接比较加密的 BundleID,因为 strcmp 也可能会被 hook。
                free(endbid);
            }
        }
        free(content);
        beginIndex = content = NULL;
        fclose(file);
    }
    printf("%d\n", result);
    return result == 0;
}

Posts: 19

Participants: 13

Read full topic

Frida-ios-dump一键砸壳菜鸡版

$
0
0

@nerosir wrote:

写在前面:

此教极其适合像我这样的菜鸡食用 常人移步庆哥官方: 一条命令完成砸壳 github:frida-ios-dump

ios端配置:

  • 打开cydia 添加源:https://build.frida.re

  • 打开刚刚添加的源 安装 Frida

  • 安装完成!检查是否工作可以可在手机终端运行 frida-ps -U 查看

mac端配置:

  • 安装Homebrew

  • 安装python: brew install python

  • 安装wget: brew install wget

  • 安装pip:

    • wget https://bootstrap.pypa.io/get-pip.py

    • sudo python get-pip.py

  • 安装usbmuxd:brew install usbmuxd

  • 清理残留: rm ~/get-pip.py

Ps: 使用brew install xxx如果一直卡在Updating Homebrew… 可以control + z结束当前进程 再新开一个终端安装 此时可以跳过更新

安装frida for mac:

  • 终端执行:

    • sudo pip install frida
  • 假如报以下错误:

    -Uninstalling a distutils installed project (six) has been deprecated and will be removed in a future version. This is due to the fact that uninstalling a distutils project will only partially uninstall the project.

  • 使用以下命令安装:

    • sudo pip install frida –upgrade –ignore-installed six

配置frida-ios-dump环境:

  • 从Github下载工程:
    sudo mkdir /opt/dump && cd /opt/dump && sudo git clone https://github.com/AloneMonkey/frida-ios-dump
  • 安装依赖:
    sudo pip install -r /opt/dump/frida-ios-dump/requirements.txt --upgrade
  • 修改dump.py参数:
    vim /opt/dump/frida-ios-dump/dump.py
    找到如下几行(32~35):
  User = 'root'
      Password = 'alpine'
      Host = 'localhost'
      Port = 2222
   按需修改 如把Password 改成自己的
   ps:如果不习惯vim 用访达打开/opt/dump/frida-ios-dump/dump.py手动编辑。
  • 设置别名:

    • 在终端输入:
      vim ~/.bash_profile
    • 在末尾新增下面一段:
      alias dump.py="/opt/dump/frida-ios-dump/dump.py"

    注意:以上的/opt/dump 可以按需更改 。

  • 使别名生效:
    source ~/.bash_profile

Enjoying and using it !

  • 打开终端 设置端口转发:
    iproxy 2222 22
  • command + n 新建终端执行一键砸壳(QQ):
    dump.py QQ

好了 现在在终端ls 查看刚刚的成果吧~

Posts: 9

Participants: 4

Read full topic

解除Yummy FTP的使用限制(第一次成功的逆向经历)

$
0
0

@Peterpan0927 wrote:

逆向分析(直接markdown粘过来的)

博客链接

因为30天使用过期了,所以这个玩意不让我们使用了,虽然网上有序列号,但是这样的方式并不能让我愉♂悦,所以我们正好试试前两天学的Mac逆向。
首先我们用Hopper来搞一手,但是这个过程中发现逆向出来的函数名中似乎没有我们需要的呀:

这一步有点卡手,然后看看其他的,发现都是sub_xxxx什么的,而且还巨多,看完眼睛都瞎掉,想了想之后决定用字符串查找,因为这个是序列号验证的模式,我们就通过序列号英文来查找:

然后查找这个引用的关系,到这里似乎出现了突破口,继续向上寻找引用,然后发现了有四个调用:

我们首先看看第一个,太长了,跳出来看看第二个,发现离调用的头部很近:

屏幕快照 2018-04-21 下午11.32.10.png

从判断的逻辑关系可以得出,一开始对于日期或者其他的什么进行了一次校验,校验失败之后走左边的路线,也就是你的App认证为是过期的,我们发现开头的右边那条线就直接返回了,所以我们在这里修改判断条件让函数直接返回或者走右边的路线,原来的代码是:

mov	al, byte [esi-0x72b21+0x2f1114]                     ; 0x2f1114
and	al, 0x0
jne loc_72d49

那么我们修改判断为一定不为0的即可:

or al, 0x01
jne	loc_72d49

修改好之后非常开心的打开了应用,然而现实给了我沉重的一巴掌,还是不OK,此时我的心中似乎奔过了千万头草泥马,想到还有几个引用,于是乎又转头回去看了:

除了逻辑变了一下,其他的也没改什么,所以照样改就好了,第四个是一个子调用,应该不需要改,修改完之后重新导出,然后替换原有的可执行文件,重新打开App:

nice,那个烦人的玩意没了,开始愉悦的使用吧~分享到此结束。

补一个github的链接吧:Yummy_FTP_crack

Posts: 1

Participants: 1

Read full topic

针对某移动办公App的网络安全分析

$
0
0

@iblue wrote:

简书链接

0 前言

逆向的目标之一是评定App安全等级,找到存在的安全隐患,并以有效手段进行反破解。之前实现任意位置打卡的几种方法,都是修改实际的GPS位置信息,并没有从网络、代码层面进行深入的分析。乘着假期,将网络请求的流程梳理出来,看看能否从网络层,获取相关的请求、参数及加密方法。

1 网络流程分析

Charels抓包,网络请求走的都是HTTPS,没有可参考的价值。

回归代码,结合头文件,网络请求用的AFNetworking。既然如此,直接Hook住AFHTTPSessionManager类中最常用的POST请求方法 - POST:parameters:progress:success:failure:,将url、参数打印出来:

//Hook的class
CHDeclareClass(AFHTTPSessionManager);

// - (id)POST:(id)arg1 parameters:(id)arg2 progress:(id)arg3 success:(id)arg4 failure:(id)arg5
CHMethod(5, void, AFHTTPSessionManager, POST, id, arg1, parameters, id, arg2, progress, id, arg3, success, id, arg4, failure, id, arg5)
{
    NSLog(@"%s::%@\n %@", __func__, arg1, arg2);
    return CHSuper(5, AFHTTPSessionManager, POST, arg1, parameters,arg2, progress, arg3, success, arg4, failure, arg5);
}

__attribute__((constructor)) static void entry()
{
    CHLoadLateClass(AFHTTPSessionManager);
    CHClassHook(5, AFHTTPSessionManager, POST, parameters, progress, success, failure);
}

1.1 登录请求分析

接下来,打开App进行登录,看看发送了哪些内容。打印信息如下:

https://app.xxxxtech.com/GetAndroidDataService.svc/LoginVerify
{
    jsonData = 0e10de0e07c7f127d43b326cb30ceac2566b2dfabd8facbd431e7e31708ffaf8bb310fd0abd77570e10fa3a61c36aac11261b59889c0aa606b64cbde9f43d6814503211a0e0a2a55552646c393e8e9fa72ab0703685cf19308f9a16522d58e405874fa9956a40a99313b01cdb6d3eab702019d3a2eedd819b0a2fab032a8fff52258133eb42b828a80fd4d15df199f4ab83cd4a2df67a5fac0906694ede697abde35c29587de9ca894036f785f0c2d94164a363cb6e92c4a0072d21e1f8b9b0843b1c2af7c90d3c89117318ef8e1c86fa32f99ec9182d57b5191a8e10ad0d80e6ee8de8cb0a3398be971e5e261d77b93858ed103b91af91570a832a95f42ac658737739cd60f46920d21b1154158a0ecae8f8cb7dbcb2ac6c3894e51a290c033715f11bb9e3ce18cb48429f4e5ba69be4e7d17861b355d96ce64d8ac472fafcf04ed891f29a98fa276086fc4d57d10ca;
}

得到请求url 【https://app.xxxxtech.com/GetAndroidDataService.svc/LoginVerify

参数是字典格式NSDictionary<NSString *, NSString *>的数据,key值为 jsonData,value值是一串很长的字符串,显然是加密过的。单从value的字符串形式来看,是由十六进制格式的字符组成的。可能是某种加密方式与MD5的结合,但如果进行了MD5,服务端是无法解析数据的;App和服务采用对称加密的方法最常用,AES/DES等,这里有可能先进行了对称加密,再将每个字符进行转换。如果能找到原始的请求参数、加密方法,整个网络请求层应该都可以破解掉了。

1.2 请求回溯

要找到原始的参数,就需要找到哪个类调用了POST方法。然后往前一步一步回溯,将每个步骤串起来,就可以倒推网络请求的流程。

借助Hopper,搜索字符串POST:parameters:progress:success:failure:,发现ServiceUtil中的几个方法使用了:

queryService:
queryListService:
updateArrayService:
updateService:

登录一般只是检验密码,hook住queryService,打印请求参数类型,确实有相应的输出:

UserInfoModel

进一步分析,queryService并没有被其他类直接调用,而是通过在RegisteredServicesMonitor函数内,注册xxxx_MOBILE_ServiceQuery的通知方法被动调用的。

void -[ServiceUtil RegisteredServicesMonitor](void * self, void * _cmd) {
   ...
    r0 = [NSNotificationCenter defaultCenter];
    r0 = [r0 retain];
    stack[2032] = r0;
    _objc_msgSend(r0, *r0, self, @selector(queryService:), @"xxxx_MOBILE_ServiceQuery", 0x0);
    [stack[2032] release];
   ...
    return;
}

搜索通知xxxx_MOBILE_ServiceQuery,再根据[LoginViewController login]伪代码,将整个代码调用顺序串起来,最终发现参数通过DESUtil类进行加密:

2 加密分析

整个请求的流程清晰了,回过头来看UserInfoModel是如何转化为成十六进制字符串的。

queryService的参数是在ServiceUtil类的函数dictionaryFormQueryData被转化了,具体过程分为两步:

  • 模型转字典

先用UserInfoModel对象的convertToUpdateDictionary 方法(其中敏感信息FItemNumber、FPassword作了隐藏处理),将对象转化成字典结构

{
    FAppVersion = "v4.0.2.Basics (51)";
    FItemNumber = *****;
    FMobileType = IOS;
    FModuleId = 2990;
    FOSVersion = "10.3.2";
    FPassword = "******";
    FVerSion = 51;
}
  • 字典转查询条件、并加密
    ObjectForDesAndReturnData函数内,将前面步骤的字典转化成加密的字符串,并组成新的字典,作为请求的参数
{
	jsonData = 0e10de0e07c7f127d43b326cb30ceac25...
	}

2.1 DES加密

进入最终的加密函数 [DESUtil doCipher:key:context:],查看伪代码:

void * +[DESUtil doCipher:key:context:](void * self, void * _cmd, void * arg2, void * arg3, unsigned int ret_addr) {
    r7 = (sp - 0x14) + 0xc;
    sp = sp - 0x1a4;
    var_20 = *___stack_chk_guard;
    objc_storeStrong(r7 - 0x2c, arg2);
    objc_storeStrong(r7 - 0x30, arg3);
    var_34 = ret_addr;
    var_3C = 0x0;
    if (var_34 == 0x1) {
            ...
    }
    else {
            r0 = [0x0 dataUsingEncoding:0x4, r0];
            r7 = r7;
            r0 = [r0 retain];
            var_C8 = r0;
            r1 = var_3C;
            var_3C = [r0 mutableCopy];
            [r1 release];
            [var_C8 release];
    }
    r0 = [0x0 dataUsingEncoding:0x4, r0];
    r7 = r7;
    r0 = [r0 retain];
    var_60 = [r0 mutableCopy];
    [r0 release];
    [var_60 setLength:0x8, r0];
    var_68 = [0x0 length] + 0x8 & 0xfffffff8;
    var_64 = malloc(var_68);
    __memset_chk();
    var_F0 = [objc_retainAutorelease(var_60) bytes];
    var_F8 = [var_60 length];
    *(r7 - 0x100) = [objc_retainAutorelease(var_60) bytes];
    objc_retainAutorelease(var_3C);
    *((r7 - 0x100) + 0xfffffffffffffffc) = _objc_msgSend;
    *((r7 - 0x100) + 0xfffffffffffffff8) = (*((r7 - 0x100) + 0xfffffffffffffffc))();
    *((r7 - 0x100) + 0xfffffffffffffff4) = _objc_msgSend;
    r2 = *((r7 - 0x100) + 0xfffffffffffffff4);
    *((r7 - 0x100) + 0xfffffffffffffff0) = (r2)(var_3C, @selector(length), r2, var_3C);
    *(var_34 + 0xffffffffffffffec) = 0x1;
    r12 = *(var_34 + 0xffffffffffffffec);
    *(var_34 + 0xffffffffffffffe8) = r7 - 0x6c;
    *(var_34 + 0xffffffffffffffe4) = var_64;
    var_70 = 0x0;
    *((r7 - 0x100) + 0xffffffffffffffe0) = CCCrypt(var_34, 0x1, r12, var_F0, var_F8, *(r7 - 0x100), *((r7 - 0x100) + 0xfffffffffffffff8), *((r7 - 0x100) + 0xfffffffffffffff0), *((r7 - 0x100) + 0xffffffffffffffe4), var_68, *((r7 - 0x100) + 0xffffffffffffffe8));
    if (var_34 == 0x1) {
           ...
    }
    else {
            *((r7 - 0x100) + 0xffffffffffffffc8) = _objc_msgSend;
            r0 = (*((r7 - 0x100) + 0xffffffffffffffc8))(@class(NSData), @selector(dataWithBytes:length:), var_64, 0x0);
            r7 = r7;
            var_74 = [r0 retain];
            free(var_64);
            *((r7 - 0x100) + 0xffffffffffffffc4) = _objc_msgSend;
            r2 = *((r7 - 0x100) + 0xffffffffffffffc4);
            (r2)(@class(NSMutableString), @selector(alloc), r2);
            *((r7 - 0x100) + 0xffffffffffffffc0) = @"";
            r3 = *((r7 - 0x100) + 0xffffffffffffffc0);
            *((r7 - 0x100) + 0xffffffffffffffbc) = _objc_msgSend;
            var_78 = (*((r7 - 0x100) + 0xffffffffffffffbc))();
            objc_retainAutorelease(var_74);
            *((r7 - 0x100) + 0xffffffffffffffb8) = _objc_msgSend;
            var_7C = (*((r7 - 0x100) + 0xffffffffffffffb8))();
            var_80 = 0x0;
            do {
                    *((r7 - 0x100) + 0xffffffffffffffb4) = _objc_msgSend;
                    r3 = *((r7 - 0x100) + 0xffffffffffffffb4);
                    *((r7 - 0x100) + 0xffffffffffffffb0) = var_80;
                    if (*((r7 - 0x100) + 0xffffffffffffffb0) >= (r3)(var_74, @selector(length), var_80, r3)) {
                        break;
                    }
                    s0 = *@"%x";
                    *((r7 - 0x100) + 0xffffffffffffffac) = @"%x";
                    *((r7 - 0x100) + 0xffffffffffffffa8) = _objc_msgSend;
                    r0 = (*((r7 - 0x100) + 0xffffffffffffffa8))(@class(NSString), @selector(stringWithFormat:), *((r7 - 0x100) + 0xffffffffffffffac), s0 & 0xff);
                    r7 = r7;
                    var_84 = [r0 retain];
                    *((r7 - 0x100) + 0xffffffffffffffa4) = _objc_msgSend;
                    r2 = *((r7 - 0x100) + 0xffffffffffffffa4);
                    if ((r2)(var_84, @selector(length), r2) == 0x1) {
                            r2 = *(("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" | 0x2c0000) + 0x4bd9c);
                            *((r7 - 0x100) + 0xffffffffffffffa0) = @"0%@";
                            *((r7 - 0x100) + 0xffffffffffffff9c) = _objc_msgSend;
                            r1 = r2;
                            r2 = *((r7 - 0x100) + 0xffffffffffffffa0);
                            r12 = *((r7 - 0x100) + 0xffffffffffffff9c);
                            *((r7 - 0x100) + 0xffffffffffffff98) = var_78;
                            r0 = (r12)(@class(NSString), r1, r2, var_84);
                            r0 = [r0 retain];
                            r3 = *((r7 - 0x100) + 0xffffffffffffff98);
                            *((r7 - 0x100) + 0xffffffffffffff94) = r0;
                            r0 = r3;
                            *((r7 - 0x100) + 0xffffffffffffff90) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff94);
                            r3 = *((r7 - 0x100) + 0xffffffffffffff90);
                            r0 = (r3)(r0, @selector(stringByAppendingString:), r2, r3);
                            r7 = r7;
                            r0 = [r0 retain];
                            *((r7 - 0x100) + 0xffffffffffffff8c) = r0;
                            *((r7 - 0x100) + 0xffffffffffffff88) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff88);
                            r1 = var_78;
                            var_78 = (r2)(r0, @selector(mutableCopy), r2, r0);
                            [r1 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff8c);
                            [r0 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff94);
                            [r0 release];
                    }
                    else {
                            *((r7 - 0x100) + 0xffffffffffffff84) = @"%@";
                            *((r7 - 0x100) + 0xffffffffffffff80) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff84);
                            r12 = *((r7 - 0x100) + 0xffffffffffffff80);
                            *((r7 - 0x100) + 0xffffffffffffff7c) = var_78;
                            r0 = (r12)(@class(NSString), @selector(stringWithFormat:), r2, var_84);
                            r0 = [r0 retain];
                            r3 = *((r7 - 0x100) + 0xffffffffffffff7c);
                            *((r7 - 0x100) + 0xffffffffffffff78) = r0;
                            r0 = r3;
                            *((r7 - 0x100) + 0xffffffffffffff74) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff78);
                            r3 = *((r7 - 0x100) + 0xffffffffffffff74);
                            r0 = (r3)(r0, @selector(stringByAppendingString:), r2, r3);
                            r7 = r7;
                            r0 = [r0 retain];
                            *((r7 - 0x100) + 0xffffffffffffff70) = r0;
                            *((r7 - 0x100) + 0xffffffffffffff6c) = _objc_msgSend;
                            r2 = *((r7 - 0x100) + 0xffffffffffffff6c);
                            r1 = var_78;
                            var_78 = (r2)(r0, @selector(mutableCopy), r2, r0);
                            [r1 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff70);
                            [r0 release];
                            r0 = *((r7 - 0x100) + 0xffffffffffffff78);
                            [r0 release];
                    }
                    objc_storeStrong(r7 - 0x84, 0x0);
                    var_80 = var_80 + 0x1;
            } while (true);
            objc_storeStrong(r7 - 0x70, var_78);
            objc_storeStrong(r7 - 0x78, 0x0);
            objc_storeStrong(r7 - 0x74, 0x0);
    }
    *((r7 - 0x100) + 0xffffffffffffff68) = [var_70 retain];
    objc_storeStrong(r7 - 0x70, 0x0);
    objc_storeStrong(r7 - 0x60, 0x0);
    objc_storeStrong(r7 - 0x3c, 0x0);
    objc_storeStrong(r7 - 0x30, 0x0);
    objc_storeStrong(r7 - 0x2c, 0x0);
    r0 = *((r7 - 0x100) + 0xffffffffffffff68);
    r0 = [r0 autorelease];
    r1 = *___stack_chk_guard;
    *((r7 - 0x100) + 0xffffffffffffff64) = r0;
    if (r1 == var_20) {
            r0 = *((r7 - 0x100) + 0xffffffffffffff64);
    }
    else {
            r0 = __stack_chk_fail();
    }
    return r0;
}

代码很长,看起来很头疼,其实也没必要一行一行去看懂。利用CCCrypt的各个参数进行对比分析,大概的加密逻辑也是很容易推测出来的。

CCCryptorStatus CCCrypt(
    CCOperation op,         /* kCCEncrypt, etc. */
    CCAlgorithm alg,        /* kCCAlgorithmAES128, etc. */
    CCOptions options,      /* kCCOptionPKCS7Padding, etc. */
    const void *key,
    size_t keyLength,
    const void *iv,         /* optional initialization vector */
    const void *dataIn,     /* optional per op and alg */
    size_t dataInLength,
    void *dataOut,          /* data RETURNED here */
    size_t dataOutAvailable,
    size_t *dataOutMoved)
  • op,操作类型,由变量var_34控制,即doCipher:key:context最后一个参数,为0时,进行加密操作
    enum { kCCEncrypt = 0, kCCDecrypt, };

  • alg,加密算法,伪代码为0x1,即kCCAlgorithmDES,为DES加密方式

enum {
    kCCAlgorithmAES128 = 0,
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,       
    kCCAlgorithmCAST,       
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,   
    kCCAlgorithmBlowfish    
};
typedef uint32_t CCAlgorithm;
  • options,代码对应变量r12 = 0x1,对应枚举为kCCOptionPKCS7Padding
*(var_34 + 0xffffffffffffffec) = 0x1;
r12 = *(var_34 + 0xffffffffffffffec)
  • key,密钥,对应到doCipher:key:context的第2个参数,需要转化成char *型,在[DESUtil encode]中,可以找到相应的密钥02****5a(8位,属于敏感信息,中间4位隐藏处理),伪代码 对应 var_F0的值:
[[DESUtil doCipher:r0 key:@"02****5a" context:stack[2033], stack[2034], stack[2035]] retain]


var_F0 = [objc_retainAutorelease(var_60) bytes]
  • keyLength,密钥长度,伪代码var_F8 = [var_60 length]
  • iv,加密向量,代码对应(r7 - 0x100)的值,与var_F0一致
*(r7 - 0x100) = [objc_retainAutorelease(var_60) bytes];
  • dataIn,需要加密的内容,即doCipher:key:context的1个参数
  • dataInLength,加密内容的长度
  • dataOut,加密后的内容
  • dataOutAvailable,加密后内容长度
var_68 = [0x0 length] + 0x8 & 0xfffffff8;
  • dataOutMoved,输出值,不用关心

至此,明确了函数使用DES加密方式,并且加密向量与密钥相同,继续住下分析。

2.2 DES结果处理

  • 先将加密后的内容转化成NSData值:
r0 = (*((r7 - 0x100) + 0xffffffffffffffc8))(@class(NSData), @selector(dataWithBytes:length:), var_64, 0x0)
  • 按字节读取NSData值,并转化成十六进制格式的字符串r2:
 r3 = *((r7 - 0x100) + 0xffffffffffffffb4);
 *((r7 - 0x100) + 0xffffffffffffffb0) = var_80;
 if (*((r7 - 0x100) + 0xffffffffffffffb0) >= (r3)(var_74, @selector(length), var_80, r3)) {
        break;
   }
  s0 = *@"%x";
  *((r7 - 0x100) + 0xffffffffffffffac) = @"%x";
  *((r7 - 0x100) + 0xffffffffffffffa8) = _objc_msgSend;
  r0 = (*((r7 - 0x100) + 0xffffffffffffffa8))(@class(NSString), @selector(stringWithFormat:), *((r7 - 0x100) + 0xffffffffffffffac), s0 & 0xff);
  r7 = r7;
  var_84 = [r0 retain];
  *((r7 - 0x100) + 0xffffffffffffffa4) = _objc_msgSend;
  r2 = *((r7 - 0x100) + 0xffffffffffffffa4);
  • 判断r2长度,如果长度为1,则在前面补0;这也是为什么在解密代码中,会有高16位、低16位判断的原因
if ((r2)(var_84, @selector(length), r2) == 0x1) {
   r2 = *(("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" | 0x2c0000) + 0x4bd9c);
   *((r7 - 0x100) + 0xffffffffffffffa0) = @"0%@";
   *((r7 - 0x100) + 0xffffffffffffff9c) = _objc_msgSend;
   r1 = r2;
   r2 = *((r7 - 0x100) + 0xffffffffffffffa0);
   r12 = *((r7 - 0x100) + 0xffffffffffffff9c);
  ...
   r0 = *((r7 - 0x100) + 0xffffffffffffff94);
  [r0 release];
}
  • 其实整个循环,就是将NSData的值按字节转化成十六进制格式的字符串:
    比如字符串“a”,经过上述DES加密后,得到的NSData值为<ed531e0b 195ec5b7>,转化成字符串后为 ed531e0b195ec5b7,即为最终加密的结果。而通常AES/DES加密后,会将结果直接转换成Base64。

为了测试方便,在NSString增加了一个DES的类别,实现与伪代码相似的功能,具体见附录。

3 ServiceUtil

网络请求部分都在ServiceUtil里面,设置AFNetWorking参数、HTTP请求头等。

3.1 请求头设置

看伪代码,只是简单设置了AcceptUser-agentAccept-LanguageContent-TypeContent-Length这几个值,并没有做过多的校验,通过WEB模拟发送POST请求,应该也能通过。

void -[ServiceUtil setRequestHead:len:](void * self, void * _cmd, void * arg2, void * arg3) {
    objc_storeStrong((sp - 0x54) + 0x40, arg2);
    objc_storeStrong((sp - 0x54) + 0x3c, arg3);
    [0x0 addValue:@"application/json" forHTTPHeaderField:@"Accept", stack[2027], stack[2028], stack[2029]];
    [0x0 addValue:@"Mozilla/5.0" forHTTPHeaderField:@"User-agent", stack[2027], stack[2028], stack[2029]];
    [0x0 addValue:@"ZH-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4" forHTTPHeaderField:@"Accept-Language", stack[2027], stack[2028], stack[2029]];
    [0x0 addValue:@"application/json" forHTTPHeaderField:@"Content-Type", stack[2027], stack[2028], r2];
    [0x0 addValue:0x0 forHTTPHeaderField:@"Content-Length", r1, @"Content-Length"];
    objc_storeStrong((sp - 0x54) + 0x3c, 0x0);
    objc_storeStrong((sp - 0x54) + 0x40, 0x0);
    return;
}

3.2 HTTP端口

代码内部有一个HTTP的端口:http://app.xxxxtech.com:8080,结合登录url,可以推测,其他的请求都是用特定的功能字符串拼接起来的,用web模拟,发现8080端口也是有效的。
http://app.xxxxtech.com:8080/GetAndroidDataService.svc/xxxx
https://app.xxxxtech.com/GetAndroidDataService.svc/xxxx

void * -[ServiceUtil http](void * self, void * _cmd) {
    stack[2043] = r4;
    r7 = (sp - 0x14) + 0xc;
    *((sp - 0x14) + 0xfffffffffffffffc) = r8;
    sp = (sp - 0x14) + 0xfffffffffffffffc - 0x40;
    stack[2042] = self;
    if (stack[2042]->_http == 0x0) {
            r0 = (*@"%@%@%@")(@class(NSString), @selector(stringWithFormat:), @"%@%@%@", @"http://", @"app.xxxxtech.com:8080", @"/");
            r0 = [r0 retain];
            stack[2034] = r0;
            r0 = (*r0)(@class(NSMutableString), @selector(stringWithFormat:), @"%@%@%@", stack[2034], @"GetAndroidDataService.svc", @"/");

	...
    }
    r0 = stack[2042]->_http;
    r0 = loc_239340(r0, *0x31aa04);
    return r0;
}

4 安全检验

登入App后,试了其他几个请求,发现请求参数、请求头并没有登录返回的FToken信息,难道登录只是进入App的壳子,后续的请求根本不需要检验?找一个接口一试究竟。

查询打卡时间的接口,参数只有工号是可变的:

https://app.xxxxtech.com/GetAndroidDataService.svc/GetCheckStatusData

{
  "FOSVersion" : "10.3.2",
  "FMobileType" : "IOS",
  "FAppVersion" : "v4.0.2.Basics (51)",
  "FModuleId" : "3093",
  "FItemNumber" : "*****"
}

FItemNumber修改为其他的5位数,并对参数加密,得到请求参数(敏感信息隐藏处理):

{"jsonData":"0e10de0e07c7f12758af1e31ffdea690c040bb8ecab59985f118ebfbb6d0500cafaa48ff3194a97be6eb6053ec6c6206db03e151be4b528d78db3becbcb1629fc29d0e049cff1a27e5584d684d8b78e7a925a47801cd1511d6a98c7b1c33debbe446eed7fe7674987e52e6b64bd3f73fb9ebfb7a986b5c16537..."}

使用web发送请求,可以得到正常返回:

    "FID": "",
    "IsSuccess": true,
    "Result": "{\"FStatus\":0,\"FAttendId\":0,\"FCheckInTime\":\"**:**\",\"FCheckOutTime\":\"22:04\"}",
    "ResultCode": 200
}

再试其他接口,也是可以直接通过的,可见除了登录协议外,其他协议都没有做安全性校验。

5 总结

虽然App是内部用的,但有几点还是可以再提高一下的:

增加请求头、安全检验
增加DES加密的复杂度
关闭8080端口
关键几处函数进行代码混淆,反正不需要AppStore审核

附录:加解密代码

#import "NSString+DES.h"
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>

@implementation NSString (DES)

- (NSData *)jm_hexStringConvertToBytesData
{
    //异常字符串
    if (self.length % 2 != 0) {
        return nil;
    }
    
    Byte bytes[1024*3] = {0};
    int bytesIndex = 0;
    
    for(int i = 0; i < [self length]; i++)
    {
        int int_char;  /// 两位16进制数转化后的10进制数
        
        unichar hex_charUpper = [self characterAtIndex:i]; ///两位16进制数中的第一位(高位*16)
        int int_charUpper;
        if(hex_charUpper >= '0' && hex_charUpper <='9') {
            int_charUpper = (hex_charUpper - 48 ) * 16;   // 0 的Ascll - 48
        } else if(hex_charUpper >= 'A' && hex_charUpper <= 'F') {
            int_charUpper = (hex_charUpper - 55 ) * 16; /// A 的Ascll - 65
        } else {
            int_charUpper = (hex_charUpper - 87 ) * 16; // a 的Ascll - 97
        }
        
        i++;
        
        unichar hex_charLower = [self characterAtIndex:i]; ///两位16进制数中的第二位(低位)
        int int_charLower;
        if(hex_charLower >= '0' && hex_charLower <= '9') {
            int_charLower = (hex_charLower - 48); /// 0 的Ascll - 48
        } else if(hex_charUpper >= 'A' && hex_charUpper <='F') {
            int_charLower = (hex_charLower - 55); ///  A 的Ascll - 65
        } else {
            int_charLower = hex_charLower - 87; /// a 的Ascll - 97
        }
        
        int_char = int_charUpper + int_charLower;
        bytes[bytesIndex] = int_char;  ///将转化后的数放入Byte数组里
        bytesIndex++;
    }
    
    NSUInteger dataLength = self.length / 2;
    NSData *data = [[NSData alloc] initWithBytes:bytes length:dataLength];
    return data;
}

- (NSString *)jm_urlDecode {
    NSString *decodedString = [self stringByRemovingPercentEncoding];
    return decodedString;
}

- (NSString *)jm_urlEncode {
    NSString *encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]"]];
    return encodedString;
}

- (NSString *)jm_encryptUseDESByKey:(NSString *)key iv:(NSString *)iv
{
    NSString *ciphertext;
    NSString *encode = [self jm_urlEncode];
//    NSLog(@"%s encode::%@", __func__, encode);
    
    NSData *data = [encode dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger dataLength = data.length;
    NSUInteger bufferLength = 1024;
    unsigned char buffer[bufferLength];
    memset(buffer, 0, sizeof(char));
    
    size_t numBytesEncrypted = 0;

    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding,
                                          [key UTF8String],
                                          kCCKeySizeDES,
                                          [iv UTF8String] , //iv向量
                                          [data bytes],
                                          dataLength,
                                          buffer,
                                          bufferLength,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
        //NSLog(@"%s buffer::%s", __func__, buffer);
        //NSLog(@"%s data::%@", __func__, data);
    
        ciphertext = @"";
        for (int index = 0; index < data.length; index++) {
            char byte;
            [data getBytes:&byte range:NSMakeRange(index, 1)];
            NSString *text = [NSString stringWithFormat:@"%x", byte&0xff];
            
            //不足两位,前面补0
            if([text length] == 1) {
                text = [NSString stringWithFormat:@"0%@", text];
            }
            
            ciphertext = [ciphertext stringByAppendingString:text];
        }
    }
    
    NSLog(@"%s encryptText::%@", __func__, ciphertext);
    return ciphertext;
}

- (NSString *)jm_decryptUseDesByKey:(NSString *)key iv:(NSString *)iv
{
    NSString *decryptText;
    
    NSData *encryptData = [self jm_hexStringConvertToBytesData];
    const char *textBytes = [encryptData bytes];
    
    NSUInteger dataLength = encryptData.length;
    NSUInteger bufferLength = dataLength + 0x8 & 0xfffffff8;
    unsigned char buffer[bufferLength];
    memset(buffer, 0, sizeof(char));

    size_t numBytesEncrypted = 0;
    
    //将encryptText转化为bytes
    CCCryptorStatus decryptStatus = CCCrypt(kCCDecrypt,
                                            kCCAlgorithmDES,
                                            kCCOptionPKCS7Padding,
                                            [key UTF8String],
                                            kCCKeySizeDES,
                                            [iv UTF8String] , //iv向量
                                            textBytes,
                                            dataLength,
                                            buffer,
                                            bufferLength,
                                            &numBytesEncrypted);
    if (decryptStatus == kCCSuccess ) {
        
        NSLog(@"%s buffer::%s", __func__, buffer);
        NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
        
        NSLog(@"%s data::%@", __func__, data);
        
        decryptText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%s decryptText::%@", __func__, decryptText);
        
        decryptText = [decryptText jm_urlDecode];
        NSLog(@"%s decodeUrl::%@", __func__, decryptText);
        
    }
    
    return decryptText;
}
@end

Posts: 12

Participants: 8

Read full topic

根据MachO文件结构获取类方法

$
0
0

@MzPoint wrote:

.工具
1. demo一枚
2. MachoView(建议从git上下载下来自己编译再运行, 我测试从官网下载可执行文件下来的分析完会有闪退的情况)

.分析
首先我们将自己准备好的demo拖入到MachoView中 分析v7的结构(arm64类似, v7地址短 看的简短一点儿)


__object_classname和__object_classname 区段中分别存储的是类名和函数名. 使用MachohView查看的话, 可以暂时不管他.我们从关键的地方看.

__objc_classlist中存储的就是类对应的表信息.
首先我们来分析VCRoot这个类. 可以看到其在文件中对应的偏移为0xC228, 其值为0xCBAC. 这个0xCBAC值对应的位置就是我们要找的详细的类信息.

接着我们根据这个0xCBAC值在__objc_data中寻找.0xCBAC对应的表.


可以看到该结构指向的是一个结构体. 结构体为
typedef struct objc_class{
unsigned long long isa;
unsigned long long wuperclass;
unsigned long long cache;
unsigned long long vtable;
unsigned long long data;
unsigned long long reserved1;
unsigned long long reserved2;
unsigned long long reserved3;
}objc_class;

这里我们可以根据SuperClass获取到该类的父类是UIViewController.

其中Data地址的数据就是我们想要的类信息的数据信息. 我们记录下0xC40C地址.
接着我们去__objc_const段查看类的信息.

这里也是一个类信息的结构体.我来讲讲最重要的几个.
Base Methods: 保存着类中的函数信息对应的结构体.
Base Properties: 保存着类中数据成员变量对应的结构体.

我们先去看下函数的结构体, 这里可以看到函数的结构体对应的位置在0xC298.
同样是在const段, 我们可以找到Method List: 0xC298

Entry Size对应的是每个函数的结构大小.
Count对应的是该类中含有多少个函数.
接下来12字节就是函数的信息. (这里 v8@0:4还未搞清楚, 应该就是参数的问题.可以多写点儿类型进行测试)

这里函数的信息搞明白了 我们来看变量的. 查看Base Properties可以知道其对应的地址在0xC3EC处.

其结构跟类信息的结构是一样的, 一眼就可以看出来.

Posts: 3

Participants: 3

Read full topic


基于LLVM对MSHookIvar之后无效的问题进行分析

$
0
0

@Zhang wrote:

起源是 @laomeiHook实列变量闪退问题 询问为何MSHookIvar之后原值没有更改。原代码:

UIScreen  *uis=%orig;
CGRect cgr = MSHookIvar<CGRect>(uis, "_bounds");
cgr.size.width=1080.0f;
cgr.size.height=1920.0f;
[uis  bounds]=cgr;
NSLog(@"============:0[%f]",cgr.size.height);

CGRect screenBounds = [uis bounds];
NSLog(@"============:1[%f]",screenBounds.size.height); --调试发现,此处值仍然是旧值

然而这篇文章只解释了MSHookIvar的问题,[uis bounds]=cgr;给只读属性赋值我真的很想知道他是怎么编译的

通过在C/C++层的分析我给出了回答如下:

int a=*b形式的Pointer Dereference会复制一份目标地址的值。比如说:

#include <stdio.h>
static void foo(int* A){
  int C=*A;
  C=10;
}
int main(int argc, char const *argv[]) {
  int B=0;
  foo(&B);
  printf("%i\n",B);
  return 0;
}

输出:

λ : >>> ./a.out             
0

看下MSHookIvar的源码

template <typename Type_>
static inline Type_ &MSHookIvar(id self, const char *name) {
    Ivar ivar(class_getInstanceVariable(object_getClass(self), name));
    void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>(self) + ivar_getOffset(ivar));
    return *reinterpret_cast<Type_ *>(pointer);
}

这里第一第二行通过ObjC运行时获得了这个ivar的地址。最后一步reinterpret_cast将地址转换成正确类型的指针后通过指针解引用创建了一份这个值的引用。 CGRect cgr = XXXXXX里左值是CGRect而不是引用(CGRect&) , 所以按照标准这里就创建了一份副本(题外话: 如果是C++对象的话这里是隐式调用Copy Constructor)。正常情况下因为ivar是objc对象时本来就是对指针进行操作,所以复制一份指针的值指向的还是正确的objc对象,但对于结构体和其他原本不是指针类型的ivar这么干就会在副本上进行操作了。

@interface ViewController () {
      int foo;
}
@end
-(void)test{
    self->foo=0;
    NSLog(@"%i",self->foo);
    int lol=MSHookIvar<int>(self,"foo");
    lol=10;
    NSLog(@"%i",self->foo);
    MSHookIvar<int>(self,"foo")=10;
    NSLog(@"%i",self->foo);
}

输出:

2018-05-04 18:55:34.021 [50888:729029] 0
2018-05-04 18:55:34.021 [50888:729029] 0
2018-05-04 18:55:34.021 [50888:729029] 10

问题看起来是解决了。然而,纯粹是为了好玩,让我们掏出LLVM以更低层的方式来看待这个问题。构造源码如下:

#include <stdio.h>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
template <typename Type_>
static inline Type_ &MSHookIvar(id self, const char *name) {
    Ivar ivar(class_getInstanceVariable(object_getClass(self), name));
    void *pointer(ivar == NULL ? NULL : reinterpret_cast<char *>(self) + ivar_getOffset(ivar));
    return *reinterpret_cast<Type_ *>(pointer);
}
int main(int argc, char const *argv[]) {
  NSData* item=[NSData new];
  int lol=MSHookIvar<int>(item,"foo");
  lol=10;
  MSHookIvar<int>(item,"foo")=10;
  return 0;
}

使用clang -S -emit-llvm helloworld.mm 生成如下 LLVM中间表示:

define i32 @main(i32, i8**) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  %6 = alloca %0*, align 8
  %7 = alloca i32, align 4
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  store i8** %1, i8*** %5, align 8
  %8 = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_", align 8
  %9 = load i8*, i8** @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !8
  %10 = bitcast %struct._class_t* %8 to i8*
  %11 = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*)*)(i8* %10, i8* %9)
  %12 = bitcast i8* %11 to %0*
  store %0* %12, %0** %6, align 8
  %13 = load %0*, %0** %6, align 8
  %14 = bitcast %0* %13 to i8*
  %15 = call dereferenceable(4) i32* @_ZL10MSHookIvarIiERT_P11objc_objectPKc(i8* %14, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))
  %16 = load i32, i32* %15, align 4
  store i32 %16, i32* %7, align 4
  store i32 10, i32* %7, align 4
  %17 = load %0*, %0** %6, align 8
  %18 = bitcast %0* %17 to i8*
  %19 = call dereferenceable(4) i32* @_ZL10MSHookIvarIiERT_P11objc_objectPKc(i8* %18, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))
  store i32 10, i32* %19, align 4
  ret i32 0
}

@_ZL10MSHookIvarIiERT_P11objc_objectPKc是MSHookIvar在被C++ Name Mangling之后的函数名称,对应int& MSHookIvar<int>(objc_object*, char const*)
alloca指令在栈上分配内存空间。
这里我们可以看到在第一次调用之后的IR如下:

  %15 = call dereferenceable(4) i32* @_ZL10MSHookIvarIiERT_P11objc_objectPKc(i8* %14, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))
  %16 = load i32, i32* %15, align 4
  store i32 %16, i32* %7, align 4
  store i32 10, i32* %7, align 4

第一条指令%15 = call 省略i32*后省略的i32*告诉我们返回长度32bit的整数的指针。%16的意思是从%15这个地址加载了一个32bit的整数到%16这个值。 后面两条store指令分别将刚加载的值和10这个常数保存到%7, 那么%7 是什么呢? 倒回去看一下函数开头:

 %7 = alloca i32, align 4

说明%7是一个在栈上分配的32bit整数而不是原来的ivar所处的地址。这也就是为什么第一行MSHookIvar之后数值没有改变的原因。

然后我们来看第二次调用:

 %19 = call dereferenceable(4) i32* @_ZL10MSHookIvarIiERT_P11objc_objectPKc(i8* %18, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))
  store i32 10, i32* %19, align 4

这次我们将常数10直接保存到了MSHookIvar返回的指针(即%19) 所指向的内存区域里,所以这次的MSHookIvar成功的修改了对应的值

Posts: 1

Participants: 1

Read full topic

ObjC的符号/名称混淆的坑以及如何手动破坏符号表

$
0
0

@Zhang wrote:

之前是因为这贴 Llvm “防”反向 pass 下的关于符号混淆的讨论以及Hikari开源之后诸多"为什么Hikari不混淆ObjC名称"的问题,一直想写篇相关的东西,今天空了随手糊点字。


Objective-C的方法/类名

其实很简单,因为OC本身语言特性的问题不可能在不限制语言特性的情况下完美无缺。考虑如下代码

@interface AAA:NSObject
-(void)BBB;
@end
@interface CCC:AAA
-(void)BBB;
@end

@implementation DDD
-(void)FFF:(id)arg1{
   [arg1 BBB];
}
@end

假设AAA是系统类,也就说编译器不能修改里面的定义。很显然在混淆FFF:的时候混淆工具无法确定这里的SELBBB是应该修改成CCC里BBB混淆后的的名字还是维持不动。 这个问题实际上有很多种变种,但其他所有变种都跟这个例子某种意义上类似。 比如说常见的通过respondToSelector:来运行时判断类型对分析带来的干扰等等。在这个基础上还有分析时跨界的引用问题,比如上例的AAA CCC DDD都在多个翻译单元(aka源代码文件),又或者是子类复写的父类的SEL等等带来的问题,或者更明确一点,都来自语言本身高度的反射机制和最重要的: 类和SEL的解耦合以及泛型/弱类型。 语言本身的设计问题,无法完美解决。 更不要说这些SEL和类型信息走入LLVM IR之后就无法直接获取到了。类名相对会好做一些,但是SEL是一个巨大的坑。

看到这里读者很可能会问: 那么为什么不做一个黑名单机制,即所有系统的SEL/Class都不混淆呢? 这又回归到了设计哲学上的问题。首先不完全统计iOS11为例系统大约有数万个系统类和十万左右的系统SEL,这对于编译过程中的分析是一个无比巨大的内存和性能开销。除此之外还需要额外维护整张表来处理第三方框架等问题。这个过程是非常非常容易出错的。这就回到开头的另一个原因,懒。

传统的C/C++/Swift符号

我私下里用这玩意儿好几年了,反正也不玩这块了随手写点东西发了。轻拍
传统意义上的符号在MachO的__LINKEDIT段里。我们随意编译一份二进制来做demo。
样本 (8.3 KB)

为了方便,我们首先使用MachOView 来观察这个二进制。将二进制拉到MachOView在Docker里的Logo上:


细心的读者已经看到了。下方已经标记好了两个符号表的位置。这两个符号表的地址实质上是由MachO头部的Load Commands中的LC_SYMTABLC_DYSYMTAB决定的。我们观察一下,这里以LC_SYMTAB为例:

这里的Symbol Table Offset的值是十六进制的0x2068。
看下下面的符号表:

起始地址也是在0x2068,这个结构体的长度则由上文中Symbol Table Offset下方的Number of Symbols决定。这里是8个符号。
往右边仔细看这个符号表。

String Table Index指的就是这个符号的名字在符号表里的字符串表中的索引。比如这里的main是0x16。
下方的Type代表这个符号的类型,这里最重要的是N_UNDF,有这个属性的符号代表外部符号,比如说系统函数等等都属于此列。可能不要动这些符号是一个好主意 :slight_smile:
而上面的比如说main等等都可以随意修改,比如说我们可以将索引修改为0,或者是随意交换索引,又或者是将索引修改为一个大数。为了有效的对抗反汇编器。这里我们将索引全部修改为0。 双击16进制的索引即可修改,完成后Command+S保存即可。
这时我们可以看到反汇编工具已经无法识别符号了:


拉进Hopper:

伪代码中函数名变成了空格:

拓展

这里我们修改了索引,对于普通的hacker来说这已经足够了,但是对于稍微有一定技能的攻击者来说攻击者可能会尝试修复符号表的索引。所以我们可以修改刚才索引指向的地址的内容,而不是索引本身的数值。这类思路我觉得是比在LLVM层去做好很多的。这里说到的方法也可以全部自动化完成。具体的代码就不发了留给读者当作一次手动Parse MachO的练习即可。

下集预告

下一篇文章中我们将着重分析最近小圈子里比较火热的某(号称)虚拟化的iOS插件加固服务,并作出一些分析以及可能的对抗措施。看心情可能也会再写点其他基于LLVM搞事情的教程 :face_with_raised_eyebrow:

EDIT:
鉴于有人问那ObjC怎么手动重命名呢?

Posts: 13

Participants: 3

Read full topic

逆向直播盒子Green-iOS客户端

$
0
0

@yuzhouheike wrote:

写在前面的话本次要使用的几个工具

  • IDA
  • AloneMonkey的MonkeyDev
  • Charles
  • Kali

什么是直播盒子?

  • 单个直播的叫平台,比如斗鱼,熊猫,快手等等
  • 所有的平台放在一个App里面就被称为盒子
  • 现在上面上有很多直播平台(带颜色的)

什么是iOS逆向?

  • 对我来说就是研究别人App里面的东西

为什么逆向这个盒子?

  • 事情的起因是:帮兄弟的帮,兄弟让破解,所以研究下

首先上个图(市面上的直播盒子现在也有很多,图只是其中一种,兄弟发过来的链接)

  • 首页

  • 随便点击一个

您的会员账号已到期,请续费付费是不可能付费的,这辈子都不会付费的。只能靠逆向才能维持生活这样子。。。

然后使用Chareles抓包 得到以下结果

  • 显然作者对数据进行了加密
  • 看到了Host api.appplat6688.com
  • 看到域名那就扫描一下端口吧 !祭出Kali
 nmap api.appplat6688.com 

过了半根烟的时间

  • 出现如下结果:
Starting Nmap 7.60 ( https://nmap.org ) at 2018-05-13 15:52 CST
Nmap scan report for api.appplat6688.com (101.55.26.69)
Host is up (0.64s latency).
Other addresses for api.appplat6688.com (not scanned): 220.95.210.101 101.55.26.70 182.16.53.100 216.118.239.124 52.128.230.228 180.178.48.220 103.90.137.107 216.118.239.132 220.95.210.78 182.16.55.76 180.178.51.212 119.42.148.148
Not shown: 983 closed ports
PORT     STATE    SERVICE
22/tcp   open     ssh
80/tcp   open     http
135/tcp  filtered msrpc
139/tcp  filtered netbios-ssn
443/tcp  open     https
445/tcp  filtered microsoft-ds
593/tcp  filtered http-rpc-epmap
901/tcp  filtered samba-swat
1068/tcp filtered instl_bootc
3128/tcp filtered squid-http
3333/tcp filtered dec-notes
4444/tcp filtered krb524
5800/tcp filtered vnc-http
5900/tcp filtered vnc
6129/tcp filtered unknown
6667/tcp filtered irc
6789/tcp open     ibm-db2-admin

Nmap done: 1 IP address (1 host up) scanned in 59.59 seconds
  • 上面这些端口呢。基本上都是常用的。坦白的说我也搞不定它。所以先不管它(估计有人会问:既然搞不定,为什么要扫呢?因为人外有人,天外有天,我搞不了的不代表正在看文章的你搞不定。帮你扫的!)

因为我们的主题是逆向iOS客户端

  • 如上图所示。此客户端进行了数据加密。一般数据加密的App说明开发者对自己的App做了保护。那么我们就要去看看他是怎么加密的

  • 把件IPA文件(经过3秒的思想斗争。最终决定还是不放链接了,想研究的加我V信:yuzhouheikewll)扔到IDA里面

半根烟时间后

  • 全局搜索 您的会员账号已到期

  • 最终结果

  • 点击X按钮(一路X只到出现汇编代码)

  • 经过一些列分析,定位到如下代码

  • hook KYLMQxqXCDsiemxz:params:success:failure:
%hook GBoxNetManager

-(void)KYLMQxqXCDsiemxz:(id)arg1 params:(id) arg2 success:(id)arg3 failure:(id)arg4 {

%log;

NSLog(@"arg1%@", arg1);

NSLog(@"arg2%@", arg2);

NSLog(@"arg3%@", arg3);

NSLog(@"arg4%@", arg4);

%orig;

}

%end
  • 得到以下结果

  • 那就看看这个函数的返汇编,和F5(IDA常用功能)出来的伪代码

  • 根据我浅显的英文水准,判断出GBoxNetCrypto这个类就是加密类
  • 那么我们就去看看这个类,然后Hook它
  • Hook代码
%hook GBoxNetCrypto

- (id) desEncrypt:(id)arg1 key:(id)arg2 {
	// %log;
	NSLog(@"desEncrypt arg1 = %@ arg2 = %@", arg1, arg2 );
	NSLog(@"desEncrypt===orig %@", %orig);

	return %orig;
}


- (id) desDecrypt:(id)arg1 key:(id)arg2 {
	// %log;
	NSLog(@"desDecrypt arg1 = %@ arg2 = %@", arg1, arg2 );
	NSLog(@"desDecrypt===orig %@", %orig);
	return %orig;
}
- (id) QVGRSpobWNqWYHVm:(id)arg1 key:(id)arg2 {
	// %log;
	NSLog(@"QVGRSpobWNqWYHVm:key arg1 = %@ arg2 = %@", arg1, arg2 );
	return %orig;
}

- (id) QVGRSpobWNqWYHVm:(id)arg1 {
	// %log;
	NSLog(@"QVGRSpobWNqWYHVm arg1 = %@", arg1 );
	return %orig;
}

- (id) dCkFSxbcvATgvDOF:(id)arg1 {
	// %log;
	NSLog(@"dCkFSxbcvATgvDOF arg1 = %@", arg1 );
	return %orig;
}


- (id) PxXAtABexHNGjGWc:(id)arg1 {
	// %log;
	NSLog(@"PxXAtABexHNGjGWc arg1 = %@", arg1 );
	return %orig;
}
%end

  • 这里我没有去关注它内部的加密逻辑,只是拿到了加密的输出和输入(我们要的就是这个)

  • 那么我们就去我们想要的界面去找想要的内容

看打印出的服务端返回内容

{
	"code": 200,
	"list": [{
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/310598/1525874444233.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "MZZ颜宝",
		"roomId": "213909",
		"roomPay": 0,
		"url": "",
		"userId": 310598,
		"watchNum": 2774
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/300719/1526197877705.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "红酒女神",
		"roomId": "213901",
		"roomPay": 0,
		"url": "",
		"userId": 300719,
		"watchNum": 2640
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/343317/201805030457277987.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "U兔宝宝",
		"roomId": "213934",
		"roomPay": 0,
		"url": "",
		"userId": 343317,
		"watchNum": 2433
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/258508/201805091218287030.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "Mzz人丑对不起祖国",
		"roomId": "213709",
		"roomPay": 0,
		"url": "",
		"userId": 258508,
		"watchNum": 8792
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/222192/1525758635927.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "er秀秀女",
		"roomId": "213861",
		"roomPay": 0,
		"url": "",
		"userId": 222192,
		"watchNum": 3623
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/273879/201805130501314734.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "妞妞Da姐姐",
		"roomId": "213955",
		"roomPay": 0,
		"url": "",
		"userId": 273879,
		"watchNum": 387
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/288102/1526199691114.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "cK小淫妹",
		"roomId": "213923",
		"roomPay": 0,
		"url": "",
		"userId": 288102,
		"watchNum": 1407
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201804/217817/1523589369535.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "MZZ小辣椒",
		"roomId": "213964",
		"roomPay": 0,
		"url": "",
		"userId": 217817,
		"watchNum": 359
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/330326/1525259084018.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "FV俄国留学生",
		"roomId": "213875",
		"roomPay": 0,
		"url": "",
		"userId": 330326,
		"watchNum": 4004
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/283380/1526202846359.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG新葡京官方认证推筒子",
		"roomId": "213969",
		"roomPay": 0,
		"url": "",
		"userId": 283380,
		"watchNum": 315
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/351343/1526190381915.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG魔图精灵",
		"roomId": "213787",
		"roomPay": 0,
		"url": "",
		"userId": 351343,
		"watchNum": 437
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201804/225589/201804151030587590.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "燕子",
		"roomId": "213929",
		"roomPay": 0,
		"url": "",
		"userId": 225589,
		"watchNum": 912
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/383599/1526185673046.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "Q户外运动2",
		"roomId": "213897",
		"roomPay": 0,
		"url": "",
		"userId": 383599,
		"watchNum": 2
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/341027/1526190449533.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "MZZ我叫然儿",
		"roomId": "213790",
		"roomPay": 0,
		"url": "",
		"userId": 341027,
		"watchNum": 3139
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201804/218746/1523616631878.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "苏苏",
		"roomId": "213624",
		"roomPay": 0,
		"url": "",
		"userId": 218746,
		"watchNum": 4265
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/381140/1526200185989.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG赌皇国际娱乐会所",
		"roomId": "213936",
		"roomPay": 0,
		"url": "",
		"userId": 381140,
		"watchNum": 53
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/235006/1526194826473.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG娱乐在线",
		"roomId": "213865",
		"roomPay": 0,
		"url": "",
		"userId": 235006,
		"watchNum": 70
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/335223/1525414300646.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "SR-甜甜蜜蜜",
		"roomId": "213949",
		"roomPay": 0,
		"url": "",
		"userId": 335223,
		"watchNum": 143
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/365481/1526195868109.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG娱乐一筒天下",
		"roomId": "213881",
		"roomPay": 0,
		"url": "",
		"userId": 365481,
		"watchNum": 408
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/283355/1526007090245.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "Q闺蜜老公",
		"roomId": "213721",
		"roomPay": 0,
		"url": "",
		"userId": 283355,
		"watchNum": 949
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/238973/1526150049307.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "湿妹",
		"roomId": "213956",
		"roomPay": 0,
		"url": "",
		"userId": 238973,
		"watchNum": 487
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/217200/1526201580870.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "霸道小浪妹",
		"roomId": "213948",
		"roomPay": 0,
		"url": "",
		"userId": 217200,
		"watchNum": 714
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201804/313511/1524838889797.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "sr魅魔",
		"roomId": "213595",
		"roomPay": 0,
		"url": "",
		"userId": 313511,
		"watchNum": 2845
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/384935/1526185367686.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG名流娱乐会所",
		"roomId": "213726",
		"roomPay": 0,
		"url": "",
		"userId": 384935,
		"watchNum": 5325
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/329991/201805011847014858.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "MI芊",
		"roomId": "213889",
		"roomPay": 0,
		"url": "",
		"userId": 329991,
		"watchNum": 1315
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/312549/201805010204564037.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "JL雨",
		"roomId": "213907",
		"roomPay": 0,
		"url": "",
		"userId": 312549,
		"watchNum": 1899
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/262886/1525618035798.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "Dz妖孽人生",
		"roomId": "213821",
		"roomPay": 0,
		"url": "",
		"userId": 262886,
		"watchNum": 2608
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/223279/1526201868037.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "至尊大湿妹",
		"roomId": "213951",
		"roomPay": 0,
		"url": "",
		"userId": 223279,
		"watchNum": 1055
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/264781/1526201888712.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "小小姐",
		"roomId": "213953",
		"roomPay": 0,
		"url": "",
		"userId": 264781,
		"watchNum": 954
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/360406/201805071210131372.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "霸道宝贝",
		"roomId": "213940",
		"roomPay": 0,
		"url": "",
		"userId": 360406,
		"watchNum": 681
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/354808/1525544975204.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "Q妖姬",
		"roomId": "213789",
		"roomPay": 0,
		"url": "",
		"userId": 354808,
		"watchNum": 1143
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/377175/1526170882681.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "SG闭月羞花2",
		"roomId": "213567",
		"roomPay": 0,
		"url": "",
		"userId": 377175,
		"watchNum": 2508
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/272572/1525795865574.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "霸道全国跪求约泡可怜求爱爱",
		"roomId": "213950",
		"roomPay": 0,
		"url": "",
		"userId": 272572,
		"watchNum": 45
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/224784/1525323978804.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "MZZ甜心可可",
		"roomId": "213900",
		"roomPay": 0,
		"url": "",
		"userId": 224784,
		"watchNum": 1824
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/328016/201805130829280235.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "霸道椰子壳",
		"roomId": "213570",
		"roomPay": 0,
		"url": "",
		"userId": 328016,
		"watchNum": 1067
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201804/283909/1524472684340.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "283909丢丢",
		"roomId": "213702",
		"roomPay": 0,
		"url": "",
		"userId": 283909,
		"watchNum": 5689
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/383651/1526101482856.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG诚信走天下B",
		"roomId": "213944",
		"roomPay": 0,
		"url": "",
		"userId": 383651,
		"watchNum": 24
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/374105/1525901432016.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "新人求礼物",
		"roomId": "213961",
		"roomPay": 0,
		"url": "",
		"userId": 374105,
		"watchNum": 456
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/378153/1526181757820.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "霸道淫乱",
		"roomId": "213674",
		"roomPay": 0,
		"url": "",
		"userId": 378153,
		"watchNum": 742
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/387256/201805130509055450.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "xv小妖精",
		"roomId": "213965",
		"roomPay": 0,
		"url": "",
		"userId": 387256,
		"watchNum": 34
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/224084/1526197795009.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "nL小姐姐",
		"roomId": "213899",
		"roomPay": 0,
		"url": "",
		"userId": 224084,
		"watchNum": 3
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/338856/1525672991470.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "霸道左小雨",
		"roomId": "213921",
		"roomPay": 0,
		"url": "",
		"userId": 338856,
		"watchNum": 3
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201804/263866/1525086433855.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "醉红颜凡凡",
		"roomId": "213960",
		"roomPay": 0,
		"url": "",
		"userId": 263866,
		"watchNum": 303
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/298724/201805130427082900.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG愤怒的蚊子",
		"roomId": "213933",
		"roomPay": 0,
		"url": "",
		"userId": 298724,
		"watchNum": 914
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/316941/201805131623228255.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "妞妞可爱丽嘚嘚",
		"roomId": "213928",
		"roomPay": 0,
		"url": "",
		"userId": 316941,
		"watchNum": 1566
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/379899/1526202488299.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "ZJ夢無痕",
		"roomId": "213963",
		"roomPay": 0,
		"url": "",
		"userId": 379899,
		"watchNum": 251
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/277556/1526197660901.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "Oh舒服",
		"roomId": "213959",
		"roomPay": 0,
		"url": "",
		"userId": 277556,
		"watchNum": 2
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/365205/1525763881629.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG东东老虎鸡哦",
		"roomId": "213958",
		"roomPay": 0,
		"url": "",
		"userId": 365205,
		"watchNum": 394
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/379459/1526135486226.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "LY多多呀",
		"roomId": "213970",
		"roomPay": 0,
		"url": "",
		"userId": 379459,
		"watchNum": 63
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/356966/1525596024623.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG公平推筒子",
		"roomId": "213914",
		"roomPay": 0,
		"url": "",
		"userId": 356966,
		"watchNum": 17
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/370521/201805130344003097.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG你好明天吧",
		"roomId": "213893",
		"roomPay": 0,
		"url": "",
		"userId": 370521,
		"watchNum": 65
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/327725/201805130242323786.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG童颜大波",
		"roomId": "213851",
		"roomPay": 0,
		"url": "",
		"userId": 327725,
		"watchNum": 945
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/245238/1526201838262.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG依",
		"roomId": "213952",
		"roomPay": 0,
		"url": "",
		"userId": 245238,
		"watchNum": 5
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/384925/1526189955136.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GG辉煌娱乐",
		"roomId": "213782",
		"roomPay": 0,
		"url": "",
		"userId": 384925,
		"watchNum": 297
	}, {
		"avatar": "http://lopk.oss-cn-shanghai.aliyuncs.com/public/attachment/201805/365824/1526173626609.png?x-oss-process=image/resize,m_mfit,h_200,w_200",
		"nickName": "GGYYA诚信天下",
		"roomId": "213592",
		"roomPay": 0,
		"url": "",
		"userId": 365824,
		"watchNum": 8458
	}],
	"accountConfig": "{\"sdkAppId\":\"1400081396\",\"accountType\":\"24916\",\"IMType\":\"1\",\"webSdkAppId\":\"1106161652\"}"
}

其实正常其他盒子到这一步,就已经可以拿到直播url了,但是这家的盒子做的比较严谨!url居然返回空!散了吧!到此为止!这个盒子我搞不了

最后补上一个成功的文章http://bbs.iosre.com/t/mt-box-ios/11907

Posts: 39

Participants: 15

Read full topic

Native lldb 6.0.0 for iOS

关于ZipperDown

$
0
0

@Zhang wrote:

不是什么大事儿,但是鉴于新闻热度等等原因可能还是开一贴给普通开发者解释一下比较好。
压缩文件是允许路径指向类似../A/../B这种格式的, UNIX下../代表这个文件夹的上一层。比如说/A/B/../C实际上指的是/A/C

有问题的解压库没有对这种../做过滤,也就是说可以往解压路径外的地方解压文件。这不是一个系统级的沙盒逃逸漏洞

现在重点来了:
很多App会把所谓的热更新补丁放在沙盒内的某个路径下,比如说我们叫Documents/A.js吧,如果热更新的传输过程有中间人攻击的问题或者app被通过某种方式打开恶意的压缩包, 攻击者就可以覆盖A.js的内容,这样下次app启动加载热更新补丁A.js时就会执行恶意代码。

修复

原版的几个库下啊都有人提交了修复的PR, 老外破逼事多不一定merge,自己实现一遍也不难。另外做好SSL Pinning并且最重要的是,我说了无数遍了: 热更新绝大多数时候都是给18线三流程序员擦屁股用的,也是同样的一批人根本不知道怎么正确的做安全,引入巨量的隐患。 这都第几次了

感想

还是那句话,漏洞本身其实不是什么太复杂的事儿,但是确实暴露出来一些普通开发者的问题。

另外没记错的话盘古6还是哪一代的越狱也利用了类似的漏洞

Posts: 4

Participants: 4

Read full topic

逆向直播盒子MT·Box-iOS客户端

$
0
0

@yuzhouheike wrote:

首选推荐一下上次的一篇文章:逆向直播盒子Green-iOS客户端·另外我写这个文章主要是想说文章最后的几句话

写在前面的话本次要使用的几个工具

  • IDA
  • AloneMonkey的MonkeyDev
  • Charles

什么是直播盒子?

  • 单个直播的叫平台,比如斗鱼,熊猫,快手等等
  • 所有的平台放在一个App里面就被称为盒子
  • 现在上面上有很多直播平台(带颜色的)
  • 今天给大家带来的是MT·Box

什么是iOS逆向?

  • 不重要了

为什么上周刚刚逆向了直播盒子Green!这周又逆向这MT·Box个盒子?

  • 事情的起因是:上周帮兄弟们的忙,兄弟们需要破解,上个周末分析了直播盒子Green(破解本地各种加密。分析到最后发现服务端做了身份校验,所以失败了了)。所以被好多兄弟疯狂嘲讽!为了不让兄弟们失望而归!所以这个周末的第一天又进行一次

首先上个图(市面上的直播盒子现在也有很多,图只是其中一种,另一个兄弟发过来的链接)

  • 登录页可以抓包修改数据包status值为1 也可以直接输入123456进入

  • 顺利进入首页

  • 随便点击一个进去看看(点到你想看的位置)

  • 提示开通VIP 开通是不可能开通的。这辈子都不会开通的。只能找老师傅来破解。
  • 抓包改包

  • 没有vip字段。没有status状态码。
    • 但是你有没有发现有几个字段返回的空比如lifetime
    • 接下来改包

  • 成功看到了直播!意料之中

接下来就是hook源代码,直接把lifetime写出666666

%hook LoginModel

- (void)setLifetime:(id)arg1 {
	%log;
	NSLog(@"%@", arg1);
	%orig;
}

- (void)setStatus:(id)arg1 {
	%log;
	NSLog(@"%@", arg1);
	%orig;
}


- (NSString *)lifetime{
	%log;
 	return @"666666";
 }

-(NSString *)status{
%log;
	return @"1";
}

%end



上一篇文章结尾我说了这句话:其实正常其他盒子到这一步,就已经可以拿到直播url了,但是这家的盒子做的比较严谨!url居然返回空!散了吧。到此为止!这个盒子我搞不了。今天写这一篇主要就是为了证明我没有骗你们

最后

最后的最后!我想告诉大家一个小秘密。

那就是经过我不懈的努力。在盘古科技曝出iOS安全漏洞的第二天复现了盘古科技(搞越狱那个)曝出的iOS安全漏洞ZipperDown的漏洞视频(黑掉微博iOS客户端)https://zipperdown.org。但是作为一个有操守的iOS逆向爱好者,在微博没有修补漏洞之前。现在不能跟大家分享复现这个漏洞视频的过程(异常的艰辛。所以最终一定会和大家分享复现这个漏洞并执行微博任意代码的这个过程。盘古选择微博。是因为即使有这个漏洞,想执行微博任意代码还是不太容易!为了搞定这个。我甚至有一天晚上工作到3点!希望快速成长起来!)

Posts: 24

Participants: 11

Read full topic

在10.13.1上编译xnu内核(小白文)

$
0
0

@Peterpan0927 wrote:

为什么是小白文呢?因为我就是小白啊

在进行MOXil学习的时候,第九章我们需要通过编译内核来对XNU有进一步的了解,那么如何在最近的系统上编译XNU呢?今天就一起踩坑吧

资源工作

这里我们的内核版本和系统对应,首先看看是什么系统:

系统版本

那么我们从苹果开源代码网站上找到自己版本对应的XNU内核,对于我们来说,对应的是xnu-4570.20.62

如果下载了内核源码之后直接编译你就会遇到一大堆的error然后痛苦的go die,这是因为缺少很多头文件,库等等,首先我们需要安装XcodeCommand line Tool,这会帮我们解决很多麻烦,然后我们需要下载几个包,为了简便起见,我们直接通过脚本来做:

vim download.sh

脚本的内容为:

wget https://opensource.apple.com/tarballs/libplatform/libplatform-126.1.2.tar.gz
wget https://opensource.apple.com/tarballs/libdispatch/libdispatch-703.1.4.tar.gz
wget https://opensource.apple.com/tarballs/xnu/xnu-4570.20.62.tar.gz
wget https://opensource.apple.com/tarballs/dtrace/dtrace-262.tar.gz
wget https://opensource.apple.com/tarballs/AvailabilityVersions/AvailabilityVersions-32.tar.gz

其实你直接把命令复制过去就好的,到时候如果要做个自动化的工程还可以加个自动解压加编译的脚本
这样我们的下载过程就完成,接下来就是解压然后逐个编译,并配置好环境

环境准备

编译

AvailabilityVersions

$ mkdir -p dst
$ make install SRCROOT=$PWD DSTROOT=$PWD/dst
$ sudo ditto $PWD/dst/usr/local `xcrun -sdk macosx -show-sdk-path`/usr/local

dtrace

只需要编译 dtrace 项目中的 ctfconvertctfdumpctfmerge

$ mkdir -p obj sym dst
$ xcodebuild install -target ctfconvert -target ctfdump -target ctfmerge ARCHS="x86_64" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
$ sudo ditto $PWD/dst/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain

XNU头文件

在编译 libdispatch 之前,需要将XNU的头文件复制到SDK文件夹下,因为编译 libdispatch 时需要用到这些头文件。

$ mkdir -p BUILD.h/obj BUILD.h/sym BUILD.h/dst
$ make installhdrs SDKROOT=macosx ARCH_CONFIGS=X86_64 SRCROOT=$PWD OBJROOT=$PWD/BUILD.h/obj SYMROOT=$PWD/BUILD.h/sym DSTROOT=$PWD/BUILD.h/dst
$ sudo xcodebuild installhdrs -project libsyscall/Libsyscall.xcodeproj -sdk macosx ARCHS="x86_64" SRCROOT=$PWD/libsyscall OBJROOT=$PWD/BUILD.h/obj SYMROOT=$PWD/BUILD.h/sym DSTROOT=$PWD/BUILD.h/dst
$ sudo ditto BUILD.h/dst `xcrun -sdk macosx -show-sdk-path`

xnu-4570.1.46 版本的内核源码因为 缺失thread_self_restrict.h头文件,所以此处必定会编译失败。
需要在 xcodebuild 前先创建一个空的文件来使编译通过。

$ touch libsyscall/os/thread_self_restrict.h

libplatform

编译 libdispatch 时同样需要用到 libplatform 的头文件。

$ sudo ditto $PWD/include `xcrun -sdk macosx -show-sdk-path`/usr/local/include

libdispatch

$ mkdir -p obj sym dst
$ sudo xcodebuild install -project libdispatch.xcodeproj -target libfirehose_kernel -sdk macosx ARCHS="x86_64" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
$ sudo ditto $PWD/dst/usr/local `xcrun -sdk macosx -show-sdk-path`/usr/local

XNU

# KERNEL_CONFIGS: RELEASE / DEVELOPMENT / DEBUG
$ make SDKROOT=macosx ARCH_CONFIGS=X86_64 KERNEL_CONFIGS="RELEASE"

编译成功后,即可在BUILD/obj目录下找到内核文件。

# KERNEL_CONFIGS: RELEASE
$ cd BUILD/obj/RELEASE_X86_64
$ file kernel
kernel: Mach-O 64-bit executable x86_64

# KERNEL_CONFIGS: DEVELOPMENT
$ cd BUILD/obj/DEVELOPMENT_X86_64
$ file kernel.development
kernel: Mach-O 64-bit executable x86_64

# KERNEL_CONFIGS: DEBUG
$ cd BUILD/obj/DEBUG_X86_64
$ file kernel.debug
kernel: Mach-O 64-bit executable x86_64

最后可以看到我们编译成功的图:

最后要注意的是编译内核对于你的系统版本之类的有要求,比如上述的教程在更老的系统可能就不会成功,甚至在同为High Serria的其他小版本上也不会成功,我只是没有想到很坑的一点是我用的Serria的GCD和平台库函数加上10.13.1的内核最后东瓶西凑成功的。

一开始总是报一个没有<internal/atomic.h>的错,我本来想要创建一个解决,但是GCD中有很多依赖这个头文件的数据类型,所以我通过google找到这个出自于libplatform-125,于是乎我就在源代码逐个筛选,最后成功。如果你想体验这个过程,就全部使用10.13.1libplatformlibdispatch,你就会有不一样的体会。

替换内核操作(需要关闭SIP)

sudo cp BUILD/obj/kernel /System/Library/Kernels/
sudo kextcache -invalidate /

参考链接

Building the XNU kernel on Mac OS X Sierra (10.12.X)
XNU内核编译简要教程

Posts: 5

Participants: 3

Read full topic


Hikari的Swift移植

iOS11上面debugserver+lldb配置。感谢@Ouroboros 大佬的解答

$
0
0

@LuaSaltFish wrote:

iOS11越狱发行半年有余,Cycript这个最好用的运行时刻更新似乎因为神仙打架的原因而不支持iOS11.笔者只能将目光投入到lldb+debugserver。

按照debugserver的配置办法从设备中拷贝debugserver到电脑,除了小黄书提到的4个权限需要额外签名 platform-application 否则会被直接干掉。原本以为就此结束可以开心的用debugserver调试。但是现实中做到这一步确实可以运行debugserver但基本会出现以下问题
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-360.0.26.14
for arm64.
Attaching to process Preferences…
error: failed to attach to process named: “” (os/kern) invalid argument
按照@Ouroboros大佬提示的原因,需要使用jailbreak_client提权。

按照大佬给的代码。

#include <spawn.h>

int  main(int argc, char *argv[], char *envp[])
{
    if (argc < 2)
    {
   fprintf(stderr, "usage: %s program args...\n", argv[0]);
       
       return EXIT_FAILURE;
    }
    
    int ret, status;
    pid_t pid;
    posix_spawnattr_t attr;
    
    posix_spawnattr_init(&attr);
    posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
    
    ret = posix_spawnp(&pid, argv[1], NULL, &attr, &argv[1], envp);
    
    posix_spawnattr_destroy(&attr);
    
    if (ret != 0)
    {
        printf("posix_spawnp failed with %d: %s\n", ret, strerror(ret));
        return ret;
    }
    
    char buf[200];
    
    snprintf(buf, sizeof(buf), "/electra/jailbreakd_client %d 1", pid);
    system(buf);
    
    kill(pid, SIGCONT);
    waitpid(pid, &status, 0);
    
    return 0;
}

使用theos的tool模版制作一个小的命令行工具,我这里用的名字是JBDO。make package install 后ssh进入设备


执行命令JBDO debugserver *:1234 -a “Preferences”

提示如下

 debugserver-@(#)PROGRAM:debugserver  PROJECT:debugserver-360.0.26.14
 for arm64.
Attaching to process Preferences...
Listening to port 1234 for a connection from *...
Failed to get connection from a remote gdb process.
Exiting.

还是不行。通过简单搜索,发现debugserver只能绑定iOS设备本地ip。可是lldb for ios同样没有iOS11的版本。只好祭出usbmux


使用python运行tcp tcprelay.py 监听端口:监听端口

新建另一个终端窗口 lldb

process connect connect://localhost:监听端口

挺卡的,即使usb有线也用了数十秒

  Process 2788 stopped
* thread #1: tid = 0xb3313, 0x0000000181bd4bc4 libsystem_kernel.dylib`mach_msg_trap + 8, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x0000000181bd4bc4 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
->  0x181bd4bc4 <+8>: ret    

libsystem_kernel.dylib`mach_msg_overwrite_trap:
    0x181bd4bc8 <+0>: movn   x16, #0x1f
    0x181bd4bcc <+4>: svc    #0x80
    0x181bd4bd0 <+8>: ret    
(lldb)  

至此,iOS11上面的lldb+debugserver就完成了。感谢群里大佬们的指点。希望能帮到遇到同样问题的坛友。

Posts: 2

Participants: 2

Read full topic

带着《iOS应用逆向与安全》,我还在路上

$
0
0

@AloneMonkey wrote:

带着《iOS应用逆向与安全》,我还在路上…

一直想写篇文章把书里面的某个章节拿出来给大家分享一下,然后顺便推荐下我的新书,但最近一直没有腾出时间来做这件事。后面自己也想了想,如果只是单纯的把书里面的内容拷贝过来然后加个购买链接的话,感觉也不是很好。其实这类的技术文章论坛、网上都有很多很多,你多看一篇或者会有自己的思考,或许只是已阅,我希望这篇文章带给你的不只是一个技术点,还有一点小道理。

学习之路

在这个互联网的时代,学习真的是一件非常容易的事情,网上有很多的资料和前人的总结,可以让我们少走很多弯路。但是随之的问题也来了,东西太多,资料太杂找不到属于自己的路。其实一开始我们都是小白,只想跟着前人的脚印在这条路上越走越远,然后尽量能够为后面的人留下自己的脚印。然后当你开始走的时候,发现大家的脚印和方向都不一样,你该跟谁走?

所以学习的开始,应该先想好规划好自己的路线,把大方向变成小方向,根据自己的规划然后借鉴别人的走法走出属于自己的路。切忌上来一顿操作,然后在各种岔路口迷茫浪费时间。在学习过程中,一定不能浮躁,要放低自己的心态。

  • 不能浮躁,很多人一上来就急于求成,然后遇到各种问题的时候又不知从何下手,可能最后你浪费的大把的时间解决了问题,但你还是不知道这是为什么,所以不如放慢步伐,一步一步来。
  • 放低心态,应该感谢那些编写文章和提供工具的人,而不是遇到问题的时候把问题甩到别人头上。

另外提到问问题的事,之前有人说过问别人问题的时候,别人都不回答之类的。其实这种情况一般会有三个原因:

  1. 别人确实在忙,没有时间回答。
  2. 自己问的问题是完全没有经过思考的问题。
  3. 问问题的时候完全没有上下文,别人要追问好几个问题才知道你要干嘛。

而且每个人的时间都是宝贵的,有人帮助你回答问题自然要感谢,没人回答也不应该去埋怨谁,而是尝试自己去解决。一开始的时候都会问出小白问题,但是不是遇到什么问题的时候想也不想就丢出来给别人,然后也没有个上下文啥的。自己先思考一番后,实在找不到答案再去问别人。

每个人都应该好好看看提问的智慧

还有就是总会想为什么别人这么优秀,都是“大佬”,其实并没有什么大佬,或者说每个人都是大佬,只是在不同领域不同方向别人比你多花那么一点点时间和多那么一点经验,换一个领域我们都是小白。或者你有没有想过,你在做什么事情的时候总是想着推迟一点点再想,晚一点点再做,或者每一点点就是现在的你和优秀的你之间的一点点差距。

最后用四个“带着”说下我自己的方法吧:

  1. 带着目标动手学习
  2. 带着疑问寻找答案
  3. 带着想法寻求实现
  4. 带着思维融入工作

选择之路

在埋头走路的时候,可以一鼓作气无惧前行,但是如果觉得自己走偏的时候,或者停下来查看前进方向的时候,总会有太多的顾虑和选择。其实我们技术人也是一样,成长到一定阶段的时候就会面临各种选择,比如:我应该继续深入这个方向研究,还是扩充下自己各个方面的能力。我应该转管理路线还是继续从事技术研究。我应该学习现在热门的机器学习、人工智能这些东西吗,不学会不会跟不上市场需求。我该不该换个工作环境,应该选择哪一个公司等等。

每当需要抉择的时候,不管是自己还是身边的人都会有各种不同的声音,有说深入技术才有自己的优势,也有说要跟上技术的潮流不然会被淘汰之类的。不管怎么说,在选择之前我们都不确定自己会不会是在对的路上,但是自己也需要结合自身的情况作出合理的决定,不要因为决定让自己彷徨不前。我一直坚信我选择的每条路都是对的,当然也非常感谢在路上给我给予我帮助和机会的人。

其实选择也是成长的一个过程,在选择的过程中更加认清自己,有得到也有失去,但最终都是你人生路上的一道美丽的风景。不要害怕自己做错了决定,有错误也有磨炼都是一种经历,一旦决定了就要勇敢的走下去。

其实我自己做视频或写书,都是在成长路上抓住了别人提供的一些机会,同时也是给自己的一个机会。一个事情,就如做视频来说,一开始会想很多,要自己准备资料、制作PPT、录制、确定剪辑效果等等这些东西没人教你,你不知道你会不会做的好,自己能不能cover住,但是不做的话永远也不会知道,同时自己也失去了一个机会。做过后就算自己做的不够完美,也会收获一些经验知道自己还有哪些问题,这些都是不可多得的东西。

回到新书

最后还是提一下我的新书吧,一开始也只是和朋友聊到可以写本书,然后他认识出版社的人可以给我推荐一下,我一开始也犹豫了许久,最后还是决定去做了,原因有三:

  1. 自己之前也写过一些文章,但是很零散也没有系统性,可以通过写书做一个系统性的总结。
  2. 自己也是从小白走过来的,有系统性的东西和路线对自己的学习帮助很大,也可以帮助下后面学习的人。
  3. 这也是一个挑战,没有经验,想要写好很难,但是能够去尝试做的话,并且还能做的还可以的话,对自己也是一份收获。

写书其实是一个很枯燥的过程,你需要去深入理解、去梳理结构、整理文字,其实以我的能力来说,并不能够做到很好,但还是尽力去做了,写的时间也不是很长,从去年年中到10月就完稿了,然后就是等待出版社的一系列流程,所以可能很多东西都没有做到很好。

我对整个书的大体构思是这样的,因为从我自己学习或者是别人遇到的一些问题来看,最大的问题是:

  1. 自己没有系统的学习路线,都是走到哪算哪。
  2. 只掌握表面的东西,对内部原理不了解,导致遇到问题不知从何下手。
  3. 同一个东西不同人的思维方式差很大,这也是知识面决定了策略。

所以我的书主要分为如下几个方面:

  1. 新手能够快速上手,有工具使用和原理的介绍。
  2. 有经验的人可以更加深入,从原理、语言、结构各个方面去掌握和理解。
  3. 可以补充不同层面的知识点、包括正向开发、逆向分析技巧、安全防护等等。

说到思维方式的话,举个简单的例子,不久前在论坛看到一篇去广告的文章:《逆向世界杯直播App 央视影音-iOS客户端》。其实需求很简单就是“去掉广告”,但是具体实现的思路,一百个人可能有99种方法。

在继续阅读下文之前,希望你能自己先针对这个问题思考一下,如果是你你会怎么做,有哪些思路?

这里说下我自己的想法,当然文章里面提供的思路和切入点也是非常好的。首先接到这个需求的时候不着急下手,先思考下可选的方案:

  1. 找到对应的Controller或者View使用logify.pl打印方法调用日志。
  2. class-dump头文件从类名或方法名中根据一些关键字比如Ad,找找线索。
  3. 使用frida或者其它的动态函数调用跟踪工具看看调用流程。
  4. 还原符号,使用LLDB对类的所有方法断点看调用。
  5. 直接从Malloc stack看View对象是在哪里生成的。

这里选择第4和5个方案简单演示一下。

首先下载App,然后使用frida-ios-dump把解密的ipa提取出来:

➜  frida-ios-dump git:(3.x) ✗ ./dump.py 央视影音
Start the target app 央视影音
Dumping 央视影音 to /var/folders/h3/8n169g610_g69k50z7g_q4gc0000gp/T
start dump /var/containers/Bundle/Application/EF1D9293-1894-490D-B01D-5C21A71A2AFF/Cbox.app/Cbox
Cbox.fid: 100%|████████████████████████████| 38.6M/38.6M [00:13<00:00, 3.02MB/s]
start dump /private/var/containers/Bundle/Application/EF1D9293-1894-490D-B01D-5C21A71A2AFF/Cbox.app/Frameworks/PI_VRRender.framework/PI_VRRender
PI_VRRender.fid: 100%|███████████████████████| 136k/136k [00:00<00:00, 1.18MB/s]
start dump /private/var/containers/Bundle/Application/EF1D9293-1894-490D-B01D-5C21A71A2AFF/Cbox.app/Frameworks/PI_MediaCore.framework/PI_MediaCore
PI_MediaCore.fid: 100%|████████████████████| 1.14M/1.14M [00:00<00:00, 4.74MB/s]
start dump /private/var/containers/Bundle/Application/EF1D9293-1894-490D-B01D-5C21A71A2AFF/Cbox.app/Frameworks/PI_iLiveBase.framework/PI_iLiveBase
PI_iLiveBase.fid: 100%|██████████████████████| 252k/252k [00:00<00:00, 3.10MB/s]
start dump /private/var/containers/Bundle/Application/EF1D9293-1894-490D-B01D-5C21A71A2AFF/Cbox.app/Frameworks/PSLogUpload.framework/PSLogUpload
PSLogUpload.fid: 100%|███████████████████████| 462k/462k [00:00<00:00, 3.73MB/s]
zhanghao-button-denglu.png: 121MB [00:25, 4.91MB/s]
0.00B [00:00, ?B/s]Generating "央视影音.ipa"

然后创建MonkeyDev工程,将解密后的ipa放到TargetApp目录下面运行,出现了如下的错误:

/Users/monkey/Desktop/CBox/CBox/TargetApp/Cbox.app/Frameworks/PSLStreaming.framework/PSLStreaming

This file is encrypted! please use https://github.com/AloneMonkey/frida-ios-dump to decrypt!

这个是因为应用启动的时候这个framework并没有加载起来,可以等加载的时候再dump或者不影响的话先删了,顺便在Build Settings里面设置MONKEYDEV_RESTORE_SYMBOLYES,方便后面设置符号断点。

重新运行点击世界杯,然后点击一个直播后使用Xcode查看界面结构找到对应的广告View如下:

然后在LLDB使用如下命令对类的方法下断点:

(lldb) br set -r \[CNAdPlayerView .*\]
Breakpoint 1: 80 locations.

然后重新进入直播可以看到断点断到如下位置:

这是这个View的初始化,同时在调用栈上面看到了-[CNVideoPlayer setupAdNeedPlay]这个设置是否显示广告的函数,使用下面的命令找到这个函数在文件的偏移然后打开Hopper看一下,这里的0x1002ec3e4就是点击这个函数在Xcode里面看到的地址:

(lldb) image lookup -a 0x1002ec3e4
      Address: Cbox[0x00000001002183e4] (Cbox.__TEXT.__text + 2169700)
      Summary: Cbox`-[CNVideoPlayer setupAdNeedPlay]

这就很明显可以看到是-[CNVPConfig adEnabled]在控制的广告是否显示了,编写代码搞定:

CHDeclareClass(CNVPConfig)

CHOptimizedMethod0(self, BOOL, CNVPConfig, adEnabled) {
    return NO;
}

CHConstructor{
    CHLoadLateClass(CNVPConfig);
    CHClassHook0(CNVPConfig, adEnabled);
}

再来看看第5个方案,点击Edit Scheme选择Diagnostics勾选Malloc Stack:

然后重新运行找到View的对象地址(和上面查看View结构一样),然后点击Debug Memory Graph搜索这个对象,找到这个View的对象后在右侧可以看到这个对象的Malloc Stack:

同样可以快速定位到-[CNVideoPlayer setupAdNeedPlay]这个函数从而找到突破口。

所以说学习逆向,更多的是需要把自己掌握的知识能够结合起来并灵活运用,而要做到这一步首先需要自己把需要的理论基础知识有一个系统的掌握和吸收,然后在遇到问题的时候从不同的角度通过不同的方法去尝试和解决它。

硬广

上面就是我在写书的一些经历以及对整本书的一个知识组成和安排,另外上面提到的一些基础、理论、原理以及分析的技巧都会在书本里面讲解到,可以让大家对逆向有一个系统的更深层次的认知和掌握。

最后是时候亮出购买地址了:

京东: https://item.jd.com/12361729.html

天猫: https://detail.tmall.com/item.htm?id=570691214072

很高兴能在今年让这本书和大家见面,并且分享给大家,2018我还在路上,你呢?

Posts: 11

Participants: 11

Read full topic

Libc中malloc对于内存的分配流程(请指教)

关于macOS10.11.6本地提权(小白文)

$
0
0

@Peterpan0927 wrote:

0x00 前言

在对漏洞进行学习的时候,因为各方面细小知识点的缺少,导致踩了一些坑,这次来分析的是之前很多人已经说过的三叉戟漏洞的后两个来实现的本地提权,虽然有很多poc,但是其中的描述并不是很详细,所以我就通过自己的学习过程来做一些科普吧算是。
在阅读之前需要对于这两个漏洞有一定的了解,资料我丢在链接部分了。

0x01 流程

首先我还是按照惯例梳理了整个Poc的流程画了一张流程图,可以结合别人poc看看:

0x02 踩坑处

  1. 对于macOS中堆的分配机制不太了解,因为UAF的构造就是利用这个性质,可以去看一下kalloc或者linux上的堆分配器Glibc的分配机制都会有所帮助
  2. 对于NULL pointer解引用从而跳到零页面,参考stackover flow
  3. 对于零页面的构造,这个有关于C++的vtable在内存中的情况,可以参考深入理解C++对象模型第一章。

0x03 参考链接

因为很多人写过相关,所以我就不班门弄斧了,丢几个写的好的链接供大家参考
1.jndok
2.lookout
3.zhengmin(Spark)

Posts: 1

Participants: 1

Read full topic

Viewing all 301 articles
Browse latest View live