-
-
[原创]虚拟相机vcamera v18破解
-
发表于:
2025-10-14 10:54
1796
-
之前破解过作者一个版本的虚拟相机在b站发过视频
视频链接:
我破解的之前的版本也有人破解过
链接地址:108K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3M7X3g2$3k6i4u0U0j5#2)9J5c8Y4m8Q4x3V1j5I4y4K6V1J5x3U0j5K6y4#2)9J5k6h3S2@1L8h3H3`.
最近有客户找我破解同一个作者的另外一个版本的虚拟相机,索性研究一下
先查壳,邦邦免费加固,比较简单,只是对frida的简单检测,上面的链接已经对邦邦免费版的加固和检测做了非常详细的分析,用到了ebpf等各种工具
用frida先hook一下,有崩溃的迹象,可能是脚步注入太早,或者hook现成的时候地址无法访问,通过纠正脚本的到frida的hook代码
setTimeout(() => {
if (!Java.available) {
console.log("[系统] Java环境未就绪,跳过Hook执行");
return;
}
// 调用栈打印函数 - 确保能正常执行
function showSafeStacks() {
try {
const stackTrace = Java.use("java.lang.Thread").currentThread().getStackTrace();
if (stackTrace && stackTrace.length > 0) {
console.log("调用栈:");
const maxDepth = Math.min(stackTrace.length, 50);
for (let i = 0; i < maxDepth; i++) {
const elem = stackTrace[i];
console.log(` ${elem.getClassName()}.${elem.getMethodName()}:${elem.getLineNumber()}`);
}
}
} catch (e) {
console.log("调用栈打印失败: " + e.message);
}
}
Java.perform(() => {
console.log("[系统] 开始执行Hook逻辑...");
// Hook f1.g.a 方法
// Hook d1.g.b 方法(修复NumberFormatException问题)
try {
const D1GHookClass = Java.use("d1.g");
console.log("[d1.g] 成功获取类引用");
const d1TargetMethod = D1GHookClass["b"].overloads.find(m => m.argumentTypes.length === 1);
if (d1TargetMethod) {
d1TargetMethod.implementation = function(str) {
console.log("[d1.g.b] 被调用");
try {
console.log(`[参数] 值: ${str || 'null'}`);
} catch (e) {
console.log("[参数] 获取失败: " + e.message);
}
showSafeStacks();
try {
// 使用int范围内的数值,避免NumberFormatException
// 数组长度保持与原始方法一致(此处为2个元素)
const newResult = Java.array('java.lang.String', [
"2147483647", // int类型最大值,确保可被Integer.parseInt解析
Math.floor(Date.now() / 1000).toString() // 10位时间戳
// 如需3个元素,取消下面一行的注释
// "激活成功"
]);
// 打印修改后的返回值
console.log(`[返回值] 修改后的数组(长度:${newResult.length})内容:`);
for (let i = 0; i < newResult.length; i++) {
console.log(` 第${i+1}个元素: ${newResult[i]}`);
}
return newResult;
} catch (e) {
console.log("[d1.g.b] 调用失败: " + e.message);
throw e;
}
};
console.log("[d1.g] b方法Hook安装完成");
} else {
console.log("[d1.g] 未找到符合条件的b方法");
}
} catch (e) {
console.log("[d1.g] 处理出错: " + e.message);
}
// Hook java.lang.Integer 类方法
// Hook java.lang.Long 类方法(处理大整数)
console.log("[系统] 所有Hook安装完成");
});
}, 8000);总的来说,注入的时间延迟了一下,就解决了注入崩溃的问题
hook他激活的过程拿到了激活返回的数据,他需要请求服务器验证拿到激活的时间戳

然后通过计算时间的差值,来给用户使用时间。
这个虚拟相机还请求了root权限,不然没有版本跨进程通讯,
总的来说这个相机有启动了三个进程
一是相机app的主进程,主进程是java层和c++层的跨进程通讯
二是主进程通过java层在命令行运行二进制可执行文件vcmplay 传入参数来启动第二个进程,这个进程主要负责相机vcmpaly主进程和注入系统相机相机服务的libvc.so进程跨进程通讯,通讯的主要方式是ibinder,
三是注入系统服务的相机cameraserver的so文件。
如果应用root权限获取不到,或者网络问题,会出现进程启动失败的问题

这个相机需要激活码才能激活使用,通过分析java层,把他的界面全部hook了出来
激活需要请求服务器拿到验证信息

拿到的数据全是加密的
通过分析vcmplay二进制可执行文件可得到 请求的数据是rsa加密,秘钥通过stackplz的ebpf工具定位到了加密秘钥是128位的
还要说一句
这个应用很狡猾他把vcmplay加了一个so后缀,让人以为是so文件,需要加载到java的内存其实他是另外一个进程。
我hook相机服务cameraserver内的libvc.so发现frida一 attach相机服务就崩溃,不知道是不是检测,分析了很长时间,偶然发现vcmplay一次死掉的时机,竟然能hook了 怀疑可能是vcmplay的进程对cameraserve进程进行了检测
我用ida对vcmpay进程进行调试,发现他还有反调试,扫了一下proc/self/status内的tracepid来确定有没有被调试,而且更恐怖的是他发现反调试不是程序崩溃,他竟然通过root权限删除了安卓系统内的重要文件,然后手机重启进入双清状态,必须初始化系统才能进系统,手机内的所有东西都没了。我通过kernelpatch编写内核hook模块kpm过掉了反调试
大体的逻辑都弄清楚了,估计破解也是不容易的。
以后待续吧......
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!