首页
社区
课程
招聘
[原创]破解iOS微信骰子和猜拳
发表于: 2021-1-9 10:49 30595

[原创]破解iOS微信骰子和猜拳

2021-1-9 10:49
30595

本文主要实现的是自由控制微信消息中发送的骰子数和石头剪刀布的结果。
功能是去年9月实现的,针对的微信版本是7.0.18,逻辑分析耗时4天,UI用时2天。

 

最终实现的效果如下所示,在微信的“发现”页面增加2行,用于输入想要的骰子点数和猜拳结果。比如这里输入的骰子点数是6,那么在微信消息页面发送的骰子静止后点数就是6。

 

 

首先我们在桌面先创建一个文件夹WXProgram,用它来存放我们的项目中涉及到的资源

第一步 准备

  1. 首先当然是脱壳得到我们的ipa包了,这一步就不说了,相信大家都会。我们将脱壳后得到的ipa包命名为WeiXin.ipa,放在WXProgram文件夹下。
  2. 在WXProgram文件夹下创建一个名称为“wxheader”的文件夹,用来存放class-dump获取微信的所有类的头文件信息。
    1
    class-dump -S -s -H WeChat ~/Desktop/WXProgram/wxheader
  3. 利用Xcode创建一个iOS的MonkeyApp,重签名微信应用。

    MonkeyApp工程目录如下,之后的hook代码编写都是在WeiXinDylib目录下的Logos中。

第二步 获取发送骰子所触发的函数

1. 先获取发送骰子所在的页面。
将Xcode重签名后的微信应用运行在手机上后,来到发送骰子的消息页面,利用Xcode的视图层级调试功能(Debug View Hierarchy)查看当前试图层级,得到发送骰子所在的页面控制器为BaseMsgContentViewController。当然也有其他的方法来获取发送骰子所在的控制器,如Reveal和Cycript。

 

 

2. 获取发送骰子调用的方法。

方法有2种,1是使用logify去hook BaseMsgContentViewController 类的所有方法,打印参数;2是使用MonkeyDev提供的MDConfig去hook。

 

本文采用的是通过配置MonkeyDev提供的MDConfig.plist来实现hook控制器中的方法,下图中是配置好的MDConfig.plist,MDConfig.plist中各属性的意义请参考https://github.com/omxcodec/OCMethodTrace/blob/master/README.md

 

 

配置好后重新运行程序,发送骰子后,控制器打印出hook到的方法及参数,观察输出日志,发现 SendEmoticonMesssageToolView:方法应该是我们要找的方法。

1
[<BaseMsgContentViewController: 0x111bb6400> SendEmoticonMesssageToolView:<CEmoticonWrap: self.m_uiType=1, self.m_bCanDelete=1, self.m_uiGameType=2, self.m_nsAppID=(null), self.m_extFlag=0, self.m_nsThumbImgPath=(null), self.m_lastUsedTime=0, self.m_isAsyncUpload=0, self.m_isRemoteRecommed=0, self.m_isLastSended=0, self.m_query=(null), self.m_emojiInfo=<EmojiInfoObj: md5=dice_emoticon_md5, url=(null), thumbUrl=(null), designerId=(null), encryptUrl=(null), aesKey=(null), productId=custom_emoticon_pid, externUrl=(null), externMd5=(null), disableExtern=0, activityId=(null), attachedText=(null), tpurl=(null), tpauthkey=(null), attachedTextColor=(null), lensId=(null)>>]

接下来编写代码,hook这个-[BaseMsgContentViewController SendEmoticonMesssageToolView:]方法,去验证。

第三步 查看发送骰子逻辑

  1. 首先我们hook下发送骰子的方法,-[BaseMsgContentViewController SendEmoticonMesssageToolView:]

    下面的hook代码位于工程目录下的Logos文件夹下,WeiXinDylib.xm中。%orig 表示调用原来的方法实现。

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>
 
%hook BaseMsgContentViewController
 
- (void)SendEmoticonMesssageToolView:(id)arg1 {
 
    %orig;
 
}
 
%end

2.查看方法调用流程。
我们为上面的方法中 %orig;所在的一行下个断点,然后在聊天界面发送骰子,断点断住后在Xcode控制台使用 bt 命令打印方法的调用堆栈。堆栈信息如下:

 

可以看到堆栈信息中frame #7 - frame #10是WeChat中调用的方法栈,其他的则为系统调用。但是这几个调用栈都看不到方法名称,显示的是___lldb_unnamed_symbol,这说明部分符号信息被裁减,需要我们手动恢复。可以尝试用restore-symbol去恢复,但是本人尝试后发现python脚本出错,未能恢复符号。

 

因为从堆栈信息无法看到函数名称,所以就靠我们计算来看了。

内存地址 - ASLR = Mach-O中偏移地址

 

ASLR 通过 image list -o -f WeChat 获取。

 

比如上面的frame #7对应的内存地址为 0x00000001021a4960,减去ASLR(0x00000000002cc000),得到 0x101ED8960,可以用 Hopper 或 IDA 在Mach-O中找到 0x101ED8960 所对应的函数。

  1. 查看发送骰子函数调用

本文采用的是直接利用IDA反编译

  1. 先看-[BaseMsgContentViewController SendEmoticonMesssageToolView:]方法内部实现,反编译后伪代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void __cdecl -[BaseMsgContentViewController SendEmoticonMesssageToolView:](BaseMsgContentViewController *self, SEL a2, id a3)
{
  BaseMsgContentViewController *v3; // x20
  __int64 v4; // x19
  signed __int64 v5; // x21
  void *v6; // x20
  void *v7; // x24
  char v8; // w23
  __int64 v9; // x0
  __int64 v10; // x20
  void *v11; // x24
  int v12; // w22
 
  v3 = self;
  v4 = objc_retain(a3, a2);
  v5 = (signed __int64)&v3->m_delegate;
  v6 = (void *)objc_loadWeakRetained(&v3->m_delegate);
  if ( !((unsigned __int64)objc_msgSend(v6, "respondsToSelector:", "CanSendEmoticonMessage") & 1) )
    goto LABEL_6;
  v7 = (void *)objc_loadWeakRetained(v5);
  v8 = (unsigned __int64)objc_msgSend(v7, "CanSendEmoticonMessage");
  objc_release(v7);
  objc_release(v6);
  if ( v8 & 1 )
  {
    v9 = objc_loadWeakRetained(v5);
    if ( v9 )
    {
      v10 = v9;
      v11 = (void *)objc_loadWeakRetained(v5);
      v12 = (unsigned __int64)objc_msgSend(v11, "respondsToSelector:", "SendEmoticonMessage:");
      objc_release(v11);
      objc_release(v10);
      if ( v12 )
      {
        v6 = (void *)objc_loadWeakRetained(v5);
        objc_msgSend(v6, "SendEmoticonMessage:", v4);
LABEL_6:
        objc_release(v6);
        goto LABEL_7;
      }
    }
  }
LABEL_7:
  objc_release(v4);
}

可以看到方法内部调用了 [self->m_delegate SendEmoticonMessage:],其中self为BaseMsgContentViewController对象,断点调试打印BaseMsgContentViewController 的 m_delegate 属性,得到m_delegate是一个WeixinContentLogicController对象,在WeixinContentLogicController.h中查找SendEmoticonMessage:,发现并没有,继续在父类中找,最后在父类BaseMsgContentLogicController中找到方法。

 

 

2.接下来看-[BaseMsgContentLogicController SendEmoticonMessage:]方法内部实现,反编译后伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
void __cdecl -[BaseMsgContentLogicController SendEmoticonMessage:](BaseMsgContentLogicController *self, SEL a2, id a3)
{
  BaseMsgContentLogicController *v3; // x21
  void *v4; // x19
  void *v5; // x0
  __int64 v6; // x23
  char v7; // w22
  int v8; // w0
  GameController *v9; // x22
  void *v10; // x0
  void *v11; // x20
  struct objc_object *v12; // x0
  __int64 v13; // x1
  __int64 v14; // x22
  struct objc_object *v15; // x0
  void *v16; // x21
  void *v17; // x0
  void *v18; // x0
  void *v19; // x23
  void *v20; // x0
  __int64 v21; // x24
  void *v22; // x0
  __int64 v23; // [xsp+8h] [xbp-38h]
 
  v3 = self;
  v4 = (void *)objc_retain(a3, a2);
  if ( v4 )
  {
    v5 = -[CBaseContact m_nsUsrName](v3->m_contact, "m_nsUsrName");
    v6 = objc_retainAutoreleasedReturnValue(v5);
    v7 = (unsigned __int64)+[PluginUtil isPluginUserName:](&OBJC_CLASS___PluginUtil, "isPluginUserName:", v6);
    objc_release(v6);
    v8 = (unsigned __int64)objc_msgSend(v4, "m_uiGameType");
    if ( v7 & 1 || !v8 )
    {
      v23 = 0LL;
      v12 = +[CEmoticonMgr emoticonMsgForEmoticonWrap:imageData:errorMsg:](
              &OBJC_CLASS___CEmoticonMgr,
              "emoticonMsgForEmoticonWrap:imageData:errorMsg:",
              v4,
              0LL,
              &v23);
      v11 = (void *)objc_retainAutoreleasedReturnValue(v12);
      v14 = objc_retain(v23, v13);
      -[BaseMsgContentLogicController SendNotGameEmoticonMessage:errorMsg:](
        v3,
        "SendNotGameEmoticonMessage:errorMsg:",
        v11,
        v14);
      v15 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
      v16 = (void *)objc_retainAutoreleasedReturnValue(v15);
      v17 = objc_msgSend(&OBJC_CLASS___EmoticonInputRecommendMgr, "class");
      v18 = objc_msgSend(v16, "getService:", v17);
      v19 = (void *)objc_retainAutoreleasedReturnValue(v18);
      v20 = objc_msgSend(v11, "m_nsEmoticonMD5");
      v21 = objc_retainAutoreleasedReturnValue(v20);
      v22 = +[CUtility genCurrentTime](&OBJC_CLASS___CUtility, "genCurrentTime");
      objc_msgSend(v19, "updateEmoticon:withMd5:usedTime:", v4, v21, v22);
      objc_release(v14);
      objc_release(v21);
      objc_release(v19);
      objc_release(v16);
    }
    else
    {
      v9 = v3->m_GameController;
      v10 = -[CBaseContact m_nsUsrName](v3->m_contact, "m_nsUsrName");
      v11 = (void *)objc_retainAutoreleasedReturnValue(v10);
      -[GameController sendGameMessage:toUsr:](v9, "sendGameMessage:toUsr:", v4, v11);
    }
    objc_release(v11);
  }
  objc_release(v4);
}

转换成OC代码后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- (void)SendEmoticonMessage:(CEmoticonWrap *)a3 {   
      if ( a3 )
      {
         NSString *m_nsUsrName = [self.m_contact m_nsUsrName];
 
         BOOL isPluginUserName = [PluginUtil isPluginUserName:m_nsUsrName];
 
         int m_uiGameType = a3.m_uiGameType;
 
         if ( isPluginUserName || !m_uiGameType )
        {
            NSError *error = nil;
 
            CMessageWrap *cMessageWrap = [CEmoticonMgr emoticonMsgForEmoticonWrap:a3 imageData:a3.m_imageData errorMsg:&error];
 
            [self SendNotGameEmoticonMessage:cMessageWrap errorMsg:error];
 
            MMContext *currentContext = [MMContext currentContext];
 
           EmoticonInputRecommendMgr *service = [currentContext getService:%c(EmoticonInputRecommendMgr)];
 
 
 NSString *m_nsEmoticonMD5 = cMessageWrap.m_nsEmoticonMD5;
 
 int genCurrentTime = [CUtility genCurrentTim.;
            [service updateEmoticon:a3 withMd5:m_nsEmoticonMD5 usedTime:genCurrentTime];
 
        }
        else
        {
            GameController *m_GameController = self.m_GameController;
 
            NSString *m_nsUsrName = self.m_contact.m_nsUsrName;
 
            [m_GameController sendGameMessage:a3 toUsr:m_nsUsrName];
        }
 
      }
}

hook方法-[BaseMsgContentLogicController SendEmoticonMessage:],最终确定这里走的是else的流程,调用 [m_GameController sendGameMessage:a3 toUsr:m_nsUsrName] 方法,打印出m_GameController 为一个 GameController对象。

 

3.接下来看-[GameController sendGameMessage:toUsr:]方法内部实现,反编译后伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
void __cdecl -[GameController sendGameMessage:toUsr:](GameController *self, SEL a2, id a3, id a4)
{
  id v4; // x19
  id v5; // x24
  __int64 v6; // x23
  __int64 v7; // x1
  __int64 v8; // x21
  void *v9; // x0
  void *v10; // x19
  struct objc_object *v11; // x0
  __int64 v12; // x20
  struct objc_object *v13; // x0
  void *v14; // x25
  void *v15; // x0
  void *v16; // x0
  void *v17; // x0
  void *v18; // x26
  void *v19; // x0
  void *v20; // x0
  void *v21; // x0
  void *v22; // x0
  void *v23; // x24
  void *v24; // x0
  __int64 v25; // x23
  struct objc_object *v26; // x0
  void *v27; // x24
  void *v28; // x0
  void *v29; // x0
  void *v30; // x25
  void *v31; // x0
  __int64 v32; // x0
  __int64 v33; // x26
  void *v34; // x0
  struct objc_object *v35; // x0
  void *v36; // x20
  void *v37; // x0
  void *v38; // x0
  void *v39; // x21
  void *v40; // x0
  __int64 v41; // x20
 
  v4 = a4;
  v5 = a3;
  v6 = objc_retain(a3, a2);
  v8 = objc_retain(v4, v7);
  v9 = (void *)objc_alloc(&OBJC_CLASS___CMessageWrap);
  v10 = objc_msgSend(v9, "initWithMsgType:", 47LL);
  v11 = +[SettingUtil getLocalUsrName:](&OBJC_CLASS___SettingUtil, "getLocalUsrName:", 0LL);
  v12 = objc_retainAutoreleasedReturnValue(v11);
  objc_msgSend(v10, "setM_nsFromUsr:", v12);
  objc_release(v12);
  objc_msgSend(v10, "setM_nsToUsr:", v8);
  objc_release(v8);
  v13 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
  v14 = (void *)objc_retainAutoreleasedReturnValue(v13);
  v15 = objc_msgSend(&OBJC_CLASS___MMNewSessionMgr, "class");
  v16 = objc_msgSend(v14, "getService:", v15);
  v17 = (void *)objc_retainAutoreleasedReturnValue(v16);
  v18 = v17;
  v19 = objc_msgSend(v17, "GenSendMsgTime");
  objc_msgSend(v10, "setM_uiCreateTime:", v19);
  objc_release(v18);
  objc_release(v14);
  objc_msgSend(v10, "setM_uiStatus:", 1LL);
  v20 = objc_msgSend(v5, "m_uiType");
  objc_msgSend(v10, "setM_uiEmoticonType:", v20);
  v21 = objc_msgSend(v5, "m_uiGameType");
  objc_msgSend(v10, "setM_uiGameType:", v21);
  +[GameController SetGameContentForMsgWrap:](&OBJC_CLASS___GameController, "SetGameContentForMsgWrap:", v10);
  v22 = objc_msgSend(v5, "m_emojiInfo");
  v23 = (void *)objc_retainAutoreleasedReturnValue(v22);
  objc_release(v6);
  v24 = objc_msgSend(v23, "productId");
  v25 = objc_retainAutoreleasedReturnValue(v24);
  objc_msgSend(v10, "setM_nsEmoticonBelongToProductID:", v25);
  objc_release(v25);
  objc_release(v23);
  v26 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
  v27 = (void *)objc_retainAutoreleasedReturnValue(v26);
  v28 = objc_msgSend(&OBJC_CLASS___WCTempChatMgr, "class");
  v29 = objc_msgSend(v27, "getService:", v28);
  v30 = (void *)objc_retainAutoreleasedReturnValue(v29);
  v31 = objc_msgSend(v10, "m_nsToUsr");
  v32 = objc_retainAutoreleasedReturnValue(v31);
  v33 = v32;
  v34 = objc_msgSend(v30, "isTempChatForUserName:", v32);
  objc_msgSend(v10, "setM_isTempSessionMsg:", v34);
  objc_release(v33);
  objc_release(v30);
  objc_release(v27);
  v35 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
  v36 = (void *)objc_retainAutoreleasedReturnValue(v35);
  v37 = objc_msgSend(&OBJC_CLASS___CMessageMgr, "class");
  v38 = objc_msgSend(v36, "getService:", v37);
  v39 = (void *)objc_retainAutoreleasedReturnValue(v38);
  objc_release(v36);
  v40 = objc_msgSend(v10, "m_nsToUsr");
  v41 = objc_retainAutoreleasedReturnValue(v40);
  objc_msgSend(v39, "AddEmoticonMsg:MsgWrap:", v41, v10);
  objc_release(v41);
  objc_release(v39);
  objc_release(v10);
}

分析伪代码逻辑,发现+[GameController SetGameContentForMsgWrap:]这个方法中应该是对消息的处理。

 

4.接下来看+[GameController SetGameContentForMsgWrap:]方法内部实现,反编译后伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void __cdecl +[GameController SetGameContentForMsgWrap:](GameController_meta *self, SEL a2, id a3)
{
  void *v3; // x19
  int v4; // w20
  __int64 v5; // x0
  signed __int64 v6; // x8
  signed int v7; // w9
  __int64 v8; // x20
  struct objc_object *v9; // x0
  __int64 v10; // x21
 
  v3 = (void *)objc_retain(a3, a2);
  if ( (unsigned int)objc_msgSend(v3, "m_uiGameType") )
  {
    v4 = (unsigned __int64)objc_msgSend(v3, "m_uiGameType");
    v5 = random();
    v6 = 6LL;
    if ( v4 == 1 )
      v6 = 3LL;
    if ( v4 == 1 )
      v7 = 1;
    else
      v7 = 4;
    v8 = v7 + (unsigned int)v5 - (unsigned int)(v5 / v6) * (_DWORD)v6;
    random();
    v9 = +[GameController getMD5ByGameContent:](&OBJC_CLASS___GameController, "getMD5ByGameContent:", v8);
    v10 = objc_retainAutoreleasedReturnValue(v9);
    objc_msgSend(v3, "setM_nsEmoticonMD5:", v10);
    objc_release(v10);
    objc_msgSend(v3, "setM_uiGameContent:", v8);
    +[iConsole logWithLevel:module:errorCode:file:line:func:format:](
      &OBJC_CLASS___iConsole,
      "logWithLevel:module:errorCode:file:line:func:format:",
      1LL,
      0LL,
      0LL,
      "GameController.mm",
      95LL,
      "+[GameController SetGameContentForMsgWrap:]",
      CFSTR("game random content %u"),
      v8);
  }
  objc_release(v3);
}

伪代码中有个random()函数,生成随机值的,猜想就是为了生成骰子点数的。将上面的方法转换成OC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+ (void)SetGameContentForMsgWrap:(CMessageWrap *)a3
{
    if(a3.m_uiGameType)
    {
      int m_uiGameType = a3.m_uiGameType;
 
      long v5 = random();
      int v6 = 6;
      int v7 = 0;
 
      if( m_uiGameType == 1 )
         v6 = 3;
 
      if( m_uiGameType == 1 )
         v7 = 1;
      else
         v7 = 4;
 
      int v8 = v7 + (unsigned int)v5 - (unsigned int)(v5 / v6) * v6;
 
     NSString *mD5ByGameContent = [self getMD5ByGameContent:v8];
 
     [a3 setM_nsEmoticonMD5:mD5ByGameContent];
 
     [a3 setM_uiGameContent:v8];
   }
}

hook上面的方法,

  1. 发送石头剪刀布时,m_uiGameType = 1
    v8 = 1, 最终结果为 剪刀
    v8 = 2, 最终结果为 石头
    v8 = 3, 最终结果为 布
  2. 发送骰子时,m_uiGameType = 2
    v8 = 4, 骰子点数为 1
    v8 = 5, 骰子点数为 2
    v8 = 6, 骰子点数为 3
    v8 = 7, 骰子点数为 4
    v8 = 8, 骰子点数为 5
    v8 = 9, 骰子点数为 6
    得出规律 v8 = 骰子点数 + 3

第四步 编写代码,实现UI界面

  1. 编写UI界面。
    微信发现界面对应的控制器是FindFriendEntryViewController,在Logos文件夹下创建一个名为SWSettingCell.xm的文件。项目中需要的图片资源自己放在app包中就行。

SWSettingCell.xm中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
 
#define SWUserDefaults [NSUserDefaults standardUserDefaults]
#define sw_shaizi_key @"sw_shaizi_key"
#define sw_caiquan_key @"sw_caiquan_key"
 
NSMutableDictionary *sw_dict;
 
@interface FindFriendEntryViewController:UIViewController<UIScrollViewDelegate, UITextFieldDelegate>
 
@property (nonatomic) UITableView *m_tableView;
 
 
- (long long)numberOfSectionsInTableView:(id)tableView;
- (void)addNotice;
 
@end
 
 
%hook FindFriendEntryViewController
 
%new
- (void)addNotice {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
 
%new
#pragma mark textField内容发生改变
- (void)textFieldContentChanged:(UITextField *)textField {
    NSLog(@"11111== %ld", textField.tag);//输入文字时 一直监听
 
    if (textField.text.length >= 1) {
        textField.text = [textField.text substringToIndex:1];
        [textField resignFirstResponder];
    }
}
 
%new
#pragma mark 键盘出现
-(void)keyboardWillShow:(NSNotification *)note
{
    CGRect keyBoardRect=[note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    UITableView *tableView = [self valueForKey:@"m_tableView"];
    [tableView setContentOffset:CGPointMake(0, keyBoardRect.size.height) animated:YES];
 
}
 
%new
#pragma mark 键盘消失
-(void)keyboardWillHide:(NSNotification *)note
{
    UITableView *tableView = [self valueForKey:@"m_tableView"];
    [tableView setContentOffset:CGPointMake(0, 0) animated:YES];
}
 
%new
- (void)textFieldTextDidChange:(NSNotification *)notification {
    UITextField *textField = [notification object];
 
    if (textField.text.length >= 1) {
        textField.text = [textField.text substringToIndex:1];
 
        NSString *key = sw_shaizi_key;
        NSInteger num = [textField.text integerValue];
        BOOL isCorrect = (num > 0 && num < 7);
        if (textField.tag == 201) {
            key = sw_caiquan_key;
            isCorrect = (num > 0 && num < 4);
        }
 
        if (isCorrect) {
            [SWUserDefaults setInteger:num forKey:key];
            [SWUserDefaults synchronize];
            [textField resignFirstResponder];
        } else {
            NSString *message = @"请输入1-6的数值";
            if (textField.tag == 201) {
                message = @"请输入1-3的数值";
            }
            //提示从新输入
            textField.text = nil;
            UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:self cancelButtonTitle:@"好的" otherButtonTitles:nil];
            [alertview show];
        }
 
    }
}
 
- (void)viewDidLoad {
    %orig;
 
    UITableView *tableView = [self valueForKey:@"m_tableView"];
    [tableView setValue:self forKey:@"delegate"];
 
    tableView.estimatedRowHeight = 0;
    tableView.estimatedSectionFooterHeight = 0;
    tableView.estimatedSectionHeaderHeight = 0;
 
    sw_dict = [NSMutableDictionary dictionary];
 
    for (int i = 0; i < 2; i++) {
         UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 7, 65, 40)];
         textField.borderStyle = UITextBorderStyleLine;
         textField.textAlignment = NSTextAlignmentCenter;
         textField.keyboardType = UIKeyboardTypeNumberPad;
         textField.returnKeyType = UIReturnKeyDone;
         textField.clearButtonMode = UITextFieldViewModeWhileEditing;
         textField.delegate = self;
 
         textField.font = [UIFont systemFontOfSize:14];
         [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(textFieldTextDidChange:)
         name:UITextFieldTextDidChangeNotification
         object:textField];
 
         textField.tag = 200 + i;
 
         if (i == 0) {
            textField.placeholder = @"1-6";
            [sw_dict setObject:textField forKey:@"sw_shaizi_textfield"];
         } else {
            textField.placeholder = @"1-3";
            [sw_dict setObject:textField forKey:@"sw_caiquan_textfield"];
 
         }
    }
 
    [self addNotice];
}
 
- (long long)numberOfSectionsInTableView:(id)tableView {
    return %orig + 1;
}
 
- (long long)tableView:(id)tableView numberOfRowsInSection:(long long)section {
    if (section != [self numberOfSectionsInTableView:tableView] - 1) {
        return %orig;
    }
    return 2;
}
 
- (id)tableView:(id)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath section] != [self numberOfSectionsInTableView:tableView] - 1) {
        return %orig;
    }
 
    NSString *cellId = @"shaizi";
    if (indexPath.row == 1) {
        cellId = @"caiquan";
    }
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
 
        cell.imageView.image = [UIImage imageNamed:@"weixin_shaizi"];
 
        cell.textLabel.text = @"骰子点数";
 
        UITextField *textField = [sw_dict objectForKey:@"sw_shaizi_textfield"];
        NSInteger num = [SWUserDefaults integerForKey:sw_shaizi_key];
 
         if (indexPath.row == 1) {
            cell.imageView.image = [UIImage imageNamed:@"weixin_caiquan"];
 
            cell.textLabel.text = @"猜拳(1剪刀 2石头 3布)";
            textField = [sw_dict objectForKey:@"sw_caiquan_textfield"];
            num = [SWUserDefaults integerForKey:sw_caiquan_key];
         }
 
        NSString *str = [NSString stringWithFormat:@"%ld", num];
        textField.text = str;
        cell.accessoryView = textField;
    }
 
    cell.backgroundColor = [UIColor whiteColor];
    return cell;
}
 
- (double)tableView:(id)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath section] != [self numberOfSectionsInTableView:tableView] - 1) {
        return %orig;
    }
    return 55;;
}
 
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([indexPath section] != [self numberOfSectionsInTableView:tableView] - 1) {
        return %orig;
    }
 
//    [tableView deselectRowAtIndexPath:indexPath];
}
 
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.view endEditing:YES];
}
 
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self.view endEditing:YES];
}
 
 
#pragma  mark - textField delegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    NSLog(@"11111== %ld", textField.tag);//输入文字时 一直监听
    if (textField.text.length > 1) {
        textField.text = [textField.text substringToIndex:1];
        [textField resignFirstResponder];
    }
    return YES;
}
 
%end
  1. 实现逻辑。
    在Logos文件夹下的WeiXinDylib.xm文件中编写逻辑代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
 
#define SWUserDefaults [NSUserDefaults standardUserDefaults]
#define sw_shaizi_key @"sw_shaizi_key"
#define sw_caiquan_key @"sw_caiquan_key"
 
 
@interface GameController
 
+ (void)SetGameContentForMsgWrap:(id)arg1;
+ (id)getMD5ByGameContent:(unsigned int)arg1;
- (void)sendGameMessage:(id)arg1 toUsr:(id)arg2;
 
@end
 
 
@interface CMessageWrap
 
@property(nonatomic) NSString *m_nsEmoticonMD5;
@property(nonatomic) NSString *m_nsToUsr; // @synthesize m_nsToUsr;
@property(nonatomic) int m_uiMessageType; // @synthesize m_uiMessageType;
@property(nonatomic) int m_uiMesLocalID; // @synthesize m_uiMesLocalID;
@property(nonatomic) int m_uiStatus; // @synthesize m_uiStatus;
@property(nonatomic) int m_uiSendTime; // @synthesize m_uiSendTime;
@property(nonatomic) int m_uiGameContent; // @dynamic m_uiGameContent;
@property(nonatomic) unsigned int m_uiGameType; // @dynamic m_uiGameType;
 
@end
 
 
%hook GameController
 
+ (void)SetGameContentForMsgWrap:(CMessageWrap *)a3
{
    if(a3.m_uiGameType)
    {
    int m_uiGameType = a3.m_uiGameType;
 
    long v5 = random();
    int v6 = 6;
    int v7 = 0;
 
    if( m_uiGameType == 1 )
    v6 = 3;
 
    if( m_uiGameType == 1 )
    v7 = 1;
    else
    v7 = 4;
 
    int v8 = v7 + (unsigned int)v5 - (unsigned int)(v5 / v6) * v6;
 
    if (m_uiGameType == 1) {  //1.剪刀 2.石头  3.
        int num = [SWUserDefaults integerForKey:sw_caiquan_key];
 
        if (num > 0 && (num < 3)) {
            v8 = num;
        }
 
    } else if (m_uiGameType == 2) {
        v8 = [SWUserDefaults integerForKey:sw_shaizi_key] + 3;
    }
 
      NSString *mD5ByGameContent = [self getMD5ByGameContent:v8];
 
      [a3 setM_nsEmoticonMD5:mD5ByGameContent];
 
      [a3 setM_uiGameContent:v8];
 
    }
}
%end

当然,把上面的代码用Tweak实现也是可以的啊


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-1-9 11:08 被wx_流云_437编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (9)
雪    币: 15
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
MARK 大佬牛批
2021-1-18 11:46
0
雪    币: 225
活跃值: (136)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
重签名微信后,怎么防止wx检测封号啊?
2021-1-18 16:18
0
雪    币: 22
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
eastlhu 重签名微信后,怎么防止wx检测封号啊?
目前没有发现微信有封号,只是进入安全模式,让手机重启是不完美越狱失效。微信的进入安全模式的方法也是可以Hook的
2021-1-22 20:10
0
雪    币: 22
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
ﺭ并亲了你一下ﺭ荒 MARK 大佬牛批
一般一般,谢谢夸奖
2021-1-22 20:11
0
雪    币: 588
活跃值: (367)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
兄弟怎么才能跟你一样优秀
2021-1-22 20:52
0
雪    币: 22
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
听风不是雨 兄弟怎么才能跟你一样优秀
我相信,唯有热爱,不惧万难
2021-1-24 13:08
0
雪    币: 11
活跃值: (96)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
老哥以前做过ios开发?
2021-3-16 17:37
0
雪    币: 22
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
snappyjack 老哥以前做过ios开发?
是的呢
2021-3-23 14:12
0
游客
登录 | 注册 方可回帖
返回
//