一个人的闲言碎语

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

2020年12月29日星期二


苹果M1芯片基于ARM可直接在新版 MacBook 上使用iOS应用 , 也就是多数iOS应用都可以在MacBook上使用。

不过部分 APP 依然不支持在MacBook 上安装(在 appstore 上搜索不到),于是大家就开始直接下载 ipa 包进行安装运行了。

为了避免从第三方平台下载导致的安全问题,找到一个通过 '同步/备份机制' 绕过下载验证的方案,具体方案如下,理论上可安装任意 iOS APP。

  1. 在macOS应用商店里下载Apple Configurator 2工具 ,安装该软件后打开并连接你的iPhone或iPad下载APP。
  2. 在Apple Configurator 2工具里点击你连接的iPhone或iPad , 然后右键选择添加、添加APP、搜索你要的APP。
  3. 找到你要的APP后点击添加就可以自动下载 , 下载完成会提示你的设备已存在该应用,此时切记不要点击替换。(这里注意到,保持该界面不动,先进行4,提取缓存的 ipa 文件后再处理这里的应用;另,如果设备未安装,会直接进行安装,不会保留缓存 ipa 文件。)
  4. 使用快捷键组合Command+Shift+G打开以下路径,打开这个路径就可以看到下载好的IPA文件将其复制出来。
  1. ~/Library/Group Containers/K36BKF7T3D.group.com.apple.configurator/Library/Caches/Assets/TemporaryItems/MobileApps/
    
5. 双击IPA文件进行安装 , 打开会报错没有权限 , 打开终端然后输入以下命令对特定路径的APP进行权限处理即可。
  1. sudo xattr -r -d com.apple.quarantine /Applications/WeChat.app
Categories: , ,

2020年11月26日星期四

报错信息:

~ $ git
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

修复方案:

~ $ xcode-select --install
xcode-select: note: install requested for command line developer tools

触发 xcode 更新,确认更新即可。




Categories: , ,

2020年7月22日星期三

使用brew/apt方式安装的afl没有afl-qemu-trace(不支持使用QEMU模式),所以我们需要下载afl的源码自己编译。

0x1 安装配置

编译完成后,需要配置qemu环境。不过,afl提供了一个脚本,在qemu-mode文件夹下的build_qemu_support.sh。运行这个脚本来配置qemu环境,但qemu-mode只支持linux,macOS可以在docker上使用,docker使用参考macOS上使用kali-linux for docker
$ ./build_qemu_support.sh
=================================================
AFL binary-only instrumentation QEMU build script
=================================================

[*] Performing basic sanity checks...
[-] Error: QEMU instrumentation is supported only on Linux.
编译成功信息如下:
[+] Build process successful!
[*] Copying binary...
-rwxr-xr-x 1 root root 10956864 Dec 13 12:26 ../afl-qemu-trace
[+] Successfully created '../afl-qemu-trace'.
[*] Testing the build...
[+] Instrumentation tests passed.
[+] All set, you can now use the -Q mode in afl-fuzz!

遇到的坑

  1. 运行后会提示libtool等资源库没有安装,使用sudo apt install安装即可:
    $ ./build_qemu_support.sh
    =================================================
    AFL binary-only instrumentation QEMU build script
    =================================================
    [*] Performing basic sanity checks...
    [-] Error: 'libtool' not found, please install first.
    $ apt-get install libtool-bin
    
  2. 安装一些软件包时,有时会出现找不到glib2的错误:
    $ apt install glib2
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    E: Unable to locate package glib2
    
    查看build_qemu_support.sh相关代码,需要在/usr/include/glib-2.0/或者/usr/local/include/glib-2.0/有相关库:
    if [ ! -d "/usr/include/glib-2.0/" -a ! -d "/usr/local/include/glib-2.0/" ]; then
    echo "[-] Error: devel version of 'glib2' not found, please install first."
    exit 1
    
    可通过安装以下工具来解决:
    sudo apt-get install libgtk2.0-dev
    
  3. qemu编译错误:
    util/memfd.c:40:12: error: static declaration of 'memfd_create' follows non-static declaration
    static int memfd_create(const char *name, unsigned int flags)
             ^~~~~~~~~~~~
    In file included from /usr/include/x86_64-linux-gnu/bits/mman-linux.h:115,
                  from /usr/include/x86_64-linux-gnu/bits/mman.h:45,
                  from /usr/include/x86_64-linux-gnu/sys/mman.h:41,
                  from /root/afl-2.52b/qemu_mode/qemu-2.10.0/include/sysemu/os-posix.h:29,
                  from /root/afl-2.52b/qemu_mode/qemu-2.10.0/include/qemu/osdep.h:104,
                  from util/memfd.c:28:
    
    afl默认qemu版本太老,官方已经patch
    ./configure
    @@ -3923,7 +3923,7 @@ fi
    # check if memfd is supported
    memfd=no
    cat > $TMPC << EOF
    -#include <sys/memfd.h>
    +#include <sys/mman.h>
    
    ./util/memfd.c
    @@ -31,9 +31,7 @@
    #include "qemu/memfd.h"
    -#ifdef CONFIG_MEMFD
    -#include <sys/memfd.h>
    -#elif defined CONFIG_LINUX
    +#if defined CONFIG_LINUX && !defined CONFIG_MEMFD
    
    修改完成后使用如下指令重打包,再修改build_qemu_support.sh里的QEMU_SHA384重新编译即可,SHA384值可以使用sha384sum获取:
    $ tar -Jcf qemu-2.10.0.tar.xz qemu-2.10.0/
    $ sha384sum qemu-2.10.0.tar.xz
    

更换qemu版本

如果想使用更新版本qemu,可以直接将build_qemu_support.sh设置的版本换成官方的较新版本,但更换版本后问题较多:
#VERSION="2.10.0"
VERSION="2.12.1"
#QEMU_SHA384="68216c935487bc8c0596ac309e1e3ee75c2c4ce898aab796faa321db5740609ced365fedda025678d0"
QEMU_SHA384="92957551a3a21b1ed48dc70d9dd91905859a5565ec98492ed709a3b64daf7c5a0265d670030ee7e6d16da96436795435"
  1. patch错误:
    patching file linux-user/elfload.c
    Hunk #2 succeeded at 2233 (offset 146 lines).
    Hunk #3 succeeded at 2268 (offset 146 lines).
    patching file accel/tcg/cpu-exec.c
    Hunk #1 succeeded at 37 (offset 1 line).
    Hunk #2 succeeded at 147 with fuzz 2 (offset 1 line).
    Hunk #3 FAILED at 369.
    1 out of 3 hunks FAILED -- saving rejects to file accel/tcg/cpu-exec.c.rej
    
    patch针对的是上层路径的文件更新,可以直接注释掉
    #patch -p1 <../patches/cpu-exec.diff || exit 1
    
  2. 缺少pixman,安装即可
    apt-get install libpixman*
    
  3. LINK错误:
    LINK    x86_64-linux-user/qemu-x86_64
    linux-user/syscall.o: In function `do_syscall':
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/syscall.c:11983: undefined reference to `afl_forksrv_pid'
    linux-user/elfload.o: In function `load_elf_image':
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    collect2: error: ld returned 1 exit status
    Makefile:193: recipe for target 'qemu-x86_64' failed
    make[1]: *** [qemu-x86_64] Error 1
    Makefile:478: recipe for target 'subdir-x86_64-linux-user' failed
    make: *** [subdir-x86_64-linux-user] Error 2
    
    上层patch中对源码文件做了修改,导致部分外部变量没有导入,注释掉相关patch即可:
    # patch -p1 <../patches/elfload.diff || exit 1
    # patch -p1 <../patches/syscall.diff || exit 1
    
  4. afl-qemu-trace测试失败
    [+] Successfully created '../afl-qemu-trace'.
    [*] Testing the build...
    [-] Error: afl-qemu-trace instrumentation doesn't seem to work!
    
    应该是用64位afl-qemu-trace工具测试32位程序引起的,忽略即可,或者通过如下指令指定32位架构:
    $ CPU_TARGET=i386 ./build_qemu_support.sh
    
    结果如下,实际也是忽略了测试:
    [+] Successfully created '../afl-qemu-trace'.
    [!] Note: can't test instrumentation when CPU_TARGET set.
    [+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!
    

0x2 使用afl-qemu fuzz

0x21 同cpu架构程序

以系统中wget指令为例,使用如下指令执行fuzz,-Q参数表示使用qemu模式;-m参数设置使用内存大小,不设置则默认200MB:
$ afl-fuzz -i fuzz_in -o fuzz_out -m 200 -Q wget @@
运行成功界面如下(ubuntu for docker):

0x22 不同cpu架构程序

0x221 获取目标文件

Android adbd程序为例,获取adbd文件:
$ file /system/bin/adbd
/system/bin/adbd: ELF executable, 64-bit LSB arm64, static, for Android 28, BuildID=2ef781f7497eaad0b8ba145996afd9a1, not stripped
$ adb pull /system/bin/adbd ./

0x222 编译目标架构qemu模式

如果fuzz的程序与qemu架构不同,则可能出现如下错误,需要用之前方式指定正确架构进行编译qemu模式:
$ afl-fuzz -i fuzz_in -o fuzz_out/ -Q ./adbd @@
...
[-] Hmm, looks like the target binary terminated before we could complete a
    handshake with the injected code. There are two probable explanations:

    - The current memory limit (200 MB) is too restrictive, causing an OOM
      fault in the dynamic linker. This can be fixed with the -m option. A
      simple way to confirm the diagnosis may be:

      ( ulimit -Sv $[199 << 10]; /path/to/fuzzed_app )

      Tip: you can use http://jwilk.net/software/recidivm to quickly
      estimate the required amount of virtual memory for the binary.

    - Less likely, there is a horrible bug in the fuzzer. If other options
      fail, poke <lcamtuf@coredump.cx> for troubleshooting tips.

[-] PROGRAM ABORT : Fork server handshake failed
         Location : init_forkserver(), afl-fuzz.c:2253
编译arm64版本工具,qemu支持的架构类型见./qemu-2.10.0/linux-user/host/目录,其中arm为32位arm,aarch64为64位arm:
$ ls  ./qemu-2.10.0/linux-user/host/
aarch64  arm  i386  ia64  mips  ppc  ppc64  s390  s390x  sparc  sparc64  x32  x86_64
以64位arm为例,编译指令如下:
$ CPU_TARGET=aarch64 ./build_qemu_support.sh
编译aarch64版本后,继续执行fuzz:
$ ../afl-2.52b/afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q ./adbd
依然错误:
[-] PROGRAM ABORT : Test case 'id:000000,orig:testcase' results in a crash
         Location : perform_dry_run(), afl-fuzz.c:2852

0x223 qemu user mode检查

由于可执行文件的架构不一样,所以需要按照QEMU user mode仿真做一下检查,运行成功后再进行fuzz,详情参考:IoT(七)通过qemu调试IoT固件和程序里的用户模式调试程序。
在该文中,adbd程序由于缺少外部资源依然执行失败(如需对有外部资源依赖的程序进行fuzz,可在该平台编译对应版本的fuzz工具),故重新选取能够执行成功的静态编译工具,如Android上的三方工具busybox:
$ file busybox
busybox: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped
$ qemu-aarch64 busybox
BusyBox v1.25.0-NetHunter (2016-03-19 19:36:31 EDT) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.

Usage: busybox [function [arguments]...]
   or: busybox --list[-full]
   or: busybox --install [-s] [DIR]
   or: function [arguments]...
....
此处可成功对busybox进行fuzz:
../afl-2.52b/afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q ./busybox @@
Categories: ,