Mac下对ShadowsocksX进行Hook,实现自动更新账号

原文链接:Mac下对ShadowsocksX进行Hook,实现自动更新账号


使用ShadowsocksX的人群有两种,第一种自己购买的账号,第二种就是在网上找临时的账号,但临时账号,过几小时,密码都会改变,又得重新找开网站,重新设置。今天就针对这种情况对ShadowsocksX进行Hook,达到启动APP后,自动更新账号密码的目的。
编写工具为EasySIMBL
一. 识别二维码

二. 解码字符串

三. 保存账号信息


一. 识别二维码

class_dump导出头文件后,你会发现里面有Zxing二维码扫描框架,那我们就直接使用Zxing来进行二维码识别,代码如下:

+ (NSString *)scanQRCodeWithImage:(NSImage *)image {
    if (image == nil) {
        return nil;
    }
    
    CGImageRef imageToDecode = [VPNHelper nsImageToCGImageRef:image];
    ZXLuminanceSource *source = [[objc_getClass("ZXCGImageLuminanceSource") alloc] initWithCGImage:imageToDecode];
    ZXBinaryBitmap *bitmap = [objc_getClass("ZXBinaryBitmap") binaryBitmapWithBinarizer:[objc_getClass("ZXHybridBinarizer") binarizerWithSource:source]];
    
    ZXDecodeHints *hints = [objc_getClass("ZXDecodeHints") hints];
    
    ZXMultiFormatReader *reader = [objc_getClass("ZXMultiFormatReader") reader];
    ZXResult *result = [reader decode:bitmap
                                hints:hints
                                error:nil];
    if (result) {
        return result.text;
    } else {
        return nil;
    }
}

扫描得到的信息为’ss://cmM0LW1kNToyMDQ2NzIzMkAxMzguNjguNjEuNDI6MjM0NTY=’

二. 解码字符串

由于ShadowsocksX可以生成二维码,那就去查找在哪生成的二维码。在所有.h文件中,搜索qrcode后,在SWBAppDelegate.h文件中找到 showQRCode方法,代码如下:

void -[SWBAppDelegate showQRCode](void * self, void * _cmd) {
    r12 = self;
    rax = [ShadowsocksRunner generateSSURL]; // 生成二维码URL
    rax = [rax retain];
    if (rax != 0x0) {
            var_30 = rax;
            rax = [SWBQRCodeWindowController alloc];
            rax = [rax initWithWindowNibName:@"QRCodeWindow"];
            rdi = r12->qrCodeWindowController;
            r12->qrCodeWindowController = rax;
            [rdi release];
            var_38 = r12->qrCodeWindowController;
            rbx = [[var_30 absoluteString] retain];
            [var_38 setQrCode:rbx];
            [rbx release];
            [r12->qrCodeWindowController showWindow:r12];
            [*_NSApp activateIgnoringOtherApps:0x1];
            rbx = [[r12->qrCodeWindowController window] retain];
            [rbx makeKeyAndOrderFront:0x0];
            [rbx release];
            rax = var_30;
    }
    [rax release];
    return;
}

不难发现,[ShadowsocksRunner generateSSURL]生成二维码字符串,那我们继续

void * +[ShadowsocksRunner generateSSURL](void * self, void * _cmd) {
    rax = [ShadowsocksRunner isUsingPublicServer];
    rbx = 0x0;
    if (rax == 0x0) {
            r15 = [[ShadowsocksRunner configForKey:@"proxy encryption"] retain];
            var_38 = r15;
            r13 = [[ShadowsocksRunner configForKey:@"proxy password"] retain];
            rbx = [[ShadowsocksRunner configForKey:@"proxy ip"] retain];
            rax = [ShadowsocksRunner configForKey:@"proxy port"];
            rax = [rax retain];
            var_40 = rax;
            stack[2048] = rax;
            rcx = r15;
            r12 = r13;
            r15 = rbx;
            rbx = [[NSString stringWithFormat:@"%@:%@@%@:%@", rcx, r12, r15, stack[2048]] retain];
            var_30 = rbx;
            [var_40 release];
            [r15 release];
            [r12 release];
            [var_38 release];
            rdi = rbx;
            r14 = [[rdi dataUsingEncoding:0x4] retain];
            r15 = [[r14 base64Encoding] retain];
            [r14 release];
            r12 = [[NSString stringWithFormat:@"ss://%@", r15] retain];
            rbx = [[NSURL URLWithString:r12] retain];
            [r12 release];
            [r15 release];
            [var_30 release];
    }
    rdi = rbx;
    rax = [rdi autorelease];
    return rax;
}

从代码中可以看出,该方法对账号的地址,端口,密码,加密方式进行拼接,然后Base64编码。

三. 保存账号信息

既然有了账号信息,剩下的就是将该信息存储,我们知道ShadowsocksX有个服务器设定页面,不难想象,该类里肯定有对账号进行增删改查的功能,用Interface Inspector(Mac下的UI查看工具,相当于iOS的Reveal)工具可知服务器设定页面属于SWBConfigWindowController类,在该类中,我们发现了-(void)OK:(id)arg1;,该方法是在我们修改信息后,点击确定所执行的方法,那我们支看看这里都做了什么。

void -[SWBConfigWindowController OK:](void * self, void * _cmd, void * arg2) {
    rbx = self;
    if ([self saveCurrentProfile] != 0x0) {
            [rbx saveSettings];
            r14 = [[rbx window] retain];
            [r14 performClose:rbx];
            rdi = r14;
            [rdi release];
    }
    else {
            rdi = rbx;
            [rdi shakeWindow];
    }
    return;
}

该方法简单来说调用了两个重要方法saveCurrentProfile和saveSettings,saveCurrentProfile方法只是把当前修改的账号信息保存,这方法我们忽略,重点来看看saveSettings方法。

oid -[SWBConfigWindowController saveSettings](void * self, void * _cmd) {
    rdx = self->configuration;
    [ProfileManager saveConfiguration:rdx];
    [ShadowsocksRunner reloadConfig];
    rbx = [[self delegate] retain];
    [rbx configurationDidChange];
    rdi = rbx;
    [rdi release];
    return;
}

由代码可知,保存账号信息调用了[ProfileManager saveConfiguration:rdx];和[ShadowsocksRunner reloadConfig];那我们只要手动的调用这两方法就可以了。我的实现代码如下:

Profile *profile = [self initProfileWithURL:url]; // 账号的Model
Configuration *configuration = [objc_getClass("ProfileManager") performSelector:@selector(configuration)];
configuration.profiles = [@[profile] mutableCopy];
configuration.current = 0;
[objc_getClass("ProfileManager") saveConfiguration:configuration];
[objc_getClass("ShadowsocksRunner") reloadConfig];

由于该方法是异步,所以最后我们再调用下面方法刷新服务器列表

SWBAppDelegate *appDelegate = [NSApplication sharedApplication].delegate;
[appDelegate valueForKey:@"updateServersMenu"];

** In the end !**

原创文章如需转载,请注明出处。

3 个赞

哥们的这个markdown排版貌似有点问题,你重新编辑编辑?我再来发个微博宣传一下呀~

但是shadowsocks不是开源的吗。。。。

已重新排版。

张总好,我是小白哈,结果不重要。重要的是享受整个逆向,编译,调试的过程。

我更新到导航帖去

:+1:期待更多文章

关键这个app是开源的啊,你直接下载源码编译不好吗。。。。

他只是拿来练练手,大佬们不要太认真

不要抹杀我等新手的热情:joy:

新人路过学习~~赞一个