# 手机窃听
>华为面试时几个面试官反复提及手机窃听的技术手段问题。
>
>我仅回答了画像圈定问题,没有涉及到潜在技术问题。
>
>所以回来后检索了一下相关信息,才知道原来还有侧信道攻击的事。
常规认为的方式是,APP 申请了麦克风权限,后台静默监听关键字——类似 siri 的响应。
这里闹出过一个全民皆知的笑话——[NEX化身“流氓鉴定器”?升降式摄像头让偷拍无处可藏!](https://www.sohu.com/a/239463633_100206155)
而2019年3·15,《IT 时报》提出了两个技术实现方案,在未授权麦克风权限的情况下,窃听用户谈话关键词。
1. 加速器“窃听”扬声器
2. “浏览器指纹”乱点鸳鸯谱
## 加速器“窃听”扬声器
在网络与分布式系统安全会议(NDSS)上,浙江大学网络空间安全学院任奎团队、加拿大麦吉尔大学、多伦多大学学者团队展示了一项最新的研究成果——智能手机App可在用户不知情、无需系统授权的情况下,利用手机内置加速度传感器采集手机扬声器所发出声音的震动信号,实现对用户语音的窃听。
![](https://n.sinaimg.cn/tech/crawl/245/w550h495/20200315/8134-iquxrui6911223.jpg)
标准的侧信道攻击。
通过加速器感知手机扬声器播放声音时的震动,然后后台算法还原语音,基本可以做到:
- 语音密码识别
- 语音敏感词识别
- 语音还原
但这里技术上**未还原环境音潜在的被窃听风险**。
## “浏览器指纹”乱点鸳鸯谱
这就是我回答里提到的,群体画像精准定位。
这个技术实现难度低,主要窃取和共享在同一局域网内或同一位置区域长期接触的家人、同事、朋友的使用习惯并进行配对和共享推荐,造成的危害相对较小。
![](https://n.sinaimg.cn/tech/crawl/141/w550h391/20200315/f1dc-iquxrui6911552.jpg)
这种应该是普遍的手法,安全可靠,群体画像后广告精准投放,转化会更高。当然也会给人留下被偷听的'幸存者偏差'错觉。
## 参考
1. [原来,手机是这样“窃听”你的](https://tech.sina.com.cn/i/2020-03-15/doc-iimxxstf9273990.shtml)
dr0v
blog.drov.com.cn一个人碎碎念。
A lazy security employee.
2021年4月20日星期二
2021年4月16日星期五
Posted by drovliu on 四月 16, 2021 with No comments
# hook原理 >在面微信的时候被开发的面试官问到了hook原理。我简单介绍了一般是替换函数地址的形式,保障先调用替换函数,再调用原始函数。 > >面试官的理解是有两种hook方式:一种是地址替换型,一种是代码插入型。 > >由此可知我对hook的不甚了解,所以专门找了相关资料进行学习。如文为学习总结。 ## hook方式 Hook技术无论对安全软件还是恶意软件都是十分关键的一项技术,其本质就是劫持函数调用。但是由于处于Linux 用户态,每个进程都有自己独立的进程空间,所以必须先注入到所要Hook 的进程空间,修改其内存中的进程代码,替换其过程表的符号地址。 APP 劫持三步走: 1. 注入进程 - ptrace - dlopen 2. hook 目标函数 - Java Hook - Static Field Hook:静态成员hook - Method Hook:函数hook - Native So Hook - GOT Hook:全局偏移表hook - SYM Hook:符号表hook - Inline Hook:函数内联hook 3. 执行自身代码 - 获取敏感信息 - 修改返回值 - etc. ![基于 ptrace的 hook 工作流程](https://box.kancloud.cn/4a4a6f8a7696648d181d7743f8e9a5f2_646x432.png) ## xposed hook 原理分析 xposed 虽然目前已经不更新了,不过依然是 Android 平台最著名、最广泛使用的 hook 框架。 xposed hook 工作原理: 1. 获取 root 权限 2. 替换/system/bin/app_process 3. app_process在启动过程中会加载XposedBridge.jar,完成对Zygote进程及其创建的Dalvik虚拟机的劫持 4. XposedBridge.jar中会根据用户所编写的 xposed 模块,对对应 classloader 中的 method 进行替换 ``` /** * * 将输入的Class中的Method方法的nativeFunc替换为xposedCallHandler * * @param env JniEnv * @param reflectedMethodIndirect 待反射的函数 * @param declaredClassIndirect 定义的class * @param slot 函数偏移量 * @param additionalInfoIndirect 添加的函数 * */ void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect, jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) { // 容错 if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) { dvmThrowIllegalArgumentException("method and declaredClass must not be null"); return; } // 根据函数的偏移量,从classloader中找到准备替换的函数。 ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect); Method* method = dvmSlotToMethod(declaredClass, slot); if (method == NULL) { dvmThrowNoSuchMethodError("Could not get internal representation for method"); return; } if (isMethodHooked(method)) { // already hooked return; } // 保存替换前的数据信息 XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo)); memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct)); hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect)); hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect)); // 替换函数方法 , 让nativeFunction指向本地的hookedMethodCallback SET_METHOD_FLAG(method, ACC_NATIVE); method->nativeFunc = &hookedMethodCallback; method->insns = (const u2*) hookInfo; method->registersSize = method->insSize; method->outsSize = 0; if (PTR_gDvmJit != NULL) { // reset JIT cache char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull)); if (currentValue == 0 || currentValue == 1) { MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true; } else { ALOGE("Unexpected current value for codeCacheFull: %d", currentValue); } } } ``` ![app_process](https://img-blog.csdn.net/20150821093617938) ## frida hook 原理 frida代码结构: ``` frida-core: Frida core library intended for static linking into bindings frida-gum: Low-level code instrumentation library used by frida-core bindings frida-python: Frida Python bindings frida-node: Frida Node.js bindings frida-qml: Frida Qml plugin frida-swift: Frida Swift bindings frida-tools: Frida CLI tools capstone: instruction disammbler ``` frida的工作模式有两种: - attach模式 attach到已经存在的进程,核心原理是ptrace修改进程内存,如果进程处于调试状态(traceid不等于0),则attach失败 - spawn模式 启动一个新的进程并挂起,在启动的同时注入frida代码,适用于在进程启动前的一些hook,如hook RegisterNative等,注入完成后调用resume恢复进程。 frida 的 hook 区分了 art 模式和 dalvik 模式。 ### Dalvik hook 实现 frida兼容了低版本的Android, 低于Android 5.0时,采用Dalvik虚拟机,其核心实现在replaceDalvikImplementation函数中。 frida-dalvik-hook 的原理和 xposed 的 hook 原理是一样的,把 java 函数变成 native 函数,然后修改入口信息为自定义函数信息。 ![dalvik 虚拟机执行 java 函数过程](https://mabin004.github.io/images/pasted-83.png) ``` //https://android.googlesource.com/platform/dalvik/+/6d874d2bda563ada1034d2b3219b35d800fc6860/vm/oo/Object.h#418 struct Method { ClassObject* clazz; /* method所属的类 public、native等*/ u4 accessFlags; /* 访问标记 */ u2 methodIndex; //method索引 //三个size为边界值,对于native函数,这3个size均等于参数列表的size u2 registersSize; /* ins + locals */ u2 outsSize; u2 insSize; const char* name;//函数名称 /* * Method prototype descriptor string (return and argument types) */ DexProto prototype; /* short-form method descriptor string */ const char* shorty; /* * The remaining items are not used for abstract or native methods. * (JNI is currently hijacking "insns" as a function pointer, set * after the first call. For internal-native this stays null.) */ /* the actual code */ const u2* insns; /* instructions, in memory-mapped .dex */ /* cached JNI argument and return-type hints */ int jniArgInfo; /* * Native method ptr; could be actual function or a JNI bridge. We * don't currently discriminate between DalvikBridgeFunc and * DalvikNativeFunc; the former takes an argument superset (i.e. two * extra args) which will be ignored. If necessary we can use * insns==NULL to detect JNI bridge vs. internal native. */ DalvikBridgeFunc nativeFunc; /* * Register map data, if available. This will point into the DEX file * if the data was computed during pre-verification, or into the * linear alloc area if not. */ const RegisterMap* registerMap; }; … … … function replaceDalvikImplementation (fn) { if (fn === null && dalvikOriginalMethod === null) { return; } //备份原来的method, if (dalvikOriginalMethod === null) { dalvikOriginalMethod = Memory.dup(methodId, DVM_METHOD_SIZE); dalvikTargetMethodId = Memory.dup(methodId, DVM_METHOD_SIZE); } if (fn !== null) { //自定的代码 implementation = implement(f, fn); let argsSize = argTypes.reduce((acc, t) => (acc + t.size), 0); if (type === INSTANCE_METHOD) { argsSize++; } // 把method变成native函数 /* * make method native (with kAccNative) * insSize and registersSize are set to arguments size */ const accessFlags = (Memory.readU32(methodId.add(DVM_METHOD_OFFSET_ACCESS_FLAGS)) | kAccNative) >>> 0; const registersSize = argsSize; const outsSize = 0; const insSize = argsSize; Memory.writeU32(methodId.add(DVM_METHOD_OFFSET_ACCESS_FLAGS), accessFlags); Memory.writeU16(methodId.add(DVM_METHOD_OFFSET_REGISTERS_SIZE), registersSize); Memory.writeU16(methodId.add(DVM_METHOD_OFFSET_OUTS_SIZE), outsSize); Memory.writeU16(methodId.add(DVM_METHOD_OFFSET_INS_SIZE), insSize); Memory.writeU32(methodId.add(DVM_METHOD_OFFSET_JNI_ARG_INFO), computeDalvikJniArgInfo(methodId)); //调用dvmUseJNIBridge为这个Method设置一个Bridge,本质上是修改结构体中的nativeFunc为自定义的implementation函数 api.dvmUseJNIBridge(methodId, implementation); patchedMethods.add(f); } else { patchedMethods.delete(f); Memory.copy(methodId, dalvikOriginalMethod, DVM_METHOD_SIZE); implementation = null; } } ``` ### ART hook实现 frida的ART hook实现也是把java method转为native method, 但ART的运行机制不同于Dalvik, 其实现也较为复杂。 ART虚拟机执行 Java 方法主要有两种模式: - quick code 模式:执行 arm 汇编指令 - Interpreter 模式:由解释器解释执行 Dalvik 字节码 所以 frida 要将 java method 转为 native method,需要将ARTMethod 结构进行如下修改: ``` patchMethod(methodId, { //jnicode入口entry_point_from_jni_改为自定义的代码 'jniCode': implementation, //修改为access_flags_为native 'accessFlags': (Memory.readU32(methodId.add(artMethodOffset.accessFlags)) | kAccNative | kAccFastNative) >>> 0, //art_quick_generic_jni_trampoline函数的地址 'quickCode': api.artQuickGenericJniTrampoline, //artInterpreterToCompiledCodeBridge函数地址 'interpreterCode': api.artInterpreterToCompiledCodeBridge }); ``` 参考链接 1. [动态注入技术(hook技术)](https://www.kancloud.cn/alex_wsc/android/504478) 2. [Xposed源码剖析——概述](https://blog.csdn.net/yzzst/article/details/47659987) 3. [Xposed源码剖析——app_process作用详解](https://blog.csdn.net/yzzst/article/details/47829657) 4. [Xposed源码剖析——Xposed初始化](https://blog.csdn.net/yzzst/article/details/47834077) 5. [Xposed源码剖析——hook具体实现](https://blog.csdn.net/yzzst/article/details/47913867) 6. [Frida源码分析](https://mabin004.github.io/2018/07/31/Mac%E4%B8%8A%E7%BC%96%E8%AF%91Frida/)
订阅:
博文 (Atom)
Search
Archive
Popular Posts
-
# hook原理 >在面微信的时候被开发的面试官问到了hook原理。我简单介绍了一般是替换函数地址的形式,保障先调用替换函数,再调用原始函数。 > >面试官的理解是有两种hook方式:一种是地址替换型,一种是代码插入型。 > >由此可知我对hook...
-
//调用方法 function printStack () { var Exception = Java . use ( "java.lang.Exception" ); var ins = Exception . $new ( &q...