最近我的SDK的宿主app被TrollFools注入动态库攻击了,是全局虚拟摄像头动态库,可以劫持摄像头,改为从本地视频读取。
然后我添加了动态库防护的代码,如下
// 通过遍历dyld_image检测非法注入的动态库
int dyld_count = _dyld_image_count();
for (int i = 0; i < dyld_count; i++) {
const char * imageName = _dyld_get_image_name(i);
NSString *res = [NSString stringWithUTF8String:imageName];
// 过滤非dylib后缀的路径
if(![res hasSuffix:@".dylib"]){
continue;
}
// 越狱设备动态库
if ([res containsString:@"/Library/MobileSubstrate/DynamicLibraries"]) {
return YES;
} else if([res containsString:@"/var/containers/Bundle/Application"]) {
return YES;
}
}
注入的动态库会出现在"/var/containers/Bundle/Application"中,但某些系统动态库在某些系统上也会出现在这里,这就导致了误杀的情况。
比如在iOS12的设备上,原本应该出现在/usr/lib/swift/libswift_Concurrency.dylib的库,出现在了"/var/containers/Bundle/Application"中。
请问该如何防护而不误杀?不同系统的动态库为何位置会有变化?
位置为什么发生变化请库克来回答。
如何不误杀,你既然有数据,那么直接做白名单就好了。
1 个赞
白名单难做的原因是我可以把恶意的动态库名称修改为某个系统动态库的名字,这样就可以被我放过了。我做白名单只能是做一套系统默认的动态库作为白名单。其实我只要能解决不误杀"/var/containers/Bundle/Application"中出现的系统动态库就好了,但是名字是个问题。不同的系统版本,不同的app,这个路径和会出现在这个路径下的动态库的名字还不确定。所以很难办了。
那可太多了,比如抖音直播想要禁止用虚拟摄像头动态库进行假直播,网约车的验证sdk想要禁止假司机视频验证。
误杀的情况出现在某个版本,宿主app突然添加了SwiftUI,然后在Other Linker Flags里添加了 -weak_framework SwiftUI,导致iOS12的用户反馈被误拦截了,查了以后才发现,libswift_Concurrency.dylib出现在了"/var/containers/Bundle/Application"中,之前Application是啥库也没有的。后来我还在有的iOS12上的Application里打印出了libswiftCore.dylib。这就导致我不敢用Application拦截了,生怕给其他宿主app集成我的SDK,给他们也误杀了。但是很多加固公司,他们会提供默认的白名单,过滤掉系统的swift动态库。不知道他们怎么办到的。自己用巨魔插件注入的动态库,修改成系统名字以后是可以绕过白名单的。
samlee1
(王子七七七)
7
上报他的样本,然后想怎么检测,就怎么检测咯,hook 检测 ,特征检测,然后在检测方法搞混淆 vmp
拿到他的样本 ,
请问拿到他的样本是什么意思?我只有买来的全局虚拟摄像头.dylib,然后就是trollStore和trollFools。哪个是样本?谢谢解答
dylib是可以随意起名字的啊,没办法根据名字来判断啊,完全可以把dylib名字改成和系统名字一样的。
一般白名单是根据系统版本来的,不同版本的系统上都有自己的白名单,因为老系统的动态库位置一定不会发生变化。另外你可以看一下动态库的加载顺序,应该是固定的。
samlee1
(王子七七七)
14
直接遍历 dylib 然后 vm_read 里面的内容,可疑的直接上报
vm_read指的是mach_vm_read_entry吗?读取内存地址的内容?那怎么算可疑的啊?你说的有点难啊
不同的app还不一样,有的宿主app集成了SwiftUI,还在Other Linker Flags里添加了-weak_framework SwiftUI,所以这个白名单还不一样。另外就算我有了白名单,"/var/containers/Bundle/Application"中的动态库我也不能确定是不是系统的,不能通过动态库名字判断,如果攻击的动态库改名字为swift相关的系统库,我该怎么区分呢?
我查了下vm_read,
// 使用 vm_read 函数读取数据
kr = vm_read(task, address, readSize, &dataBuffer, &dataSize);
这个需要task,也就是需要获取当前进程的任务端口,
// 获取当前进程的任务端口
if (task_for_pid(mach_task_self(), getpid(), &task)!= KERN_SUCCESS) {
printf(“Failed to get task port\n”);
return;
}
但现在是非越狱的场景,是没有这个权限的,拿不到端口。所以没办法用vm_read额