首页 > QQ技巧 > MacOS微信客户端无限多开功能实现以及实践

MacOS微信客户端无限多开功能实现以及实践

时间:2017-06-12 15:14 作者:QQ地带 我要评论

0x00 传统多开方法
在 macOS 平台上,大部分应用都是支持多开的,比如:
 
⌘ + N 大法:适用于 QQ
open -n /Applications/xxx.app 大法:适用于大部分的应用
那么对于微信客户端来说,以上两种方法都是无效的。其中第一种只能新建新的聊天对象),第二种直接什么反应都没有。
 
但是也没有难倒机智的广大网民,网上流传的 macOS 平台微信客户端多开方法有:
 
微信客户端 + 网页客户端(最笨的做法)
open /Applications/WeChat.app/Contents/MacOS/WeChat 直接通过命令行打开微信包里的二进制文件(会弹出一个 Terminal 窗口什么鬼,而且还不能关掉=。=)
0x01 准备工作
安装各种工具
Dump 出头文件
通过 Hopper Disassembler 导出静态分析文件
...
这里就不再赘述了,参考之前的实践文。
 
0x02 找出入口
由上文所说到的网传多开方法得知是通过 open 命令直接打开二进制文件可以实现双开,那么为什么打开第三个就不行呢,执行命令打开第三个微信客户端:
 
 
可以看到,命令行运行结果提示 Instance is already running! ,既然有这样的提示,那么就可以作为寻找入口的线索,因为一般这种 Log 都是 Hardcode 在代码里的,于是我们又可以祭出神器: Hopper Disassembler!
 
通过 Hopper Disassembler 一下子就能定位出该字符串所在的方法:
 
 
 
居然在 EntryPoint() 中,也就是应用的 main() 方法,不过这不是重点,重点可以看出判断客户端是否多开的方法在 if 语句中:if ([CUtility HasWechatInstance] != 0x0) {...} ,因此可以更近一步看看 [CUtility HasWechatInstance] 方法的内部实现:
 
 
 
Hopper Disassembler 解析出的伪代码已经接近源码,阅读难度大大降低。同时可以发现微信客户端是通过读取应用 BundleIdentifier 的对应实例个数来判断应用是否多开,从 if (r12 >= 0x2) {...} 中得知最多可以存在两个实例,这就是为什么上文中通过 open 命令可以双开的原因吧,但是为什么不能通过 open -n 直接打开呢,还没弄清楚。
 
因此,猜想是通过修改 [CUtility HasWechatInstance] 的返回值来绕过多开检测。
 
0x03 验证猜想
验证猜想的主要方法是通过动态调试,那么又可以祭出一神器:LLDB!
 
使用 LLDB 进行调试前我们需要找出断点地址:
 
 
 
上文中提及的 r12 变量即为实例数量变量,因此我们可以在 mov  r12, rax 上做手脚,即断点地址为 0x0000000100511644。由于该方法在 main() 方法中,因此不能通过 attach process 方法来进行动态调试。于是利用 LLDB 创建 Target 并预先设置好断点,通过 LLDB process launch 来启动应用并触发断点:
 
 
 
执行 ni ,并执行 p $r12 查看值:
 
 
 
因为没有打开客户端,因此实例数量值为0,于是可以通过 LLDB 的 register write 将 r12 值修改为 2,并执行 c 让应用跳出断点继续运行:
 
 
 
果然出现提示 Instance is already running! ,然而并没有任何微信客户端实例正在运行,因此可以得出结论猜想是正确的!
 
0x04 编写 Tweak
同样的使用 constructor 来进行 Tweak。
 
constructor / destructor
 
顾名思义,构造器和析构器,加上这两个属性的函数会在分别在可执行文件(或 shared library)load 和 unload 时被调用,可以理解为在 main() 函数调用前和 return 后执行。
 
参考资料: Clang Attributes 黑魔法小记
 
__attribute__((constructor(102))) static void multipleInstanceTweak(void) {  
    Class class = object_getClass(NSClassFromString(@"CUtility"));
    SEL selector = NSSelectorFromString(@"HasWechatInstance");
    Method method = class_getInstanceMethod(class, selector);
    IMP imp = imp_implementationWithBlock(^(id self) {
        return 0; //永远返回0
    });
    class_replaceMethod(class, selector, imp, method_getTypeEncoding(method));
}
以上,通过方法替换,使 [CUtility HasWechatInstance] 永远返回 0,通过 open -n 来打开多个微信便可以实现微信 macOS 客户端无限多开。
 
0x05 快捷方式多开
通过命令行来多开会不会有点麻烦?通过 Tweak 来添加快捷的多开方式?
 
快捷多开的位置最终选择了在 Dock Menu 的位置,既不影响应用原有的布局也方便多开的操作。
 
根据文档中给出的添加 Dock Menu 的方法是 - (NSMenu *)applicationDockMenu:(NSApplication *)sender; ,然而通过 Hopper Disassembler 分析出来并没有这个方法,因为客户端没有实现这个方法。于是又可以利用 Objective-C 的动态特性,动态添加方法。
 
利用 class_addMethod 添加方法:
 
@implementation NSObject (WeChatTweak)
 
static void __attribute__((constructor)) tweak(void) {  
    class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:))), "@:@");
}
 
+ (BOOL)tweak_HasWechatInstance {
    return NO;
}
 
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
    NSMenu *menu = [[objc_getClass("NSMenu") alloc] init];
    NSMenuItem *menuItem = [[objc_getClass("NSMenuItem") alloc] initWithTitle:@"登录新的微信账号" action:@selector(openNewWeChatInstace:) keyEquivalent:@""];
    [menu insertItem:menuItem atIndex:0];
    return menu;
}
 
- (void)openNewWeChatInstace:(id)sender {
    NSString *applicationPath = [[objc_getClass("NSBundle") mainBundle] bundlePath];
    NSTask *task = [[objc_getClass("NSTask") alloc] init];
    task.launchPath = @"/usr/bin/open";
    task.arguments = @[@"-n", applicationPath];
    [task launch];
}
 
@end
最终效果:
 
 
 
0x06 最后
PS:以上教程代码为原始代码,最终版代码以 GitHub 上的为准,其实就是美化了一下 (:」∠)_
 
源码:https://github.com/Sunnyyoung/WeChatTweak-macOS

标签: 微信
顶一下
(1)
100%
踩一下
(0)
0%

Google提供的广告