# 手机窃听
>华为面试时几个面试官反复提及手机窃听的技术手段问题。
>
>我仅回答了画像圈定问题,没有涉及到潜在技术问题。
>
>所以回来后检索了一下相关信息,才知道原来还有侧信道攻击的事。
常规认为的方式是,APP 申请了麦克风权限,后台静默监听关键字——类似 siri 的响应。
这里闹出过一个全民皆知的笑话——[NEX化身“流氓鉴定器”?升降式摄像头让偷拍无处可藏!](https://www.sohu.com/a/239463633_100206155)
而2019年3·15,《IT 时报》提出了两个技术实现方案,在未授权麦克风权限的情况下,窃听用户谈话关键词。
1. 加速器“窃听”扬声器
2. “浏览器指纹”乱点鸳鸯谱
## 加速器“窃听”扬声器
在网络与分布式系统安全会议(NDSS)上,浙江大学网络空间安全学院任奎团队、加拿大麦吉尔大学、多伦多大学学者团队展示了一项最新的研究成果——智能手机App可在用户不知情、无需系统授权的情况下,利用手机内置加速度传感器采集手机扬声器所发出声音的震动信号,实现对用户语音的窃听。

标准的侧信道攻击。
通过加速器感知手机扬声器播放声音时的震动,然后后台算法还原语音,基本可以做到:
- 语音密码识别
- 语音敏感词识别
- 语音还原
但这里技术上**未还原环境音潜在的被窃听风险**。
## “浏览器指纹”乱点鸳鸯谱
这就是我回答里提到的,群体画像精准定位。
这个技术实现难度低,主要窃取和共享在同一局域网内或同一位置区域长期接触的家人、同事、朋友的使用习惯并进行配对和共享推荐,造成的危害相对较小。

这种应该是普遍的手法,安全可靠,群体画像后广告精准投放,转化会更高。当然也会给人留下被偷听的'幸存者偏差'错觉。
## 参考
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.  ## 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); } } } ```  ## 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 函数,然后修改入口信息为自定义函数信息。  ``` //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/)
2021年2月25日星期四
Posted by drovliu on 二月 25, 2021 with No comments
//调用方法
function printStack() {
var Exception= Java.use("java.lang.Exception");
var ins = Exception.$new("Exception");
var straces = ins.getStackTrace();
if (undefined == straces || null == straces)
{
return;
}
console.log("=============================Stack start=======================");
// console.log(JSON.stringify(straces[0]));
for (var i = 0; i < straces.length; i++)
{
var str = " " + JSON.stringify(straces[i].getClassName());//关键变更点,getStackTrace获取的是类,需要取类名信息才能有效打印
console.log(str);
}
console.log("");
console.log("=============================Stack end=======================\r\n");
Exception.$dispose();
}
//以下为使用方法
Java.perform(function () {
const StringBuilder = Java.use('java.lang.StringBuilder');
StringBuilder.toString.implementation = function () {
// send("in StringBuiler");
var result = this.toString();
console.log("=============================Stack Print=======================\r\n");
console.log(result);
return result;
};
});
2021年2月22日星期一
Posted by drovliu on 二月 22, 2021 with No comments
打开后缀为xls的excel文件,alt+f11呼出VBA。
双击ThisWorkbook,新建一个代码。
然后复制粘贴如下爆破代码,点击运行即可。
Public Sub 工作表保护密码破解()
Const DBLSPACE As String = vbNewLine & vbNewLine
Const AUTHORS As String = DBLSPACE & vbNewLine & _
"作者:McCormick JE McGimpsey "
Const HEADER As String = "工作表保护密码破解"
Const VERSION As String = DBLSPACE & "版本 Version 1.1.1"
Const REPBACK As String = DBLSPACE & ""
Const ZHENGLI As String = DBLSPACE & ""
Const ALLCLEAR As String = DBLSPACE & "该工作簿中的工作表密码保护已全部解除!!" & DBLSPACE & "请记得另保存" _
& DBLSPACE & "注意:不要用在不当地方,要尊重他人的劳动成果!"
Const MSGNOPWORDS1 As String = "该文件工作表中没有加密"
Const MSGNOPWORDS2 As String = "该文件工作表中没有加密2"
Const MSGTAKETIME As String = "解密需花费一定时间,请耐心等候!" & DBLSPACE & "按确定开始破解!"
Const MSGPWORDFOUND1 As String = "密码重新组合为:" & DBLSPACE & "$$" & DBLSPACE & _
"如果该文件工作表有不同密码,将搜索下一组密码并修改清除"
Const MSGPWORDFOUND2 As String = "密码重新组合为:" & DBLSPACE & "$$" & DBLSPACE & _
"如果该文件工作表有不同密码,将搜索下一组密码并解除"
Const MSGONLYONE As String = "确保为唯一的?"
Dim w1 As Worksheet, w2 As Worksheet
Dim i As Integer, j As Integer, k As Integer, l As Integer
Dim m As Integer, n As Integer, i1 As Integer, i2 As Integer
Dim i3 As Integer, i4 As Integer, i5 As Integer, i6 As Integer
Dim PWord1 As String
Dim ShTag As Boolean, WinTag As Boolean
Application.ScreenUpdating = False
With ActiveWorkbook
WinTag = .ProtectStructure Or .ProtectWindows
End With
ShTag = False
For Each w1 In Worksheets
ShTag = ShTag Or w1.ProtectContents
Next w1
If Not ShTag And Not WinTag Then
MsgBox MSGNOPWORDS1, vbInformation, HEADER
Exit Sub
End If
MsgBox MSGTAKETIME, vbInformation, HEADER
If Not WinTag Then
Else
On Error Resume Next
Do 'dummy do loop
For i = 65 To 66: For j = 65 To 66: For k = 65 To 66
For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66
For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66
For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126
With ActiveWorkbook
.Unprotect Chr(i) & Chr(j) & Chr(k) & _
Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & _
Chr(i3) & Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
If .ProtectStructure = False And _
.ProtectWindows = False Then
PWord1 = Chr(i) & Chr(j) & Chr(k) & Chr(l) & _
Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
MsgBox Application.Substitute(MSGPWORDFOUND1, _
"$$", PWord1), vbInformation, HEADER
Exit Do 'Bypass all for...nexts
End If
End With
Next: Next: Next: Next: Next: Next
Next: Next: Next: Next: Next: Next
Loop Until True
On Error GoTo 0
End If
If WinTag And Not ShTag Then
MsgBox MSGONLYONE, vbInformation, HEADER
Exit Sub
End If
On Error Resume Next
For Each w1 In Worksheets
'Attempt clearance with PWord1
w1.Unprotect PWord1
Next w1
On Error GoTo 0
ShTag = False
For Each w1 In Worksheets
'Checks for all clear ShTag triggered to 1 if not.
ShTag = ShTag Or w1.ProtectContents
Next w1
If ShTag Then
For Each w1 In Worksheets
With w1
If .ProtectContents Then
On Error Resume Next
Do 'Dummy do loop
For i = 65 To 66: For j = 65 To 66: For k = 65 To 66
For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66
For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66
For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126
.Unprotect Chr(i) & Chr(j) & Chr(k) & _
Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
If Not .ProtectContents Then
PWord1 = Chr(i) & Chr(j) & Chr(k) & Chr(l) & _
Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
MsgBox Application.Substitute(MSGPWORDFOUND2, _
"$$", PWord1), vbInformation, HEADER
'leverage finding Pword by trying on other sheets
For Each w2 In Worksheets
w2.Unprotect PWord1
Next w2
Exit Do 'Bypass all for...nexts
End If
Next: Next: Next: Next: Next: Next
Next: Next: Next: Next: Next: Next
Loop Until True
On Error GoTo 0
End If
End With
Next w1
End If
MsgBox ALLCLEAR & AUTHORS & VERSION & REPBACK & ZHENGLI, vbInformation, HEADER
End Sub
订阅:
博文 (Atom)