一个人的闲言碎语

dr0v

blog.drov.com.cn
一个人碎碎念。
About Me
A lazy security employee.

2021年4月20日星期二

# 手机窃听

>华为面试时几个面试官反复提及手机窃听的技术手段问题。
>
>我仅回答了画像圈定问题,没有涉及到潜在技术问题。
>
>所以回来后检索了一下相关信息,才知道原来还有侧信道攻击的事。

常规认为的方式是,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)

2021年4月16日星期五

# 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/)
Categories: , ,

2021年2月25日星期四

//调用方法
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日星期一

 打开后缀为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