[postbg]bg3.png[/postbg]%hookGADBannerView
-(void)loadRequest:(id)arg1{
}
%end
===========================详细过程==================================
==================================================================
FlappyBird莫名其妙地火了,作者日入几十万,这是无数猿媛的梦想,而其“罪魁祸首”就是游戏内的广告。带着羡慕嫉妒的心理把广告干掉吧~~
大多数的免费应用游戏都会内置广告,除了GoogleAD和Apple AD还有种类繁多的第三方移动广告平台,如有米, 微云, 多盟, 亿动智道, AdMob, Millennial Media, 架势, 哇棒, 易传媒等等。因为广告是看得见的东西,所以直接使用Reveal查看。
class-dump 出头文件,导入XCode,确定入口类
在头文件中搜索 “UIResponder”,
只有“dot_AppDelegate”这一个文件,就是它了。
创建tweak
文件列表
[table=98%,rgb(204, 255, 255)]
[tr][td=426] Makefile control
RevealUtil.h Tweak_common.xm
RevealUtil.m supery_monitor_flap.plist
[/td][/tr]
[/table]
MakeFile
[table=98%,rgb(153, 204, 255)]
[tr][td=426] THEOS_DEVICE_IP = 192.168.2.3
include theos/makefiles/common.mk
TWEAK_NAME = supery_monitor_flap
supery_monitor_flap_FILES = Tweak_common.xm RevealUtil.m
supery_monitor_flap_FRAMEWORKS = UIKit CFNetwork
supery_monitor_flap_PRIVATE_FRAMEWORKS = AppSupport
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec “killall -9 flap”
[/td][/tr]
[/table]
Tweak_common.xm
[table=98%,rgb(153, 204, 255)]
[tr][td=426] #import “RevealUtil.h”
%hook dot_AppDelegate
- (BOOL)application:(id)arg1 didFinishLaunchingWithOptions:(id)arg2 {
%orig;
RevealUtil *ru = [RevealUtil alloc] init];
[ru startReveal];
return YES;
}
%end
[/td][/tr]
[/table]
supery_monitor_flap.plist
[table=98%,rgb(153, 204, 255)]
[tr][td=426] {Filter = {Bundles = ( “com.dotgears.flap” ); };}
[/td][/tr]
[/table]
RevealUtil.h
[table=98%,rgb(64, 64, 64)]
[tr][td=426] //
// RevealUtil.h
//
//
// Created by Supery on 13-2-17.
// Copyright (c) 2013年 CY7695. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface RevealUtil : NSObject
{
void *_revealLib;
}
- (void) startReveal;
- (void) stopReveal;
@end
[/td][/tr]
[/table]
RevealUtil.m
[table=98%,rgb(64, 64, 64)]
[tr][td=426] //
// RevealUtil.m
//
//
// Created by Supery on 13-2-17.
// Copyright (c) 2013年 CY7695. All rights reserved.
//
#import “RevealUtil.h”
#import <dlfcn.h>
@implementation RevealUtil
-
(void)startReveal{
NSString* revealLibName = @“libReveal.dylib”;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* dyLibPath = [NSString stringWithFormat:@"%@/%@",documentsDirectory,revealLibName];
_revealLib = NULL;
_revealLib = dlopen([dyLibPath cStringUsingEncoding:NSUTF8StringEncoding], RTLD_NOW);
if (_revealLib == NULL) {
char* error = dlerror();
NSLog(@“dlopen error %s” , error);
}else{
[NSNotificationCenter defaultCenter] postNotificationName:@“IBARevealRequestStart” object:self];}
} -
(void)stopReveal{
if (_revealLib) {
[NSNotificationCenter defaultCenter] postNotificationName:@“IBARevealRequestStop” object:self];
if (dlclose(_revealLib) == 0) {
_revealLib = NULL;
}else{
char* error = dlerror();
NSLog(@"Reveal library could not be unloaded : %s ", error);
}
}
}
[/td][/tr]
[/table]
其他文件默认即可。
使用iFunBox将Reveal提取出来的libReveal.dylib文件导入到FlappyBird的Documents文件夹下,准备工作已经完成。
编译、打包、安装
打开Reveal,待连接到设备后,可以看出广告所属类型为 GADBannerView ,它的SuperView是dot_EAGLView,也就是程序的主界面View。当然我们可以进行更多操作来测试它是否还用了其他类型的广告,以便一一击破。另外尽管google AD提供给开发者的是.a的静态库文件,通过Reveal后我们也可以看到它的UI结构。如图:
Reveal对于OpenGL是无能为力的,也就是说对于大多数的游戏UI界面是没办法分析的。
至此,可以关掉Reveal了。
广告类型我们已经确认,下面来分析头文件,搜索dot_EAGLView,结果只有一个,打开dot_EAGLView,发现它实现了GADBannerViewDelegate 并且实现了
[table=98%,rgb(153, 204, 255)]
[tr][td=414] - (void)adView:(id)arg1 didFailToReceiveAdWithError:(id)arg2;
- (void)adViewDidReceiveAd:(id)arg1;
[/td][/tr]
[/table]
这两个委托方法,可以确定广告就在这了。其次,还有这些方法
[table=98%,rgb(153, 204, 255)]
[tr][td=426]
- (void)touchesCancelled:(id)arg1 withEvent:(id)arg2;
- (void)touchesEnded:(id)arg1 withEvent:(id)arg2;
- (void)touchesMoved:(id)arg1 withEvent:(id)arg2;
- (void)touchesBegan:(id)arg1 withEvent:(id)arg2;
- (void)layoutSubviews;
- (void)leaderboardViewControllerDidFinish:(id)arg1;
- (void)drawView:(id)arg1;
- (void)stop;
- (void)start;
- (void)dealloc;
- (void)setup;
- (void)checkLocalPlayer;
- (void)adView:(id)arg1 didFailToReceiveAdWithError:(id)arg2;
- (void)adViewDidReceiveAd:(id)arg1;
- (id)initWithFrame:(struct CGRect)arg1;
[/td][/tr]
[/table]
当然最重要的还发现了GADBannerView *admobBannerView;这句,它应该就是屏幕上方的广告条实例对象,让我们来尝试一下,把admobBannerView隐藏掉。
创建Tweak
MakeFile中添加下面两句:
THEOS_DEVICE_IP= 192.168.2.3
supery_rm_ad_flap_FRAMEWORKS= UIKit
Tweak.xm
先将admobBannerView隐藏语句放在初始化方法initWithFrame中,内容如下:
[table=98%,rgb(153, 204, 255)]
[tr][td=426]
@interface GADBannerView:UIView
@end
%hook dot_EAGLView
- (id)initWithFrame:(struct CGRect)arg1{
%orig;
GADBannerView* adv = MSHookIvar<GADBannerView *>(self, “admobBannerView”);
[adv setHidden:YES];
NSString* str = [NSString stringWithFormat:@"%@",adv];
UIAlertView* alert = [UIAlertView alloc]initWithTitle:@“initWithFrame” message:str delegate:nil cancelButtonTitle:@“Ok” otherButtonTitles: nil];
[alert show];
[alert release];
return self;
}
%end
[/td][/tr]
[/table]
通过MSHookIvar 可以获取程序中的全局变量.
执行 make package install,
这里会发现找不到 MSHookIvar,google之,解决方法使用https://github.com/caughtinflux/include/blob/master/substrate.h替换$THEOS/include/substrate.h。
当然我们还可以使用KVC和OC的运行时机制来获取全局变量:
runtime
[table=98%,rgb(204, 255, 204)]
[tr][td=426] GADBannerView* adv;
object_getInstanceVariable(self, “admobBannerView”, (void **)&adv);
[/td][/tr]
[/table]
KVC
[table=98%,rgb(204, 255, 204)]
[tr][td=426] GADBannerView* adv = [self valueForKey:@“admobBannerView”];
[/td][/tr]
[/table]
安装完成后,打开Flappy Bird:
发现广告并没有消失,并且可以看到admobBannerView为空,说明此时GADBannerView实例对象还未创建,那放在哪里呢?start,stop,setup还是其他呢?来试试 -adViewDidReceivedAd: 这个方法(广告已经显示了,对象肯定是存在的),Tweak.xm内容如下:
[table=98%,rgb(153, 204, 255)]
[tr][td=426]
@interface GADBannerView:UIView
@end
%hook dot_EAGLView
-
(void)adViewDidReceiveAd:(id)arg1{
%orig;
GADBannerView* adv = MSHookIvar<GADBannerView *>(self, “admobBannerView”);
[adv setHidden:YES];NSString* str = [NSString stringWithFormat:@"%@",adv]; UIAlertView* alert = [UIAlertView alloc]initWithTitle:@"adViewDidReceiveAd" message:str delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil]; [alert show]; [alert release];
}
%end
[/td][/tr]
[tr][td=426]
[/td][/tr]
[/table]
这次admobBannerView不为空了,可以看到详细信息,而且屏幕上方的广告条消失了,说明成功关掉了广告。
彻底屏蔽广告因为只是隐藏了广告条,并没有真正消失,每次小鸟被摔死时还会去网络下载广告图片和数据,用Wireshark抓包看看:
数据包是在增长的,并且每次都是在收到广告数据的时候来更新GADBannerView对象的。
本着“斩草要除根”的原则,禁止其发起网络请求获取数据,目标瞄向SDK,admobBannerView是GADBannerView类型的,所以转向GADBannerView这个类,查看GADBannerView.h,方法列表如下:
[table=98%,rgb(153, 204, 255)]
[tr][td=426] @interface GADBannerView : UIView
{
GADSlot *_slot;
}
@property(retain, nonatomic) GADSlot *slot; // @synthesize slot=_slot; @property(readonly, nonatomic) NSString *adNetworkClassName;
@property(readonly, nonatomic) UIView *mediatedAdView;
@property(readonly, nonatomic) BOOL hasAutoRefreshed;
@property(readonly, nonatomic) GADWebView *webView;
@property(nonatomic) struct GADAdSize adSize;
@property(nonatomic) UIViewController *rootViewController;
@property(nonatomic) NSObject *delegate;
@property(copy, nonatomic) NSString *adUnitID;
- (void)loadRequest:(id)arg1;
- (void)dealloc;
- (void)awakeFromNib;
- (id)initWithAdSize:(struct GADAdSize)arg1 origin:(struct CGPoint)arg2;
- (id)initWithAdSize:(struct GADAdSize)arg1;
- (id)initWithFrame:(struct CGRect)arg1;
- (id)init;
- (void)setAlpha:(float)arg1;
- (id)inAppPurchaseDelegate;
- (void)setInAppPurchaseDelegate:(id)arg1;
- (struct CGSize)intrinsicContentSize;
- (void)setBounds:(struct CGRect)arg1;
- (void)setFrame:(struct CGRect)arg1;
- (void)commonInitializer;
- (void)setAdSize:(struct GADAdSize)arg1 andReload:(BOOL)arg2;
[/td][/tr]
[/table]
整个广告SDK的流程应该是这样的:游戏向SDK发起请求加载广告,SDK分析后判断广告数据是否已存在,如果存在,将广告数据发送给游戏并通知游戏可以加载;如果不存在,SDK则向广告服务器获取广告数据,再发送给游戏加载。如果流程中的任意一个一节点被破坏,那么最终的广告都不会被加载成功,供我们破坏的节点有以下几个切入点:
- 禁止SDK向服务器发送请求;
- 不允许游戏向SDK发起请求;
3.禁止广告服务器发送广告数据;
4.当SDK接收到广告服务器发来的数据禁止其通知或发送给游戏。
方法1、2、4都可以尝试,方法3好像没客户端什么事!
先考虑方法1,通过查看方法列表,其中-(void)loadRequest:(id)arg1;带了一个Request这个关键字,最有可能是发起请求的方法,来验证一下:
Tweak.xm
[table=98%,rgb(153, 204, 255)]
[tr][td=426] %hook GADBannerView
-
(void)loadRequest:(id)arg1{
NSString* str = [NSString stringWithFormat:@"%@",arg1];
UIAlertView* alert = [UIAlertView alloc]initWithTitle:@“loadRequest” message:str delegate:nil cancelButtonTitle:@“Ok” otherButtonTitles: nil];
[alert show];
[alert release];%orig;
}
%end
[/td][/tr]
[/table]
安装启动:
可以看到,代码被执行了,并且每次小鸟被摔死的时候这个方法都会被调用,说明这是SDK发起网络请求的地方,那直接干掉这个方法就行了,Tweak.xm内容如下:
[table=98%,rgb(153, 204, 255)]
[tr][td=426]
%hook GADBannerView
- (void)loadRequest:(id)arg1{
}
%end
[/td][/tr]
[tr][td=426]
[/td][/tr]
[/table]
安装运行,广告彻底没有了,让我们再用WireShark抓包看下,确实没有任何网络请求。实际上代码中去掉 %orig,只是不调用程序原有代码而是执行我们所写的代码,在这里什么都不做。方法1验证成功!目的已经达到。
如果方法1没能尝试成功,那么我们需要继续去尝试方法2和4。
整个过程没有用到IDA分析,因为只是去除广告,不需要分析它的逻辑,如果是更改游戏中的数据,更改游戏逻辑,IDA就很有必要,难度也要大,相对来说“破坏行为”更容易些!