Android NDK ollvm环境搭建与使用

admin 2025年7月2日10:15:40评论11 views字数 7724阅读25分44秒阅读模式
一、简言
之前一直想手撕OLLVM,无奈拖延症晚期,最近也是下定决心开始OLLVM相关的学习,今天是第一部分 ollvm的环境搭建与使用,后续会介绍ollvm的还原,包括静态分析,unidbg trace分析等。
二、配置OLLVM环境
1、下载LLVM

https://github.com/heroims/obfuscator/tree/llvm-9.0.1

2、编译

cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_CREATE_XCODE_TOOLCHAIN=ON -DLLVM_INCLUDE_TESTS=OFF -DLLVM_CREATE_XCODE_TOOLCHAIN=OFF../
make -j7
build成功后显示如下:
Android NDK ollvm环境搭建与使用
make -j7  经过漫长的等待
Android NDK ollvm环境搭建与使用
编译完成后,在build/bin 目录下,会生成所有二进制文件。
Android NDK ollvm环境搭建与使用
3、直接使用
提供了四种混淆方式
-mllvm -fla   //控制流扁平化-mllvm -sub   //指令替换-mllvm -bcf   //虚假控制流-mllvm -sobf  //字符串加密
可以对 .c 文件直接进行混淆:
$path_xxxx/build/bin/clang test.c -o -mllvm -sub -mllvm -fla
三、整合到Android NDK中
先找到自己ndk的目录
/Users/xxxxxx/Library/Android/sdk/ndk/18.1.5063045/toolchains/llvm
我这边选择的版本是18.1.5063045,在编译过程中尝试过21、22、25版本,都报链接错误,选择了较低的18版本,编译成功了。后续会尝试解决该报错。
找到目录后,将obfuscator编译好的build/binbuild/lib这2个文件夹直接拷贝到../llvm/prebuilt/darwin-x86_64/文件夹下,直接覆盖替换。当然最好将原llvm文件夹备份。防止不能用,能及时恢复。
Android NDK ollvm环境搭建与使用
接下来编辑 CMakeLists.txt文件,设置混淆方案。
Android NDK ollvm环境搭建与使用
四、效果展示
这边拿我之前写的一个crackme作为例子,so的主要逻辑就是一个字符串变换异或再AES加密,IDA反编译代码如下:
Android NDK ollvm环境搭建与使用
Graph如下:
Android NDK ollvm环境搭建与使用
混淆方案 只有 -mllvm -fla时,IDA反编译代码如下:
Android NDK ollvm环境搭建与使用
Graph如下:
Android NDK ollvm环境搭建与使用
可以看到控制流平坦化,已经加了一些while逻辑,看起来也更复杂了。但是也不是特别复杂。
接下来把混淆方案调整为 -mllvm -fla -mllvm -sub -mllvm -bcf -mllvm -sobf
加上了控制流平坦化、虚假控制流、指令替换、字符串加密
IDA反编译代码如下:
{  int v3; // r2  int v4; // r1  int ArrayLength; // r0  int ByteArrayElements; // r0  void *v7; // r0  int v8; // r12  int v9; // r3  int v10; // r1  int v11; // r2  int v12; // r0  size_t v13; // r0  int v14; // r1  int v15; // r12  int v16; // r1  int v17; // r2  int v18; // r12  int v19; // r1  char v20; // r0  int v21; // r12  int v22; // r1  int v23; // r0  int v24; // r0  int v26; // r0  int v27; // r0  void *v28; // r0  int v29; // r1  int v30; // lr  int v31; // r12  int v32; // r1  int v33; // r2  int v34; // r0  int v35; // r1  int v36; // r0  char v37; // r0  _BYTE v38[8]; // [sp-68h] [bp-110h] BYREF  int v39; // [sp-60h] [bp-108h] BYREF  int v40; // [sp-58h] [bp-100h] BYREF  int v41; // [sp-50h] [bp-F8h] BYREF  _BYTE v42[8]; // [sp-48h] [bp-F0h] BYREF  _BYTE v43[8]; // [sp-40h] [bp-E8h] BYREF  int v44; // [sp-38h] [bp-E0h] BYREF  _BYTE v45[8]; // [sp-30h] [bp-D8h] BYREF  _DWORD v46[2]; // [sp-28h] [bp-D0h] BYREF  _BYTE v47[8]; // [sp-20h] [bp-C8h] BYREF  int v48; // [sp-18h] [bp-C0h]  _DWORD v49[2]; // [sp-10h] [bp-B8h] BYREF  int v50; // [sp-8h] [bp-B0h]  int v51; // [sp+0h] [bp-A8h] BYREF  int v52; // [sp+4h] [bp-A4h]  int *v53; // [sp+8h] [bp-A0h]  _BYTE *v54; // [sp+Ch] [bp-9Ch]  _BYTE *v55; // [sp+10h] [bp-98h]  int *v56; // [sp+14h] [bp-94h]  _BYTE *v57; // [sp+18h] [bp-90h]  _DWORD *v58; // [sp+1Ch] [bp-8Ch]  _BYTE *v59; // [sp+20h] [bp-88h]  _DWORD *v60; // [sp+24h] [bp-84h]  int *v61; // [sp+28h] [bp-80h]  int v62; // [sp+30h] [bp-78h]  size_t v63; // [sp+34h] [bp-74h]  _BYTE *v64; // [sp+38h] [bp-70h]  _BYTE *v65; // [sp+3Ch] [bp-6Ch]  _DWORD *v66; // [sp+40h] [bp-68h]  _DWORD *v67; // [sp+44h] [bp-64h]  int *v68; // [sp+48h] [bp-60h]  int v69; // [sp+4Ch] [bp-5Ch]  int v70; // [sp+50h] [bp-58h]  int v71; // [sp+54h] [bp-54h]  int v72; // [sp+58h] [bp-50h]  int v73; // [sp+5Ch] [bp-4Ch]  char v74; // [sp+62h] [bp-46h]  char v75; // [sp+63h] [bp-45h]  _BYTE *v76; // [sp+64h] [bp-44h]  void **v77; // [sp+68h] [bp-40h]  size_t *v78; // [sp+6Ch] [bp-3Ch]  _DWORD *v79; // [sp+70h] [bp-38h]  size_t *v80; // [sp+74h] [bp-34h]  const char **v81; // [sp+78h] [bp-30h]  const char **v82; // [sp+7Ch] [bp-2Ch]  _BYTE *v83; // [sp+80h] [bp-28h]  v74 = (((_BYTE)x * ((_BYTE)x - 1)) & 1) == 0;  v75 = y < 10;  v73 = 966614150;  v72 = a3;  v71 = a2;  v70 = a1;  do  {    while ( 1 )    {      while ( 1 )      {        while ( 1 )        {          while ( 1 )          {            while ( 1 )            {              while ( 1 )              {                while ( 1 )                {                  while ( 1 )                  {                    while ( 1 )                    {                      while ( 1 )                      {                        v69 = v73;                        if ( v73 != -1744864028 )                          break;                        v76 = v47;                        v77 = (void **)v46;                        v78 = (size_t *)v43;                        v79 = v42;                        v80 = (size_t *)&v40;                        v81 = (const char **)&v39;                        v82 = (const char **)v38;                        v50 = v70;                        v49[0] = v71;                        v48 = v72;                        v46[0] = 0;                        v68 = &v51;                        v67 = v49;                        v66 = v46;                        v65 = v45;                        v64 = v42;                        v63 = 0;                        ArrayLength = _JNIEnv::GetArrayLength(v70, v72);                        *(v66 - 2) = ArrayLength;                        ByteArrayElements = _JNIEnv::GetByteArrayElements(*(v68 - 2), *(v67 - 2), v63);                        *((_DWORD *)v65 - 2) = ByteArrayElements;                        v7 = malloc(*(v66 - 2) + 1);                        *v77 = v7;                        qmemcpy(*v77, *((const void **)v65 - 2), *(v66 - 2));                        v8 = v63;                        *((_BYTE *)*v77 + *(v66 - 2)) = v63;                        v9 = *(v68 - 2);                        v10 = *(v67 - 2);                        v11 = *((_DWORD *)v65 - 2);                        v62 = v12;                        _JNIEnv::ReleaseByteArrayElements(v9, v10, v11, v8);                        *v78 = 16;                        v13 = *v78;                        *v79 = v38;                        v83 = &v38[-((v13 + 7) & 0xFFFFFFF8)];                        *((_DWORD *)v64 - 2) = v13;                        memset(v83, 0, *v78);                        v14 = v63;                        *v80 = v63;                        if ( y < 10 )                          v14 = 1;                        v15 = 1351927069;                        if ( !(((_BYTE)x * ((_BYTE)x - 1)) & 1) != v14 )                          v15 = -1092466268;                        v16 = v15;                        if ( y < 10 )                          v16 = -1092466268;                        if ( (((_BYTE)x * ((_BYTE)x - 1)) & 1) == 0 )                          v15 = v16;                        v73 = v15;                      }                      if ( v69 != -1197933675 )                        break;                      v17 = 1507000030;                      if ( *v80 < *v78 )                        v17 = -226534650;                      v73 = v17;                    }                    if ( v69 != -1092466268 )                      break;                    v73 = -1197933675;                  }                  if ( v69 != -784059637 )                    break;                  v73 = 1310980708;                }                if ( v69 != -757944169 )                  break;                v20 = aXs544rxnm0p4jv[*v78 - *v80 - 1];                v83[*v80] = ((v20 & 0xF6) + (~v20 & 9)) ^ 0x53;                v21 = -646556425;                v22 = -646556425;                if ( y < 10 )                  v22 = -784059637;                if ( (((_BYTE)x * ((_BYTE)x - 1)) & 1) == 0 )                  v21 = v22;                if ( y < 10 != !(((_BYTE)x * ((_BYTE)x - 1)) & 1) )                  v21 = -784059637;                v73 = v21;              }              if ( v69 != -646556425 )                break;              v37 = aXs544rxnm0p4jv[*v78 - *v80 - 1];              v83[*v80] = ((v37 & 0xA2) + (~v37 & 0x5D)) ^ 7;              v73 = -757944169;            }            if ( v69 != -226534650 )              break;            v18 = -646556425;            v19 = -646556425;            if ( y < 10 )              v19 = -757944169;            if ( (((_BYTE)x * ((_BYTE)x - 1)) & 1) == 0 )              v18 = v19;            if ( y < 10 != !(((_BYTE)x * ((_BYTE)x - 1)) & 1) )              v18 = -757944169;            v73 = v18;          }          if ( v69 != 966614150 )            break;          v3 = 1351927069;          if ( v74 != v75 )            v3 = -1744864028;          v4 = v3;          if ( v75 )            v4 = -1744864028;          if ( v74 )            v3 = v4;          v73 = v3;        }        if ( v69 != 1310980708 )          break;        ++*v80;        v73 = -1197933675;      }      if ( v69 != 1351927069 )        break;      v50 = v70;      v49[0] = v71;      v48 = v72;      v46[0] = 0;      v61 = &v51;      v60 = v49;      v59 = v47;      v58 = v46;      v57 = v45;      v56 = &v44;      v55 = v43;      v54 = v42;      v53 = &v41;      v52 = 0;      v26 = _JNIEnv::GetArrayLength(v70, v72);      *(v58 - 2) = v26;      v27 = _JNIEnv::GetByteArrayElements(*(v61 - 2), *(v60 - 2), v52);      *((_DWORD *)v57 - 2) = v27;      v28 = malloc(*(v58 - 2) + 1);      v29 = (int)v59;      *((_DWORD *)v59 - 2) = v28;      qmemcpy(*(void **)(v29 - 8), *((const void **)v57 - 2), *(v58 - 2));      v30 = v52;      *(_BYTE *)(*((_DWORD *)v59 - 2) + *(v58 - 2)) = v52;      v31 = *(v61 - 2);      v32 = *(v60 - 2);      v33 = *((_DWORD *)v57 - 2);      v51 = v34;      _JNIEnv::ReleaseByteArrayElements(v31, v32, v33, v30);      v35 = (int)v56;      *(v56 - 2) = 16;      v36 = *(_DWORD *)(v35 - 8);      *((_DWORD *)v55 - 2) = v38;      *((_DWORD *)v54 - 2) = v36;      memset(&v38[-((v36 + 7) & 0xFFFFFFF8)], 0, *(_DWORD *)(v35 - 8));      *(v53 - 2) = v52;      v73 = -1744864028;    }  }  while ( v69 != 1507000030 );  v23 = j_AES_ECB_PKCS7_Encrypt(*v77, v83);  *v81 = (const char *)v23;  *v82 = (const char *)&unk_F010;  v24 = strcmp(*v81, *v82);  *(_DWORD *)v76 = v24;  return *(_DWORD *)v76;}
Graph如下:
Android NDK ollvm环境搭建与使用
可以看到已经复杂非常多了。代码量也从20多行增加到了200多行,分析难度增大了很多。
五、总结:
本文介绍了Android NDK ollvm环境搭建与使用,后续会介绍如何对ollvm混淆进行还原。

原文始发于微信公众号(从黑客到保安):Android NDK ollvm环境搭建与使用

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年7月2日10:15:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Android NDK ollvm环境搭建与使用http://cn-sec.com/archives/4216881.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息