前一段时间想到一个问题:Android平台上能否通过一个app来修改系统相机所拍摄到的图像数据?如果可以的话,那基于此能做些什么?
首先,直觉告诉我这种修改是可行的。其次,很容易就可以想到,若能通过一个app来修改相机的图像数据,那岂不是可以任意篡改其他应用的二维码扫描结果?基于这样的问题和目标,我开始了Hook API的学习和原型的开发,为期一周,最终实现了这样的一个app,整理于此。
应该修改哪个API?
Android camera的preview图像数据都是通过回调函数onPreviewFrame(byte[] data, Camera camera)传递给应用层的app来使用,大家所熟知的微信,支付宝等二维码扫描功能都是在此处拿到图像数据加以分析扫描,所以我的目标就是修改这里的byte[] data。这是一个java层的回调函数,直接修改java层的API并非易事,所以我的着手点定位在native C层。此时就必须通过分析Android source code来找出需要hook的native API了。从onPreviewFrame一步步往下层追踪可以发现,camera从驱动层获取到数据后最终会经过native层的camera service来处理各个应用所注册的preview callback,具体实现在frameworks/base/services/camera/libcameraservice/CameraService.cpp中,其中有个接口
void CameraService::Client::handlePreviewData(int32_t msgType,
const sp<IMemory>& mem,
camera_frame_metadata_t *metadata) {
............
if (c != 0) {
// Is the received frame copied out or not?
if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
LOG2("frame is copied");
copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);
} else {
LOG2("frame is forwarded");
mLock.unlock();
c->dataCallback(msgType, mem, metadata);
}
} else {
mLock.unlock();
}
}
可以发现最终会调用copyFrameAndPostCopiedFrame函数,这个函数将图片数据进行了拷贝,并执行应用层相应的callback。也就是说应用层onPreviewFrame所拿到的数据仅仅是一份拷贝,这也恰好是我所需要的,因为这样我们即使修改了这里的拷贝后的图片数据,也不会影响系统相机本身的取景画面。是否可以说copyFrameAndPostCopiedFrame和handlePreviewData都可以是我们hook的目标?其实不然,因为它们都是内部函数调用,编译完成后其相对函数地址就已经确定存在于libcameraservice.so文件中,要用我们自己编写的函数地址来替换这个地址可并不容易,而且Android版本繁多,libcameraservice.so的实现也不停地变化,直接hook难保兼容性。继续跟踪copyFrameAndPostCopiedFrame,其实现如下: