新手练习 FlappyBird去广告

[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);
    }
    }
    }

@end

[/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则向广告服务器获取广告数据,再发送给游戏加载。如果流程中的任意一个一节点被破坏,那么最终的广告都不会被加载成功,供我们破坏的节点有以下几个切入点:
  1. 禁止SDK向服务器发送请求;
  2. 不允许游戏向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就很有必要,难度也要大,相对来说“破坏行为”更容易些!

算是干掉 google Ad 通用方法

赞! 可以写详细些你的思维过程 你的体会

学习了,感谢博主提供这么直接的思路。

:lol::lol:顶一票,确实比较详细的解说出来…让大伙都知道

为何,我的跟楼主的差不多,编译的时候会
make[2]: *** No rule to make target obj/Tweakweak_Instagram.xm.e8c06f5e.o', needed by obj/instagram.dylib.ba964c90.unsigned’. Stop.

解决否? 按提示应该是没有 ldid 签名 ,可能没安装ldid在这个路径下 /opt/theos/bin/ldid ,或者 你安装的ldid不能用

赞! 很详细的说

{Filter = {Bundles = ( “com.dotgears.flap” ); };}

请问
dotgears 以及flap 两部分是怎么组成的

这应该就是App的bundle id啊,没什么组成不组成的

哦哦 我当时看com.apple.SpringBoard 以为一部分来自于SpringBoard 中间的apple是属于苹果的某某

多谢楼主写出来分享。

哥们被你雷到了!!:titter:

多谢分享,学习了