实战5:《iOS应用逆向工程(第2版)》微博抽奖插件

这个插件是干嘛的,就不用我多介绍了吧 :blush:如果还不清楚就看这里。咱们直接开始一步步分析。

一 搭建tweak原型

咱们的目标,是在抽奖微博的所有转发者中,随机筛选出符合条件的转发者。所以手动的实现方案,就是第1步用微博iOS客户端打开抽奖微博并切换到转发界面,第2步上拉加载所有转发者,第3步根据转发的内容做筛选。其中第1步只用操作一次,手动和自动没有太大区别;而第2需要N多次上拉刷新才能加载所有转发者,第3步更是需要肉眼遍历所有转发内容,才能筛选出符合条件的转发者,手动操作起来工作量大得没边儿了。因此,第2和3步的自动化相比手动操作具有无可比拟的优势,它们是微博抽奖插件的核心功能。好了,这次实战的任务我们基本可以划分为2个小任务:

1. 找到“微博正文”转发界面的上滑加载函数,在这个函数内部找到实现“加载更多转发者”功能的函数

利用Cycript,通过UI切入代码找到V的方法书上已经重点讲解过,而通过V找到其UIScrollViewDelegate对象就很简单了,因此我们可以先找到转发界面的V,再找到其delegate,最后用IDA分析相应scroll函数,就可以找到“加载更多转发者”函数。

2. 从转发界面中找到读取所有转发内容的函数

这是一个从V找M的过程,同从UI切入代码找V一样,书上已经详细讲解不下6遍了,别说你还不会哦~
以上逻辑很清楚了吧?我们这就操作起来。

二 class-dump微博客户端头文件

1. 定位微博的可执行文件

FunMaker-5:~ root# ps -e
  PID TTY           TIME CMD
    1 ??         0:42.58 /sbin/launchd
……
 1220 ??         0:04.35 /var/mobile/Containers/Bundle/Application/B4C52234-47C1-41A9-B23F-EC2224BC5E30/Weibo.app/Weibo
 1222 ??         0:00.17 /System/Library/Frameworks/UIKit.framework/Support/pasteboardd
 1204 ttys000    0:00.04 -sh
 1224 ttys000    0:00.01 ps -e

2. dumpdecrypt之

FunMaker-5:~ root# cycript -p Weibo
cy# [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0]
#"file:///var/mobile/Containers/Data/Application/82E81825-AFB3-4D5F-A3A9-BB237B23D724/Documents/"
cy# 
FunMaker-5:~ root# cp /User/Downloads/dumpdecrypted.dylib /var/mobile/Containers/Data/Application/82E81825-AFB3-4D5F-A3A9-BB237B23D724/Documents/
FunMaker-5:~ root# cd /var/mobile/Containers/Data/Application/82E81825-AFB3-4D5F-A3A9-BB237B23D724/Documents/
FunMaker-5:/var/mobile/Containers/Data/Application/82E81825-AFB3-4D5F-A3A9-BB237B23D724/Documents root# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Containers/Bundle/Application/B4C52234-47C1-41A9-B23F-EC2224BC5E30/Weibo.app/Weibo
mach-o decryption dumper

DISCLAIMER: This tool is only meant for security research purposes, not for application crackers.

iOSRE: uid = 0, euid = 0, gid = 0, egid = 0.

[+] detected 32bit ARM binary in memory.
[+] offset to cryptid found: @0xf0a4c(from 0xf0000) = a4c
[+] Found encrypted data at address 00004000 of length 19841024 bytes - type 1.
[+] Opening /private/var/mobile/Containers/Bundle/Application/B4C52234-47C1-41A9-B23F-EC2224BC5E30/Weibo.app/Weibo for reading.
[+] Reading header
[+] Detecting header type
[+] Executable is a FAT image - searching for right architecture
[+] Correct arch is at offset 16384 in the file
[+] Opening Weibo.decrypted for writing.
[+] Copying the not encrypted start of the file
[+] Dumping the decrypted data into the file
[+] Copying the not encrypted remainder of the file
[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset 4a4c
[+] Closing original file
[+] Closing dump file

先把Weibo.decrypted丢到IDA里开始分析,然后进行下面的操作。

3. class-dump之

snakeninnys-MacBook:~ snakeninny$ class-dump -S -s -H /Users/snakeninny/Desktop/Weibo.decrypted -o /Users/snakeninny/Code/RE/新浪微博
2015-04-02 19:54:43.881 class-dump[865:32498] *** Assertion failure in -[CDObjectiveC2Processor createUniquedProtocols], /Volumes/Lion/Users/nygard/Source/git/me/Tools/class-dump/Source/CDObjectiveCProcessor.m:277
2015-04-02 19:54:43.883 class-dump[865:32498] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: [[p1 optionalInstanceMethods] count] == 0 || [[uniqueProtocol optionalInstanceMethods] count] == [[p1 optionalInstanceMethods] count]'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff9419466c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff9660976e objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff9419444a +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff9295e3a9 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
	4   class-dump                          0x0000000102d9cbc2 class-dump + 146370
	5   class-dump                          0x0000000102d9f0b9 class-dump + 155833
	6   class-dump                          0x0000000102d9b40c class-dump + 140300
	7   class-dump                          0x0000000102d7b283 class-dump + 8835
	8   class-dump                          0x0000000102d8ac01 class-dump + 72705
	9   class-dump                          0x0000000102d7a744 class-dump + 5956
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6

注意,class-dump失败了。这到底是class-dump的bug还是微博的防护,我还没有深究;一般情况下,class-dump不行就换class-dump-z,两者的用法是完全一样的:

snakeninnys-MacBook:~ snakeninny$ class-dump-z -S -s -H /Users/snakeninny/Desktop/Weibo.decrypted -o /Users/snakeninny/Code/RE/新浪微博

我们一共得到了4159个头文件,如图所示:
image

三 用Cycript找到转发界面及其delegate

转发界面如图所示:


开始寻找其V和C:

FunMaker-5:~ root# cycript -p Weibo
cy# ?expand
expand == true
cy# [[UIApp keyWindow] recursiveDescription]
@"<WBAppWindow: 0x166e9250; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x165eaff0>; layer = <UIWindowLayer: 0x166e94f0>>
……
   |    |    |    |    |    |    |    |    |    |    |    | <WBTimelineAttributedTextView: 0x17bca250; frame = (58 48; 250 43); opaque = NO; layer = <WBAsyncDrawingViewLayer: 0x17bca300>>
   |    |    |    |    |    |    |    |    |    |    |    | <UILabel: 0x17bccd20; frame = (135.5 31; 54 12); text = 'Repost 266'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x17bca5a0>>
……
cy# 

看到截图和输出中相同的“Repost 266”了么?咱们就从这里入手吧。调用

cy# [#0x17bccd20 setHidden:YES]

转发界面变成了:


到目前为止,我们的分析准确无误。继续分析,调用:

cy# [#0x17bccd20 setHidden:NO]
cy# [#0x17bccd20 superview]
#"<UITableViewCellContentView: 0x17bcb4d0; frame = (0 0; 320 98); gestureRecognizers = <NSArray: 0x17bb8010>; layer = <CALayer: 0x17bf6bf0>>"
cy# [#0x17bcb4d0 setHidden:YES]

转发界面变成了:


但是,UITableViewCellContentView貌似只是一个cell,而不是整个转发界面的table view。继续看看它的superview:

cy# [#0x17bcb4d0 setHidden:NO]
cy# [#0x17bcb4d0 superview]
#"<WBRepostListTableViewCell: 0x17bc66b0; baseClass = UITableViewCell; frame = (0 592; 320 98); opaque = NO; autoresize = W; layer = <CALayer: 0x17bd8e60>>"
cy# [#0x17bc66b0 superview]
#"<UITableViewWrapperView: 0x17be7940; frame = (0 0; 320 504); gestureRecognizers = <NSArray: 0x17be7be0>; layer = <CALayer: 0x17be7b20>; contentOffset: {0, 0}; contentSize: {320, 504}>"
cy# [#0x17be7940 superview]
#"<WBPlaceholderTableView: 0x16c6f000; baseClass = UITableView; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x17be76b0>; layer = <CALayer: 0x17be7450>; contentOffset: {0, 55.5}; contentSize: {320, 4428}; contentInset = {64, 0, 44, 0}; contentSize = {320, 4428}>"

貌似定位到了一个table view。看看它的delegate是什么:

cy# [#0x16c6f000 delegate]
#"<WBRepostListViewController: 0x17daf370>"

好了,我们到WBRepostListViewController.h里看看:

/**
 * This header is generated by class-dump-z 0.2a.
 * class-dump-z is Copyright (C) 2009 by KennyTM~, licensed under GPLv3.
 *
 * Source: (null)
 */

#import "Weibo-Structs.h"
#import "WBRepostListTableViewCellDelegate.h"
#import "WBStatusBusinessViewController.h"
#import "UIAlertViewDelegate.h"
#import "WBProgressSheetDelegate.h"

@class WBRepostEngine, NSString;

__attribute__((visibility("hidden")))
@interface WBRepostListViewController : WBStatusBusinessViewController <WBRepostListTableViewCellDelegate, UIAlertViewDelegate, WBProgressSheetDelegate> {
@private
	WBRepostEngine* repostEngine;
}
@property(readonly, copy) NSString* debugDescription;
@property(readonly, copy) NSString* description;
@property(readonly, assign) unsigned hash;
@property(readonly, assign) Class superclass;
-(void)alertView:(id)view didDismissWithButtonIndex:(int)buttonIndex;
-(void)cell:(id)cell didPressAvatarViewWithUser:(id)user;
-(void)dealloc;
-(void)didPressUserScreenNameLabel:(id)label inCell:(id)cell;
-(float)heightForRowAtIndexPath:(id)indexPath;
-(void)initStatusBusinessEngine;
-(id)initWithNibName:(id)nibName bundle:(id)bundle;
-(void)reportRepost:(id)repost;
-(void)repostDidFinishSending:(id)repost;
-(BOOL)shouldHighlightRowAtIndexPath:(id)indexPath;
-(id)tableView:(id)view cellForRowAtIndexPath:(id)indexPath;
-(void)tableView:(id)view didSelectRowAtIndexPath:(id)indexPath;
-(void)tableView:(id)view willDisplayCell:(id)cell forRowAtIndexPath:(id)indexPath;
-(BOOL)tableViewHasData:(id)data;
@end

这里没有scroll函数。去它的父类里看看,打开WBStatusBusinessViewController.h:

/**
 * This header is generated by class-dump-z 0.2a.
 * class-dump-z is Copyright (C) 2009 by KennyTM~, licensed under GPLv3.
 *
 * Source: (null)
 */

#import "Weibo-Structs.h"
#import "UITableViewDelegate.h"
#import "UITableViewDataSource.h"
#import <XXUnknownSuperclass.h> // Unknown library

@class UIViewController, NSMutableArray, WBStatusBusinessEngine, NSString, WBPlaceholderTableView, NSError, WBStatus;
@protocol WBStatusBusinessViewControllerDelegate;

__attribute__((visibility("hidden")))
@interface WBStatusBusinessViewController : XXUnknownSuperclass <UITableViewDelegate, UITableViewDataSource> {
@private
	WBStatus* status;
	NSMutableArray* list;
	BOOL hasMore;
	BOOL hasReloaded;
	WBStatusBusinessEngine* businessEngine;
	NSString* errorPrompt;
	WBPlaceholderTableView* rootTableView;
	CGPoint rootTableViewContentOffset;
	UIViewController* rootViewController;
	id<WBStatusBusinessViewControllerDelegate> delegate;
	BOOL isLoadedError;
	unsigned hotCellIndex;
	BOOL isLoaingMore;
	NSError* loadError;
	BOOL _isNotHaveReloadImage;
	NSString* emptyViewImageName;
	NSString* emptyViewDescription;
}
@property(readonly, copy) NSString* debugDescription;
@property(assign, nonatomic) id<WBStatusBusinessViewControllerDelegate> delegate;
@property(readonly, copy) NSString* description;
@property(retain, nonatomic) NSString* emptyViewDescription;
@property(retain, nonatomic) NSString* emptyViewImageName;
@property(assign, nonatomic) BOOL hasMore;
@property(readonly, assign, nonatomic) BOOL hasReloaded;
@property(readonly, assign) unsigned hash;
@property(assign, nonatomic) unsigned hotCellIndex;
@property(assign, nonatomic) BOOL isNotHaveReloadImage;
@property(assign, nonatomic) WBPlaceholderTableView* rootTableView;
@property(assign, nonatomic) CGPoint rootTableViewContentOffset;
@property(assign, nonatomic) UIViewController* rootViewController;
@property(retain, nonatomic) WBStatus* status;
@property(readonly, assign) Class superclass;
-(void)dealloc;
-(void)forceReloadData;
-(BOOL)hasNoData;
-(float)heightForRowAtIndexPath:(id)indexPath;
-(void)hideAdInMessageContentViewController;
-(void)initStatusBusinessEngine;
-(id)initWithNibName:(id)nibName bundle:(id)bundle;
-(id)initWithRootViewController:(id)rootViewController;
-(void)loadMoreData;
-(int)numberOfDataRows;
-(int)numberOfNonePlaceholderRows;
-(void)reloadData;
-(void)scrollViewDidEndDecelerating:(id)scrollView;
-(void)scrollViewDidEndDragging:(id)scrollView willDecelerate:(BOOL)decelerate;
-(void)scrollViewDidScroll:(id)scrollView;
-(void)scrollViewWillBeginDragging:(id)scrollView;
-(void)showAdInMessageContentViewController;
-(unsigned)supportedInterfaceOrientations;
-(id)tableView:(id)view cellForRowAtIndexPath:(id)indexPath;
-(void)tableView:(id)view didSelectRowAtIndexPath:(id)indexPath;
-(float)tableView:(id)view heightForHeaderInSection:(int)section;
-(float)tableView:(id)view heightForRowAtIndexPath:(id)indexPath;
-(int)tableView:(id)view numberOfRowsInSection:(int)section;
-(id)tableView:(id)view viewForHeaderInSection:(int)section;
-(BOOL)tableViewHasData;
-(BOOL)tableViewIsLoading:(id)loading;
-(void)triggerLoadMore;
-(void)viewDidLoad;
-(void)viewDidUnload;
@end

我们看到了熟悉的

-(void)scrollViewDidEndDecelerating:(id)scrollView;
-(void)scrollViewDidEndDragging:(id)scrollView willDecelerate:(BOOL)decelerate;
-(void)scrollViewDidScroll:(id)scrollView;
-(void)scrollViewWillBeginDragging:(id)scrollView;

及可疑的

@property(assign, nonatomic) BOOL hasMore;
-(void)loadMoreData;
-(void)triggerLoadMore;

此时IDA对Weibo.decrypted的分析应该已经结束了,咱们转战IDA吧。

四 在WBStatusBusinessViewController中找到“加载更多转发者”功能的函数

刚刚看到的2个可疑函数会在微博“加载更多转发者”时得到调用吗?我们先看看[WBStatusBusinessViewController triggerLoadMore]


这货很可疑啊!上LLDB测测看:

FunMaker-5:~ root# debugserver *:1234 -a "Weibo"
debugserver-@(#)PROGRAM:debugserver  PROJECT:debugserver-320.2.89
 for armv7.
Attaching to process Weibo...
Listening to port 1234 for a connection from *...
snakeninnys-MacBook:~ snakeninny$ /Applications/OldXcode.app/Contents/Developer/usr/bin/lldb 
(lldb) process connect connect://localhost:1234
2015-04-02 20:51:58.053 lldb[1148:58079] Metadata.framework [Error]: couldn't get the client port
Process 171529 stopped
* thread #1: tid = 0x29e09, 0x3179f4f0 libsystem_kernel.dylib`mach_msg_trap + 20, queue = 'com.apple.main-thread, stop reason = signal SIGSTOP
    frame #0: 0x3179f4f0 libsystem_kernel.dylib`mach_msg_trap + 20
libsystem_kernel.dylib`mach_msg_trap + 20:
-> 0x3179f4f0:  pop    {r4, r5, r6, r8}
   0x3179f4f4:  bx     lr

libsystem_kernel.dylib`mach_msg_overwrite_trap:
   0x3179f4f8:  mov    r12, sp
   0x3179f4fc:  push   {r4, r5, r6, r8}
(lldb) c
Process 171529 resuming
(lldb) image list -o -f
[  0] 0x0009e000 /private/var/mobile/Containers/Bundle/Application/B4C52234-47C1-41A9-B23F-EC2224BC5E30/Weibo.app/Weibo(0x00000000000a2000)
……
(lldb) br s -a '0x0009e000+0x153A58'
Breakpoint 3: where = Weibo`-[WBStatusBusinessViewController triggerLoadMore], address = 0x001f1a58
Process 171529 stopped
* thread #1: tid = 0x29e09, 0x001f1a58 Weibo`-[WBStatusBusinessViewController triggerLoadMore], queue = 'com.apple.main-thread, stop reason = breakpoint 3.1
    frame #0: 0x001f1a58 Weibo`-[WBStatusBusinessViewController triggerLoadMore]
Weibo`-[WBStatusBusinessViewController triggerLoadMore]:
-> 0x1f1a58:  movw   r1, #53232
   0x1f1a5c:  movt   r1, #323
   0x1f1a60:  add    r1, pc
   0x1f1a62:  ldr    r1, [r1]

它应该就是“加载更多转发者”函数了 :relaxed:

五 从WBStatusBusinessViewController中获取所有转发内容

因为刚刚我们从WBPlaceholderTableView入手,找到了它的delegate,即WBRepostListViewController;接下来我们看看WBPlaceholderTableViewdataSource是什么:

cy# [#0x179d9600 dataSource]
#"<WBRepostListViewController: 0x18315f90>"

还是WBRepostListViewController。没什么好多说的,我们去IDA里看看[WBRepostListViewController tableView:cellForRowAtIndexPath:]。大致浏览一下这个函数,发现一个可疑的NSMutableArray:


看看它的值:

cy# #0x18315f90->list
@[#"<WBStatus 0x1830fdc0\n\tcard = (null)\n\tcontentTimelineTrendItem = (null)\n\tsource = (null)\n\tsourceUrl = (null)\n\tsourceUrlEnable = 0\n\tsourceScheme = (null)\n\tfavorID = (null)\n\trid = (null)\n\tthumbnailImageURL = (null)\n\tmiddleImageURL = (null)\n\toriginalImageURL = (null)\n\twpInfo = (null)\n\tcomplaintInfo = (null)\n\tfavorited = 0\n\tretweetCount = 265\n\tcommentCount = 0\n\tlikeCount = 0\n\tisKeepTop = 0\n\tstatusType = 0\n\tstatusTypeName = (null)\n\trecentComments = (null)\n\tlocation =\n\t\tlongitude = 0\n\t\tlatitude = 0\n\tdistance = 0\n\tadMark = (null)\n\tsmartFeedID = (null)\n\tfeedGroupID = (null)\n\tdirectionGroupID = (null)\n\tstatusLikeState = 0\n\toriginalDate = (null)\n\tpictures = (null)\n\tbuttonModels = (null)\n\tdynamicButtons = (null)\n\tdynamicMenus = (null)\n\tretweetByStatus = (null)\n\trecom_state = 0\n\tdeleted = 0\n\tblockType = 0\n\tmlevel = 0\n\tscheme = (null)\n\tweiboIndex = 0\n\tbgPic = (null)\n\treadCount = -1\n\tdrawingContext = (null)\n\tclientTask = (null)\n\titemID = 3826737303640308\n\tv4ItemID = 3826737303640308\n\ttext = \xe7\x9c\x8b\xe5\x88\xb0\xe8\xbf\x99\xe7\xbe\xa4\xe4\xb8\xad\xe4\xba\x8c\xe9\x9d\x92\xe5\xb9\xb4\xef\xbc\x8c\xe6\x88\x91\xe5\xb0\xb1\xe7\x9f\xa5\xe9\x81\x93\xe6\x94\xb6\xe5\xa4\x8d\xe5\x8f\xb0\xe6\xb9\xbe\xe6\x9c\x89\xe6\x9c\x9b\xe4\xba\x86\xe3\x80\x82\n\tdate = 2015-04-01 00:38:52 +0000\n\tuser = <WBUser 0x186f5870\n\t\tgender = 1\n\t\tgenderString = m\n\t\tdesc = \xe7\x94\x9f\xe6\xb4\xbb\xe4\xb8\x8d\xe6\xad\xa2\xe7\x9c\xbc\xe5\x89\x8d\xe7\x9a\x84\xe8\x8b\x9f\xe4\xb8\x94\xef\xbc\x8c\xe8\xbf\x98\xe6\x9c\x89\xe8\xaf\x97\xe5\x92\x8c\xe8\xbf\x9c\xe6\x96\xb9\xe7\x9a\x84\xe7\x94\xb0\xe9\x87\x8e\xe3\x80\x82\n\t\tdomain = \n\t\tfollowersCount = 874\n\t\tfollowingCount = 209\n\t\tfriendsCount = 0\n\t\tfavoritesCount = 1\n\t\tstatusesCount = 220\n\t\ttopicsCount = 0\n\t\tblockedCount = 0\n\t\tprovince = \xe5\xb9\xbf\xe8\xa5\xbf\n\t\tcity = \xe5\x8d\x97\xe5\xae\x81\n\t\tverifiedReason = \xe5\xb0\x8f\xe4\xb8\x83\xe5\x88\x9b\xe6\x84\x8f\xe5\xb7\xa5\xe4\xbd\x9c\xe5\xae\xa4\xe6\x89\xa7\xe8\xa1\x8c\xe6\x80\xbb\xe7\x9b\x91\n\t\tpossiblyKnownType = 0\n\t\tpossiblyKnownReason = (null)\n\t\tlastMembershipType = 0\n\t\teventIconURL = (null)\n\t\tuserIconsInfo = (null)\n\t\tenterpriseAppCount = 0\n\t\tenterpriseAppsURL = (null)\n\t\tweihao = \n\t\tcreattime = 2011-11-12 02:52:21 +0000\n\t\tcreditScore = 80\n\t\tmedals = (null)\n\t\turank = 16\n\t\tbirthday = (null)\n\t\tmail = (null)\n\t\tblogAddress = \n\t\tqqNumber = (null)\n\t\tmsnNumber = (null)\n\t\teducationInfo = (null)\n\t\tcompanyInfo = (null)\n\t\tbadgeDic =\n\t\t\tgongyi_level = 1\n\t\t\thongbao_2014 = 1\n\t\t\tsuishoupai_2014 = 0\n\t\t\tuc_domain = 0\n\t\t\ttravel2013 = 1\n\t\t\tgongyi = 0\n\t\t\tbind_taobao = 1\n\t\t\tdailv = 0\n\t\t\tzongyiji = 0\n\t\t\tenterprise = 0\n\t\t\tzjyx_2015 = 0\n\t\t\tanniversary = 0\n\t\t\ttaobao = 0\n\t\tmbprivilege = 0000000000000000000000000000000000000000000000000000000000000000\n\t\tcoverUrl = http://ww3.sinaimg.cn/crop.0.0.640.640.640/6ce2240djw1e9oaaziu7sj20hs0hs0ui.jpg\n\t\ttmpCoverUrl = (null)\n\t\tcustomCover = 0\n\t\tcustomBackground = 0\n\t\tcanCustomWeiboSource = 0\n\t\tcoverImagePhoneLevel = 0\n\t\tlocationRightsType = 0\n\t\tuserID = 2460221455\n\t\tscreenName = \xe8\xaf\x97\xe6\xb0\xb4\xe5\x85\x88\xe7\x94\x9f\n\t\tprofileImageURL = http://tp4.sinaimg.cn/2460221455/50/5715479309/1\n\t\tavatarLargeURL = http://tp4.sinaimg.cn/2460221455/180/5715479309/1\n\t\tavatarHdURL = http://ww3.sinaimg.cn/crop.0.0.640.640.1024/92a4000fjw8enzlnkx4yjj20hs0hsq3x.jpg\n\t\tremark = \n\t\textendedDescription = (null)\n\t\tphoneName = (null)\n\t\tphoneNumber = (null)\n\t\tphoneNumbers = (null)\n\t\trelationship = 0\n\t\torigin = (null)\n\t\tmeyouRelationship = -1\n\t\tverifiedType = 1\n\t\toriginalVerifiedType = 0\n\t\tpermission = 1\n\t\tlastUsedTime = -1\n\t\tlastMessageTime = -1\n\t\tsortName = ssxs\n\t\tsortSectionName = s\n\t\tsearchString = \xe8\xaf\x97\xe6\xb0\xb4\xe5\x85\x88\xe7\x94\x9f ssxs shishuixiansheng \n\t\tonlineIndex = 0\n\t\tcontactType = 1\n\t\tuserInfo =\n\t\tlastStatusContent = (null)\n\t\tmembershipType = 12\n\t\tmembershipRank = 2\n\t\tisPage = 0\n\t\tscheme = (null)\n\t\tsortNameWithRemark = ssxs\n\t\tsortSectionNameWithRemark = s>\n\tquotedItem = <WBStatus 0x186b7b50\n\t\tcard = (null)\n\t\tcontentTimelineTrendItem = (null)\n\t\tsource = (null)\n\t\tsourceUrl = (null)\n\t\tsourceUrlEnable = 0\n\t\tsourceScheme = (null)\n\t\tfavorID = (null)\n\t\trid = (null)\n\t\tthumbnailImageURL = (null)\n\t\tmiddleImageURL = (null)\n\t\toriginalImageURL = (null)\n\t\twpInfo = (null)\n\t\tcomplaintInfo = (null)\n\t\tfavorited = 0\n\t\tretweetCount = 2229\n\t\tcommentCount = 0\n\t\tlikeCount = 0\n\t\tisKeepTop = 0\n\t\tstatusType = 0\n\t\tstatusTypeName = (null)\n\t\trecentComments = (null)\n\t\tlocation =\n\t\t\tlongitude = 0\n\t\t\tlatitude = 0\n\t\tdistance = 0\n\t\tadMark = (null)\n\t\tsmartFeedID = (null)\n\t\tfeedGroupID = (null)\n\t\tdirectionGroupID = (null)\n\t\tstatusLikeState = 0\n\t\toriginalDate = (null)\n\t\tpictures = (null)\n\t\tbuttonModels = (null)\n\t\tdynamicButtons = (null)\n\t\tdynamicMenus = (null)\n\t\tretweetByStatus = <WBStatus 0x1830fdc0>\n\t\trecom_state = 0\n\t\tdeleted = 0\n\t\tblockType = 0\n\t\tmlevel = 0\n\t\tscheme = (null)\n\t\tweiboIndex = 0\n\t\tbgPic = (null)\n\t\treadCount = -1\n\t\tdrawingContext = (null)\n\t\tclientTask = (null)\n\t\titemID = 3826732794159102\n\t\tv4ItemID = 3826732794159102\n\t\ttext = \xe3\x80\x90\xe5\x8f\xb0\xe6\xb9\xbe\xe9\x9d\x92\xe5\xb9\xb4\xe5\x9b\xa2\xe4\xbd\x93\xe7\xaa\x81\xe8\xa2\xad\xe2\x80\x9c\xe6\x80\xbb\xe7\xbb\x9f\xe5\xba\x9c\xe2\x80\x9d \xe6\x8a\x97\xe8\xae\xae\xe5\x8a\xa0\xe5\x85\xa5\xe4\xba\x9a\xe6\x8a\x95\xe8\xa1\x8c\xe3\x80\x91\xe6\x98\xa8\xe6\x99\x9a\xef\xbc\x8c7\xe4\xb8\xaa\xe5\x8f\xb0\xe6\xb9\xbe\xe9\x9d\x92\xe5\xb9\xb4\xe5\x9b\xa2\xe4\xbd\x93\xe8\xbf\x9e\xe5\xa4\x9c\xe8\xb5\xb6\xe5\x88\xb0\xe5\x8f\xb0\xe5\x8c\x97\xe2\x80\x9c\xe6\x80\xbb\xe7\xbb\x9f\xe5\xba\x9c\xe2\x80\x9d\xe9\x97\xa8\xe5\x8f\xa3\xe7\x9a\x84\xe5\x87\xaf\xe8\xbe\xbe\xe6\xa0\xbc\xe5\x85\xb0\xe5\xa4\xa7\xe9\x81\x93\xe6\x8a\x97\xe8\xae\xae\xef\xbc\x8c\xe7\xa7\xb0\xe5\x8f\xb0\xe6\xb9\xbe\xe7\x94\xb3\xe8\xaf\xb7\xe5\x8a\xa0\xe5\x85\xa5\xe4\xba\x9a\xe6\x8a\x95\xe8\xa1\x8c\xe6\x98\xaf\xe2\x80\x9c\xe9\xbb\x91\xe7\xae\xb1\xe2\x80\x9d\xe6\x93\x8d\xe4\xbd\x9c\xef\xbc\x8c\xe6\x8f\x90\xe5\x87\xba\xe2\x80\x9c\xe6\x8b\x92\xe7\xbb\x9d\xe2\x80\x98\xe4\xb8\xbb\xe6\x9d\x83\xe2\x80\x99\xe7\x9f\xae\xe5\x8c\x96\xe2\x80\x9d\xe3\x80\x81\xe2\x80\x9c\xe7\xa8\x8b\xe5\xba\x8f\xe5\x85\xac\xe5\xbc\x80\xe9\x80\x8f\xe6\x98\x8e\xef\xbc\x8c\xe5\x87\x9d\xe8\x81\x9a\xe7\xa4\xbe\xe4\xbc\x9a\xe5\x85\xb1\xe8\xaf\x86\xe2\x80\x9d\xe3\x80\x81\xe2\x80\x9c\xe6\xb0\x91\xe6\x84\x8f\xe5\x9f\xba\xe7\xa1\x80\xe4\xb8\x8d\xe8\xb6\xb3\xef\xbc\x8c\xe5\x81\x9c\xe6\xad\xa2\xe6\x94\xbf\xe7\xbb\x8f\xe6\x95\xb4\xe5\xb9\xb6\xe2\x80\x9d\xe7\xad\x89\xe8\xaf\x89\xe6\xb1\x82\xe3\x80\x82\xe8\xaf\xa6\xe8\xa7\x81\xef\xbc\x9ahttp://t.cn/RAqeaQo\n\t\tdate = 2015-04-01 00:20:58 +0000\n\t\tuser = <WBUser 0x18256940\n\t\t\tgender = 2\n\t\t\tgenderString = f\n\t\t\tdesc = \xe6\xaf\x8f\xe6\x97\xa5\xe6\x92\xad\xe6\x8a\xa5\xe5\x85\xa8\xe7\x90\x83\xe5\x90\x84\xe7\xb1\xbb\xe9\x87\x8d\xe8\xa6\x81\xe8\xb5\x84\xe8\xae\xaf\xe3\x80\x81\xe7\xaa\x81\xe5\x8f\x91\xe6\x96\xb0\xe9\x97\xbb\xef\xbc\x8c\xe5\x85\xa8\xe5\xa4\xa924\xe5\xb0\x8f\xe6\x97\xb6\xe5\x8d\xb3\xe6\x97\xb6\xe5\x8f\x91\xe5\xb8\x83\xe3\x80\x82\xe6\xac\xa2\xe8\xbf\x8e\xe6\x8a\xa5\xe6\x96\x99\xe3\x80\x81\xe6\x8a\x95\xe7\xa8\xbf\xef\xbc\x8c\xe8\xaf\xb7\xe5\x8f\x91\xe7\xa7\x81\xe4\xbf\xa1\xe6\x88\x96\xe8\x80\x85\xe9\x82\xae\xe4\xbb\xb6\xef\xbc\x9axlttnews@vip.sina.com\xe3\x80\x82\n\t\t\tdomain = breakingnews\n\t\t\tfollowersCount = 39966930\n\t\t\tfollowingCount = 321\n\t\t\tfriendsCount = 0\n\t\t\tfavoritesCount = 30\n\t\t\tstatusesCount = 102787\n\t\t\ttopicsCount = 0\n\t\t\tblockedCount = 0\n\t\t\tprovince = \xe5\x8c\x97\xe4\xba\xac\n\t\t\tcity = \xe6\xb5\xb7\xe6\xb7\x80\xe5\x8c\xba\n\t\t\tverifiedReason = \xe6\x96\xb0\xe6\xb5\xaa\xe6\x96\xb0\xe9\x97\xbb\xe4\xb8\xad\xe5\xbf\x8324\xe5\xb0\x8f\xe6\x97\xb6\xe6\x92\xad\xe6\x8a\xa5\xe5\x85\xa8\xe7\x90\x83\xe9\x87\x8d\xe5\xa4\xa7\xe6\x96\xb0\xe9\x97\xbb\n\t\t\tpossiblyKnownType = 0\n\t\t\tpossiblyKnownReason = (null)\n\t\t\tlastMembershipType = 0\n\t\t\teventIconURL = (null)\n\t\t\tuserIconsInfo = (null)\n\t\t\tenterpriseAppCount = 0\n\t\t\tenterpriseAppsURL = (null)\n\t\t\tweihao = \n\t\t\tcreattime = 2009-08-28 08:34:36 +0000\n\t\t\tcreditScore = 80\n\t\t\tmedals = (null)\n\t\t\turank = 28\n\t\t\tbirthday = (null)\n\t\t\tmail = (null)\n\t\t\tblogAddress = http://news.sina.com.cn/\n\t\t\tqqNumber = (null)\n\t\t\tmsnNumber = (null)\n\t\t\teducationInfo = (null)\n\t\t\tcompanyInfo = (null)\n\t\t\tbadgeDic =\n\t\t\t\tgongyi_level = 0\n\t\t\t\thongbao_2014 = 1\n\t\t\t\tsuishoupai_2014 = 0\n\t\t\t\tuc_domain = 0\n\t\t\t\ttravel2013 = 1\n\t\t\t\tgongyi = 0\n\t\t\t\tbind_taobao = 0\n\t\t\t\tdailv = 0\n\t\t\t\tzongyiji = 1\n\t\t\t\tenterprise = 1\n\t\t\t\tzjyx_2015 = 0\n\t\t\t\tanniversary = 0\n\t\t\t\ttaobao = 0\n\t\t\tmbprivilege = 0000000000000000000000000000000000000000000000000000000000000000\n\t\t\tcoverUrl = (null)\n\t\t\ttmpCoverUrl = (null)\n\t\t\tcustomCover = 0\n\t\t\tcustomBackground = 0\n\t\t\tcanCustomWeiboSource = 0\n\t\t\tcoverImagePhoneLevel = 0\n\t\t\tlocationRightsType = 0\n\t\t\tuserID = 1618051664\n\t\t\tscreenName = \xe5\xa4\xb4\xe6\x9d\xa1\xe6\x96\xb0\xe9\x97\xbb\n\t\t\tprofileImageURL = http://tp1.sinaimg.cn/1618051664/50/5661558324/0\n\t\t\tavatarLargeURL = http://tp1.sinaimg.cn/1618051664/180/5661558324/0\n\t\t\tavatarHdURL = http://tp1.sinaimg.cn/1618051664/180/5661558324/0\n\t\t\tremark = \n\t\t\textendedDescription = (null)\n\t\t\tphoneName = (null)\n\t\t\tphoneNumber = (null)\n\t\t\tphoneNumbers = (null)\n\t\t\trelationship = 0\n\t\t\torigin = (null)\n\t\t\tmeyouRelationship = -1\n\t\t\tverifiedType = 1026\n\t\t\toriginalVerifiedType = 3\n\t\t\tpermission = 1\n\t\t\tlastUsedTime = -1\n\t\t\tlastMessageTime = -1\n\t\t\tsortName = ttxw\n\t\t\tsortSectionName = t\n\t\t\tsearchString = \xe5\xa4\xb4\xe6\x9d\xa1\xe6\x96\xb0\xe9\x97\xbb ttxw toutiaoxinwen \n\t\t\tonlineIndex = 0\n\t\t\tcontactType = 5\n\t\t\tuserInfo =\n\t\t\tlastStatusContent = (null)\n\t\t\tmembershipType = 0\n\t\t\tmembershipRank = 0\n\t\t\tisPage = 0\n\t\t\tscheme = (null)\n\t\t\tsortNameWithRemark = ttxw\n\t\t\tsortSectionNameWithRemark = t>\n\t\tquotedItem = (null)\n\t\tsentByMe = 0\n\t\tisNew = 0\n\t\tdrawingOption = 0\n\t\turls = (null)\n\t\ttopics = (null)\n\t\ttags = (null)\n\t\ttmpTags = (null)\n\t\ttitleInfos = (null)\n\t\tkeywords = (null)\n\t\tpageInfo = (null)\n\t\tmultiMedias = (null)\n\t\tclientItemID = (null)\n\t\tclientTaskID = 0\n\t\tactionTypesDelegate = (null)\n\t\tsaveTime = 0\n\t\ttimestampInfo = (null)>\n\tsentByMe = 0\n\tisNew = 0\n\tdrawingOption = 0\n\turls = (null)\n\ttopics = (null)\n\ttags = (null)\n\ttmpTags = (null)\n\ttitleInfos = (null)\n\tkeywords = (null)\n\tpageInfo = (null)\n\tmultiMedias = (null)\n\tclientItemID = (null)\n\tclientTaskID = 0\n\tactionTypesDelegate = (null)\n\tsaveTime = 0\n\ttimestampInfo = (null)>"
……

看到这么多unicode字符,就知道这应该是一条微博的信息。验证一下:

cy# [[#0x18315f90->list objectAtIndex:0] class]
WBStatus

看看WBStatus.h

@interface WBStatus : WBUniversalStatus <WBComposeSharingObject> {

再看看WBUniversalStatus.h

@interface WBUniversalStatus : WBTimelineItem {

还记得书上第9章中出现的关键词“Timeline”吗?这里又出现了 :smirk:
继续看看WBTimelineItem.h

@interface WBTimelineItem : WBModelObject <WBDatabaseModel> {

WBModelObject的名字就已经很说明问题了。同时,我们在WBTimelineItem.h中发现了

@property(retain, nonatomic) NSString* text;

应该就是转发内容了吧!试试:

cy# [[[#0x18315f90->list objectAtIndex:0] text] length]
21
cy# [[[#0x18315f90->list objectAtIndex:1] text] length]
4

字数跟前2条微博的字数相同,如图所示:


最后一个任务,看看转发者是不是@iOS应用逆向工程的粉丝。

六 从WBUser中获取所有转发内容

WBTimelineItem.h中有个

@property(retain, nonatomic) WBUser* user;

想必就是转发者了。到WBUser.h里看看有没有粉丝信息,在其父类WBContact.h里发现了:

@property(readonly, assign, nonatomic) BOOL isFollower;
@property(readonly, assign, nonatomic) BOOL isFollowing;

哪个是我粉他,哪个是他粉我呢?我关注了这位“高铁见闻”,而他没有关注我,因此咱们试试就知道了:

cy# [[[#0x19286440->list objectAtIndex:1] user] isFollowing]
1
cy# [[[#0x19286440->list objectAtIndex:1] user] isFollower]
0

看来isFollowing是我有没有粉他;isFollower是他有没有粉我,后者就是我们的目标函数。
至此,2个小任务完成,咱们像书上一样先整理一下,再写代码。

七 逆向结果整理

这次的实战针对的是StoreApp,相对简单,如果你看懂了书上的4个实例再看这个帖子,就会发现我们还是在用书上总结出的方法论在分析,万变不离其宗。

1. 寻找“加载更多转发者”函数

因为“加载更多”的功能是通过滑动上提转发界面的操作实现的,而滑动操作是iOS提供的标准手势,其接口一般就是UIScrollViewDelegate中定义的那几个回调函数。我们通过定位转发界面,然后找出其delegate的方法,成功定位到了WBRepostListViewController类,并在其父类头文件中找到了可疑的目标函数。经测试,它们就是“加载更多转发者”函数,任务1完成。

2. 读取所有转发内容

因为所有的转发内容都是以UITableViewCell形式展现的,所以在转发界面的数据源中,一定可以找到读取所有转发内容的方法。顺着这个思路,我们首先定位到了转发界面的数据源,即WBRepostListViewController类,然后用IDA在- (id)tableView:(id)view cellForRowAtIndexPath:(id)indexPath;的实现中找到了保存所有转发微博的数组NSMutableArray *list,成功搞定任务2。

八 编写tweak

1. 用Theos新建tweak工程“iOSRELottery2”

snakeninnys-MacBook:Code snakeninny$ /opt/theos/bin/nic.pl
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/cydget
  [3.] iphone/framework
  [4.] iphone/library
  [5.] iphone/notification_center_widget
  [6.] iphone/preference_bundle
  [7.] iphone/sbsettingstoggle
  [8.] iphone/tool
  [9.] iphone/tweak
  [10.] iphone/xpc_service
Choose a Template (required): 9
Project Name (required): iOSRELottery2
Package Name [com.yourcompany.iosrelottery2]: com.iosre.iosrelottery2
Author/Maintainer Name [snakeninny]: snakeninny
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.sina.weibo
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: Weibo
Instantiating iphone/tweak in iosrelottery2/...
Done.

2. 构造iOSRELottery2.h

@interface WBStatusBusinessViewController : NSObject
{
	NSMutableArray* list;
	BOOL isLoaingMore; // 注意,这里写微博客户端的朋友把单词给写错了 :)
}
@property (assign, nonatomic) BOOL hasMore;
- (void)loadMoreData;
- (void)triggerLoadMore;
@end

@interface WBRepostListViewController : WBStatusBusinessViewController
@end

@interface WBContact : NSObject
@property (readonly, assign, nonatomic) BOOL isFollower;
@property (readonly, retain, nonatomic) NSString* displayName;
@end

@interface WBUser : WBContact
@end

@interface WBTimelineItem : NSObject
@property (retain, nonatomic) NSString* text;
@property (retain, nonatomic) WBUser* user;
@end

@interface WBUniversalStatus : WBTimelineItem
@end

@interface WBStatus : WBUniversalStatus
@end

@interface UIApplication ()
- (void)terminateWithSuccess;
@end

@interface NSSet (iOSRELottery2)
- (BOOL)iosreContainsDuplicateUser:(WBStatus *)weibo;
@end

这个头文件的所有内容均摘自类对应的头文件,构造它的目的仅仅是通过编译,避免任何报错和警告。

3. 编辑Tweak.xm

#import "iOSRELottery2.h"
#include <substrate.h>

static NSTimer *countdownTimer;
static NSTimer *loadMoreTimer;

@implementation NSSet (iOSRELottery2)
- (BOOL)iosreContainsDuplicateUser:(WBStatus *)weibo
{
	for (WBStatus *status in self)
		if ([status.user.displayName isEqualToString:weibo.user.displayName] || [status.user.displayName isEqualToString:@"hangcom2010"] || [status.user.displayName isEqualToString:@"iOS应用逆向工程"])
			return YES;
	return NO;
}
@end

%hook WBRepostListViewController
%new
- (void)iosreLoadMoreReposts
{
	NSMutableArray *allReposts = MSHookIvar<NSMutableArray *>(self, "list");
	if (self.hasMore)
	{
		if (!MSHookIvar<BOOL>(self, "isLoaingMore"))
		{
			NSLog(@"iOSRE: 已加载%lu条转发。", (unsigned long)[allReposts count]);
			[self loadMoreData];
		}
	}
	else
	{
		[loadMoreTimer invalidate];
		NSLog(@"iOSRE: 已加载全部%lu条转发。", (unsigned long)[allReposts count]);

		/* 筛选符合条件的微博 */
		NSMutableArray *allSatisfyingBookReposts = [NSMutableArray arrayWithCapacity:[allReposts count]];
		NSMutableArray *allSatisfyingDraftReposts = [NSMutableArray arrayWithCapacity:[allReposts count]];
		for (WBStatus *status in allReposts)
		{
			if (status.user.isFollower && [[status.text componentsSeparatedByString:@"@"] count] - 1 >= 3)
			{
				[allSatisfyingBookReposts addObject:status];
				if ([status.text rangeOfString:@"我要原稿"].location != NSNotFound) [allSatisfyingDraftReposts addObject:status];
			}
		}
		NSLog(@"iOSRE: 已筛选出所有符合条件的微博,其中满足送书条件的有%lu条,满足送原稿条件的有%lu条。", (unsigned long)[allSatisfyingBookReposts count], (unsigned long)[allSatisfyingDraftReposts count]);

		/* 抽奖 */
		NSMutableSet *chosenBookReposts = [NSMutableSet setWithCapacity:10];
		while ([chosenBookReposts count] < 10)
		{
			arc4random_uniform([allSatisfyingBookReposts count]);
			WBStatus *chosenBookRepost = allSatisfyingBookReposts[arc4random_uniform([allSatisfyingBookReposts count])];
			if (![chosenBookReposts iosreContainsDuplicateUser:chosenBookRepost]) [chosenBookReposts addObject:chosenBookRepost];
			arc4random_uniform([allSatisfyingBookReposts count]);
		}
		WBStatus *chosenDraftRepost = nil;
		if ([allSatisfyingDraftReposts count] > 0)
		{
			do
			{
				arc4random_uniform([allSatisfyingDraftReposts count]);
				chosenDraftRepost = allSatisfyingDraftReposts[arc4random_uniform([allSatisfyingDraftReposts count])];
				arc4random_uniform([allSatisfyingDraftReposts count]);
			}
			while ([chosenBookReposts iosreContainsDuplicateUser:chosenDraftRepost]);
		}
		NSLog(@"iOSRE: 中奖的朋友已抽出,下面开奖!");

		/* 开奖 */
		int i = 1;
		for (WBStatus *chosenBookRepost in chosenBookReposts)
		{
			NSLog(@"iOSRE: 第%d名中奖的朋友是@%@ ,ta转发的内容是“%@”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~", i, chosenBookRepost.user.displayName, chosenBookRepost.text);
			i++;
		}
		if (chosenDraftRepost) NSLog(@"iOSRE: 获得特殊奖的朋友是@%@ ,ta转发的内容是“%@”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》原稿一份,恭喜你~", chosenDraftRepost.user.displayName, chosenDraftRepost.text);
		else NSLog(@"iOSRE: 没有人想要原稿?那就小弟就自留收藏啦~");
		NSLog(@"iOSRE: 本次抽奖到此结束。感谢大家的参与和支持,谢谢!");

		/* 退出微博客户端 */
		[[UIApplication sharedApplication] terminateWithSuccess];
	}
}

%new
- (void)iosreDraw
{
	NSDateComponents *components = [[NSDateComponents alloc] init];
	components.year = 2015;
	components.month = 4;
	components.day = 9;
	components.hour = 17;
	components.minute = 0;
	NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];  
	NSDate *date = [calendar dateFromComponents:components];
	if ([date compare:[NSDate date]] == NSOrderedAscending)
	{
		/* 倒计时结束 */
		[countdownTimer invalidate];
		if (!loadMoreTimer) loadMoreTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(iosreLoadMoreReposts) userInfo:nil repeats:YES];
	}
	[components release];
	[calendar release];
}

- (void)tableView:(id)view willDisplayCell:(id)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
	%orig;
	/* 开始倒计时 */
	if (!countdownTimer) countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(iosreDraw) userInfo:nil repeats:YES];
}
%end

4. 编辑Makefile以及control

编辑完成的Makefile如下:

THEOS_DEVICE_IP = iOSIP
ARCHS = armv7 arm64
TARGET = iphone:latest:8.0

include theos/makefiles/common.mk

TWEAK_NAME = iOSRELottery2
iOSRELottery2_FILES = Tweak.xm
iOSRELottery2_FRAMEWORKS = UIKit

include $(THEOS_MAKE_PATH)/tweak.mk

after-install::
	install.exec "killall -9 Weibo"

编辑完成的control如下:

Package: com.iosre.iosrelottery2
Name: iOSRELottery2
Depends: mobilesubstrate, firmware (>= 8.0)
Version: 1.0
Architecture: iphoneos-arm
Description: 微博抽奖插件
Maintainer: snakeninny
Author: snakeninny
Section: Tweaks
Homepage: http://bbs.iosre.com

九 测试

打开新浪微博iOS客户端,登陆iOS应用逆向工程账号,进入抽奖微博并切换到转发界面,如图所示:


然后ssh到iOS并静待17:00的到来 :sleeping:
待微博自动退出后,抽奖就结束了,插件的输出如下:

FunMaker-5:~ root# grep iOSRE /var/log/syslog
Apr  9 16:56:19 FunMaker-5 Weibo[617]: MS:Notice: Loading: /Library/MobileSubstrate/DynamicLibraries/iOSRELottery2.dylib
Apr  9 17:00:01 FunMaker-5 Weibo[617]: iOSRE: 已加载21条转发。
Apr  9 17:00:05 FunMaker-5 Weibo[617]: iOSRE: 已加载41条转发。
Apr  9 17:00:05 FunMaker-5 Weibo[617]: iOSRE: 已加载60条转发。
Apr  9 17:00:06 FunMaker-5 Weibo[617]: iOSRE: 已加载80条转发。
Apr  9 17:00:07 FunMaker-5 Weibo[617]: iOSRE: 已加载99条转发。
Apr  9 17:00:08 FunMaker-5 Weibo[617]: iOSRE: 已加载119条转发。
Apr  9 17:00:09 FunMaker-5 Weibo[617]: iOSRE: 已加载139条转发。
Apr  9 17:00:10 FunMaker-5 Weibo[617]: iOSRE: 已加载159条转发。
Apr  9 17:00:11 FunMaker-5 Weibo[617]: iOSRE: 已加载179条转发。
Apr  9 17:00:12 FunMaker-5 Weibo[617]: iOSRE: 已加载199条转发。
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 已加载全部212条转发。
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 已筛选出所有符合条件的微博,其中满足送书条件的有151条,满足送原稿条件的有67条。
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 中奖的朋友已抽出,下面开奖!
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第1名中奖的朋友是@林诗仪Sylvia ,ta转发的内容是“我要原稿@风华正少乀 @徐小甜-Sissy @曲丹阳_Q ”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第2名中奖的朋友是@杨_乐_天 ,ta转发的内容是“第一版的第一页都还没看的人表示可以来找我拿了看 //@大明湖畔小小猪:计划下一本就读它,我要原稿〜@杨_乐_天 @-程虎- @Mac_Chark”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第3名中奖的朋友是@大头小粽 ,ta转发的内容是“3个好友都难找@成龙 @大自然保护协会-马云 @韩寒 ”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第4名中奖的朋友是@MOE_翔 ,ta转发的内容是“我要原稿 @creantan @小兔子报菜T @MOE_翔”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第5名中奖的朋友是@Hm_权 ,ta转发的内容是“我要原稿 @Hghowe @KenCoki @AimGod-Ben張-健斌 ”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第6名中奖的朋友是@Onetaway ,ta转发的内容是“我(xi)要(da)原(pu)稿(ben) @蔡小虎 @KITTEN-YANG @CrespoXiao ”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第7名中奖的朋友是@花伦同学喵 ,ta转发的内容是“逆向工程一我有,er我也想有,我要原稿!@叮咚叮咚2011 @Rocky_Lo @ethan23 ”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第8名中奖的朋友是@少年ni总算来了 ,ta转发的内容是“@Beyond-L权 @月半兄 @护喵大叔_ hu”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第9名中奖的朋友是@我就叫黄龙 ,ta转发的内容是“支持🐶神 来一发。我要原稿~~~~我要原稿@一行O @chengzi_wj @太平kok”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 第10名中奖的朋友是@陆刃佳 ,ta转发的内容是“come on@屌丝程序猿代码狗 @xxDeWorld @互联网实践者老张 ”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》一本,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 获得特殊奖的朋友是@汤奇tangqi92 ,ta转发的内容是“我要原稿~~@sofish @罗升阳 @ghosert”。你的奖品是两位作者签名的《iOS应用逆向工程(第2版)》原稿一份,恭喜你~
Apr  9 17:00:13 FunMaker-5 Weibo[617]: iOSRE: 本次抽奖到此结束。感谢大家的参与和支持,谢谢!
FunMaker-5:~ root# 

就是这样了。谢谢阅读!

5 个赞

前排沙发,我要签名我要签名!!