如何用逆向的方式强行在朋友圈斗图

### 前言
本文分享一下如何用逆向Hook的方式强行打开朋友圈表情包的选项
准备工作如下:
- 1.可越狱的iPhone(我使用iPhone6SPlus)
- 2.不可越狱的iPhone(我使用iPhone11ProMax)
- 3.MacBook Pro
- 4.Xcode
- 5.Hopper Disassembler
- 6.WeChat 7.0.10

### 步骤
- 1.首先我们使用ssh连接我们已经越狱的iPhone控制台，然后使用frida对其进行砸壳(这里不演示)
- 2.我们进入微信朋友圈的界面，依次按出 评论->笑脸，并不再操作保持锁屏。使用`ps -e | grep WeChat`勾住微信进程，拿到pid号。接着使用`cycript -p pid号`进入cy界面，再使用` [[UIApp keyWindow] recursiveDescription].toString()`打印并找出，评论框的视图的类名称(iOS中一般命名为XXView)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/9b4e8350-82eb-d0a7-ecd4-43eee49ac873.png)
- 3.使用class-dump打出WeChat的头文件，拿到`EmoticonBoardView`相关类的成员变量(属性，可以通过OC点语法调用)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/8d7f9fa6-0d43-182b-1d23-9973c4d7be11.png)
完整代码EmoticonBoardView.h如下

```
//
//     Generated by class-dump 3.5 (64 bit) (Debug version compiled Jan 10 2020 14:01:04).
//
//  Copyright (C) 1997-2019 Steve Nygard.
//

#import "MMUIView.h"

#import "EmoticonBackupOperateMgrExt-Protocol.h"
#import "EmoticonBoardCameraEmoticonEntryViewDelegate-Protocol.h"
#import "EmoticonBoardCrossCollectionControllerDelegate-Protocol.h"
#import "EmoticonBoardDynamicTabBarDelegate-Protocol.h"
#import "EmoticonBoardRecommendViewDelegate-Protocol.h"
#import "EmoticonCustomAddLogicControllerDelegate-Protocol.h"
#import "EmoticonRecommendMgrExt-Protocol.h"
#import "EmoticonTabRecommendMgrExt-Protocol.h"
#import "IEmoticonMgrExt-Protocol.h"
#import "IEmoticonPackageMgrExt-Protocol.h"
#import "IMMLanguageMgrExt-Protocol.h"
#import "MMConfigMgrExt-Protocol.h"
#import "MMKernelExt-Protocol.h"
#import "MMUIViewControllerDelegate-Protocol.h"

@class CAShapeLayer, EmoticonBoardCrossCollectionController, EmoticonBoardDynamicTabBar, EmoticonCustomAddLogicController, EmoticonPreview, NSString, QQEmojiPreview, UIView;
@protocol BaseEmoticonViewDelegate, EmoticonBoardViewDelegate;

@interface EmoticonBoardView : MMUIView <IMMLanguageMgrExt, MMKernelExt, IEmoticonPackageMgrExt, EmoticonRecommendMgrExt, IEmoticonMgrExt, EmoticonBackupOperateMgrExt, EmoticonBoardDynamicTabBarDelegate, EmoticonTabRecommendMgrExt, MMUIViewControllerDelegate, EmoticonBoardRecommendViewDelegate, EmoticonBoardCameraEmoticonEntryViewDelegate, EmoticonBoardCrossCollectionControllerDelegate, EmoticonCustomAddLogicControllerDelegate, MMConfigMgrExt>
{
    id <BaseEmoticonViewDelegate> m_emoticonSendDelegate;
    UIView *m_backgroundView;
    CAShapeLayer *m_topCornerLayer;
    EmoticonBoardCrossCollectionController *m_pageCollectionController;
    EmoticonBoardDynamicTabBar *m_dynamicTabBar;
    QQEmojiPreview *m_qqEmojiPreview;
    EmoticonPreview *m_emoticonPreview;
    unsigned long long _boardShowTime;
    unsigned long long _boardQQEmojiClickIndex;
    unsigned long long _boardEmoticonClickIndex;
    unsigned long long _boardTabBarClickIndex;
    EmoticonCustomAddLogicController *m_emoticonAddLogicController;
    _Bool m_shouldHideGameEmoticon;
    _Bool _isSingleTab;
    _Bool _forceShowCameraEmoticonEntry;
    _Bool _hasCorner;
    _Bool _hasFoldButton;
    double m_bottomInset;
    id <EmoticonBoardViewDelegate> _m_delegate;
    unsigned long long _emojiUseScene;
    NSString *_finishButtonTitle;
    unsigned long long _style;
    unsigned long long _recentPulledByCameraEmoticonLabelTime;
    NSString *_externInfo;
}

+ (void)unRegisterInstanceDelegate;
+ (void)destroyInstance;
+ (id)shareInstanceForChat;
@property(retain, nonatomic) NSString *externInfo; // @synthesize externInfo=_externInfo;
@property(nonatomic) unsigned long long recentPulledByCameraEmoticonLabelTime; // @synthesize recentPulledByCameraEmoticonLabelTime=_recentPulledByCameraEmoticonLabelTime;
@property(nonatomic) _Bool hasFoldButton; // @synthesize hasFoldButton=_hasFoldButton;
@property(nonatomic) _Bool hasCorner; // @synthesize hasCorner=_hasCorner;
@property(nonatomic) unsigned long long style; // @synthesize style=_style;
@property(nonatomic) _Bool forceShowCameraEmoticonEntry; // @synthesize forceShowCameraEmoticonEntry=_forceShowCameraEmoticonEntry;
@property(nonatomic) _Bool isSingleTab; // @synthesize isSingleTab=_isSingleTab;
@property(retain, nonatomic) NSString *finishButtonTitle; // @synthesize finishButtonTitle=_finishButtonTitle;
@property(nonatomic) unsigned long long emojiUseScene; // @synthesize emojiUseScene=_emojiUseScene;
@property(nonatomic) __weak id <EmoticonBoardViewDelegate> m_delegate; // @synthesize m_delegate=_m_delegate;
@property(nonatomic) _Bool shouldHideGameEmoticon; // @synthesize shouldHideGameEmoticon=m_shouldHideGameEmoticon;
@property(nonatomic) double bottomInset; // @synthesize bottomInset=m_bottomInset;
- (void).cxx_destruct;
- (void)hideEmoticonPreview;
- (void)showEmoticonPreviewForWrap:(id)arg1 description:(id)arg2 frame:(struct CGRect)arg3;
- (void)hideQQEmojiPreview;
- (void)showQQEmojiPreviewForKey:(id)arg1 atCenterPoint:(struct CGPoint)arg2;
- (void)onTapRecommendWithPid:(id)arg1;
- (void)onTapCameraEmoticonCreateButton;
- (void)onTapEmoticonWrap:(id)arg1 atIndex:(long long)arg2 maxCountPerLine:(unsigned long long)arg3 fromSection:(id)arg4;
- (void)onTapQQEmojiDeleteButton;
- (void)onTapQQEmojiSendButton;
- (void)onTapQQEmojiWithCode:(id)arg1 isRecentUse:(_Bool)arg2 atIndex:(long long)arg3 maxCountPerLine:(unsigned long long)arg4;
- (void)didChangeToPage:(id)arg1;
- (void)onEmoticonBoardDynamicTabBarClickFloatingFoldButton;
- (void)onEmoticonBoardDynamicTabBarClickSettingItem;
- (void)onEmoticonBoardDynamicTabBarClickStorePlusItem;
- (void)onEmoticonBoardDynamicTabBarTabBarClickItem:(id)arg1 atIndex:(long long)arg2;
- (id)getViewController;
- (void)onUploadEmoticonQueueChanged;
- (void)onAddBackupEmoticonOK:(id)arg1 addEmoticonWrap:(id)arg2;
- (void)onMMDynamicConfigUpdated;
- (void)OnEmoticonTabRecommendChanged;
- (void)onPreQuit;
- (void)onLanguageChange;
- (void)OnEmoticonRecommendNewStateChangedForBoardView:(_Bool)arg1;
- (void)OnUpdateEmoticonPackageList;
- (void)OnUpdateEmoticonPackageListAfterSort;
- (void)OnUpdateEmoticonList;
- (long long)emotionRecommandMaxCountConfig;
- (id)cameraPackageId;
- (id)customPackageId;
- (id)GetCurrentViewController;
- (void)layoutSubviews;
- (_Bool)selectTabOfPid:(id)arg1 forceFirstPage:(_Bool)arg2 animated:(_Bool)arg3;
- (void)changeTabToPid:(id)arg1;
- (void)changeToPidPosition:(id)arg1;
- (void)showAddEmoticonActionSheet;
- (void)reloadEmoticonViewData;
- (void)reloadTabBarWithTabItems:(id)arg1;
- (id)checkImageExistInEmoticonWrapArray:(id)arg1 abortOnFailure:(_Bool)arg2;
- (id)getEmoticonListWithPid:(id)arg1 needCheckImageExist:(_Bool)arg2;
- (id)getCameraEmoticonListWithNeedCheckImageExist:(_Bool)arg1;
- (id)getCustomEmoticonListWithNeedCheckImageExist:(_Bool)arg1;
- (id)getEmoticonArrayWithPackageArray:(id)arg1;
- (id)getTabItemsWithStickerPackages:(id)arg1 recommandItems:(id)arg2;
- (id)getTabRecommandItemsWithStickerPackages:(id)arg1;
- (id)getStickerPackages;
- (id)emoticonAddLogicController;
- (void)resetEmotionBoardShowTimeAndClickIndex;
- (void)SetCanSend:(_Bool)arg1;
@property(readonly, nonatomic) double preferHeight;
- (void)UnRegisterDelegate;
- (void)RegisterDelegate:(id)arg1;
- (void)prepareForReuseWithoutReload;
- (_Bool)checkNeedReloadForReuse;
- (_Bool)isSticker;
- (void)reloadEmotionBoardView;
- (void)initView;
- (void)initData;
- (void)dealloc;
- (id)initWithFrame:(struct CGRect)arg1;

// Remaining properties
@property(readonly, copy) NSString *debugDescription;
@property(readonly, copy) NSString *description;
@property(readonly) unsigned long long hash;
@property(readonly) Class superclass;

@end

```
由于是成员变量或者叫做类属性，我们重点关注代码中的`@property`关键字，由于之前这个功能曾经开放过但又关闭了，可以联想到该属性应该为Bool型.
- 4.使用Hopper Disassembler打出WeChat的.m文件(方法实现文件)
搜索EmoticonBoardView关键字并配合查看.h文件中的属性，最终发现`isSingleTab`属性使用setter和getter方法

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/2c64dffa-6681-0068-ce1c-590c11a0ecfc.png)

- 5.使用hook语法修改其bool变量
由于不知道true和false代表的意思，这里我们先使用true

```
%hook EmoticonBoardView

- (BOOL)IsSingleTab {
    return true;
}

- (void)setIsSingleTab:(BOOL)arg {
    arg = true;
}

%end
```

尝试使用`theos`套件的`make package install`命令进行编译安装，查看效果

<img width="414" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/e24ed827-7401-ed03-aae1-bf39fbe01ecb.png">

发现ture并未达到预期效果，接着使用false进行尝试

```
%hook EmoticonBoardView

- (BOOL)IsSingleTab {
    return true;
}

- (void)setIsSingleTab:(BOOL)arg {
    arg = true;
}

%end
```

再次安装查看效果

<img width="414" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/9d16e65c-1d55-aec1-b735-714addf6e9cf.png">

发现成功达到效果

- 6.将dylib注入app安装到iPhone11ProMax

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491410/bd9e450c-5a6c-b407-c319-8b8e21f3ea02.png)

成功实现免越狱突破限制


### 遗憾
经过本文完成时测试，腾讯已经关闭了表情包消息上传接口，但本地可以依然可以看到。本文主要为分享思路，勿喷！插件可联系吐司群——清水川崎获得dylib和deb。