zwo 发表于 2020-1-19 19:22

新版Mac wx双开的实现

本帖最后由 zwo 于 2020-1-19 19:27 编辑

新版Mac wx(2.4.0)带来了一波新功能,包括万众期待的小程序,然而在拥抱新功能的同时,多开插件失效了。



首先想到新版的小程序会不会也是一个小程序对应一个新开的进程?这样小程序挂了不会导致主程序也挂。开一个小程序验证如下

可以看到的确两个实例都是通过主程序启动的。


下面就通过IDA分析一下,把app拖进去,经过若干小时的等待后,IDA完成了分析。由于是复用同一个实例,很可能在程序的入口就做了分支。先看看入口函数的流程,不难看出程序里每个关键的地方几乎都有日志输出,大大方便了我们的分析。



在日志的加持下,重新梳理了一下启动流程如下:


可见如果满足了实例个数小于2就可以直接初始化一个新的微信实例。可以通过hook返回实例数组的方法runningApplicationsWithBundleIdentifier,不过为了避免副作用,还是直接改掉跳转指令吧,也就是把jb改成jmp。


另外一个存在的问题是,如果这样改,会使小程序的正常打开受到影响。可以通过设置个标志来判断是否需要修改跳转指令。
完整代码如下:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <Cocoa/Cocoa.h>

#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <stdio.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
#include <unistd.h>

static void patch_mem(uintptr_t p,unsigned int data){
    int page = getpagesize();
    uintptr_t address = (uintptr_t)(p);
    uintptr_t base = address/page * page;
    mach_port_t self = mach_task_self();
    kern_return_t error;
    if((page - (uintptr_t)(p) - base)<12){
      page *= 2;
    }
    if((error = vm_protect(self,base,page,FALSE ,VM_PROT_READ|VM_PROT_WRITE|VM_PROT_COPY))){
      return;
    }
*(unsigned int *) p = data;
    if((error = vm_protect(self,base,page,FALSE,VM_PROT_READ|VM_PROT_EXECUTE))){
      return;
    }
}

@implementation NSObject (WeChatTweak)

static void __attribute__((constructor)) tweak(void) {

    class_addMethod(objc_getClass("AppDelegate"), @selector(applicationDockMenu:), method_getImplementation(class_getInstanceMethod(objc_getClass("AppDelegate"), @selector(tweak_applicationDockMenu:))), "@:@");

    ;
}

- (void)alwaysOpenNewWeChat
{
      NSUserDefaults *defaults=;
      NSString *flag=;
      if (flag)
      {
                const struct mach_header *mhp = _dyld_get_image_header(0);
            uintptr_t module_base_cursor = (uintptr_t)mhp;
            // 0F 82 9E 02 00 00 jb -> E9 9F 02 00 00 jmp
            uintptr_t targetCursor = module_base_cursor + 0xf5a94f;
            patch_mem(targetCursor,0x29fe9);
            ;
            ;
      }
}

- (NSMenu *)tweak_applicationDockMenu:(NSApplication *)sender {
    NSMenu *menu = [ init];
    NSMenuItem *menuItem = [ initWithTitle:@"登录新的微信账号" action:@selector(openNewWeChatInstace:) keyEquivalent:@""];
    ;
    return menu;
}

- (void)openNewWeChatInstace:(id)sender {
    NSString *applicationPath = [ bundlePath];
    NSTask *task = [ init];
    task.launchPath = @"/usr/bin/open";
    task.arguments = @[@"-n", applicationPath];
NSUserDefaults *defaults=;
    ;
    ;
    ;
}

@end

有一点需要注意的是,如果把上面代码编译成动态库并通过环境变量DYLD_INSERT_LIBRARIES注入,由于NSTask会继承父程序的所有环境变量,建议通过绝对路径来指定,并放在app的沙盒可以访问的位置。适用于以下版本


P.S. 用之前post的app web view调试方法 https://www.52pojie.cn/thread-1068599-1-1.html ,可以看出小程序其实是个web容器,可以通过web调试器看到里面的布局。


可惜网络请求是通过底层的原生代码完成的,没办法直接调试,有时间我还会进一步研究。

shawhero928 发表于 2020-1-21 10:56

异界封侯 发表于 2020-1-20 13:35
微信双开?有这么费劲吗

我记得是cmd+N    难倒最新的10.15双开软件快捷键失效了么= =

kuruite 发表于 2020-1-19 22:30

来观摩一下

yonghune 发表于 2020-1-19 22:48

点进来看懵了系列= =

学士天下 发表于 2020-1-20 08:28

感觉挺厉害的,给作者点赞!!!

洋辣子 发表于 2020-1-20 09:27

感谢分享

linguo2625469 发表于 2020-1-20 10:47

挺厉害的,学习一下

hu3167343 发表于 2020-1-20 11:17

Lz牛逼,学习了~

异界封侯 发表于 2020-1-20 13:35

微信双开?有这么费劲吗

搜索中文字符串 发表于 2020-1-20 14:56

膜拜大神深分析

riverwind 发表于 2020-1-20 14:57

技术贴,涨姿势,谢谢分享!
页: [1] 2 3
查看完整版本: 新版Mac wx双开的实现