@witchan wrote:
原文链接: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 !**
原创文章如需转载,请注明出处。
Posts: 11
Participants: 7