CTF

Mysterious app

Campus invasion

Posted by pic4xiu on September 27, 2019

本次记录一下本菜鸡小小的分析这个送给最好的TA

我是直接暴力把 apk 后缀改成 zip 后打开的,没想到大佬们可以发现这个神奇的网站进行拆包,学到了,但是要关闭屏蔽广告的插件,毕竟不能靠爱发电

然后我就开始研究 classes.dex 源码了,先把 dex 转成 jar ,然后用jd-gui一顿 xjb 分析,大致看了一下有一个获取路径

  public String getLuaPath()
  {
    initMain();
    StringBuilder localStringBuilder = new StringBuilder();
    localStringBuilder.append(getLocalDir());
    localStringBuilder.append("/main.lua");
    return localStringBuilder.toString();
  }

用到了 lua ~~~ ,程序对 lua 进行加载和解密,进入com\androlua\LuaActivity.class,一个public Object loadLib(String paramString)使用 jni 加载 so 文件,之后就开始了 so 文件的排查解密函数

我目前的思路就是通过排查 so 进而解出 main.lua 了,如果 main.lua 没什么问题就是 java 写的了

通过几个关键函数如luaL_loadstring,我们最终定位到了j_luaL_loadbufferx

int __fastcall luaL_loadbufferx(int a1, int a2, size_t size, int a4, int a5)
{
  int v5; // r10
  size_t v6; // r6
  int v7; // r11
  int v8; // r8
  _BYTE *v9; // r0
  int v10; // r1
  signed int v11; // r2
  _BYTE *v13; // [sp+8h] [bp-28h]
  size_t v14; // [sp+Ch] [bp-24h]

  v5 = a1;
  v6 = size;
  v7 = a2;
  v8 = a4;
  v13 = (_BYTE *)a2;
  v14 = size;
  if ( *(_BYTE *)a2 == 27 && *(_BYTE *)(a2 + 1) != 76 )
  {
    v9 = malloc(size);
    if ( v6 )
    {
      *v9 = 27;
      if ( v6 != 1 )
      {
        v10 = 0;
        v11 = 1;
        do
        {
          v10 += v6;
          v9[v11] = *(_BYTE *)(v7 + v11) ^ (v10
                                          + ((unsigned int)(((unsigned __int64)(-2139062143LL * v10) >> 32) + v10) >> 7)
                                          + ((signed int)(((unsigned __int64)(-2139062143LL * v10) >> 32) + v10) < 0));
          ++v11;
        }
        while ( v6 != v11 );
      }
    }
    v13 = v9;
  }
  return ((int (__fastcall *)(int, int (*)(), _BYTE **, int, int))j_lua_load)(v5, sub_E6AA, &v13, v8, a5);
}

我们发现逻辑其实十分清晰,只有一行关键加密,我们把文件加载进来,然后顺着整个脚本就能把 lua 解密成字节码形式了,卧槽,看见大佬直接 dump 出来了,真是orz

v9[v11] = *(_BYTE *)(v7 + v11) ^ (v10
                                          + ((unsigned int)(((unsigned __int64)(-2139062143LL * v10) >> 32) + v10) >> 7)
                                          + ((signed int)(((unsigned __int64)(-2139062143LL * v10) >> 32) + v10) < 0));

其中v11为下标,v6 = size;v9则为原字节,下面就得想了,之前 a2 把值赋给了 v7 ,同时判断 a2==27 ,我们断定这只能是原字节,然后剩下的就能写出脚本了

uint8_t *decode(uint8_t *data, int size) {
        uint8_t *real = malloc(size);
        *real = 27;
        v10 = 0;
        v11 = 1;
        do
        {
          v10 += size;
          real[v11] = date[v11] ^ (v10
                                          + ((unsigned int)(((unsigned __int64)(-2139062143LL * v10) >> 32) + v10) >> 7)
                                          + ((signed int)(((unsigned __int64)(-2139062143LL * v10) >> 32) + v10) < 0));
          ++v11;
         }
         while ( size != v11 );
         return real;

然后我们可以拿着这个函数去解密了,但是我发现我把 buf 读出来怎么少了一截??只好从网上 dang 了点东西,整出来解密后的东西后我就开始了反编译之路,找了好多软件都不好用,不是报错就是嫌这个 lua 版本太新,最后终于找到了unluac

java -jar unluac_2015_06_13.jar main.lua > main_real.lua

终于完事了

require("import")
import("android.app.*")
import("android.os.*")
import("android.widget.*")
import("android.view.*")
import("android.view.View")
import("android.content.Context")
import("android.media.MediaPlayer")
import("android.media.AudioManager")
import("com.androlua.Ticker")
activity.getSystemService(Context.AUDIO_SERVICE).setStreamVolume(AudioManager.STREAM_MUSIC, 15, AudioManager.FLAG_SHOW_UI)
activity.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE)
m = MediaPlayer()
m.reset()
m.setDataSource(activity.getLuaDir() .. "/0.mp3")
m.prepare()
m.start()
m.setLooping(true)      //单曲循环,真狠啊你是
ti = Ticker()
ti.Period = 10
function ti.onTick()    //重写点击
  activity.getSystemService(Context.AUDIO_SERVICE).setStreamVolume(AudioManager.STREAM_MUSIC, 15, AudioManager.FLAG_SHOW_UI)
  activity.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE)
end
ti.start()
function onKeyDown(A0_0, A1_1)
  if string.find(tostring(A1_1), "KEYCODE_BACK") ~= nil then    //KEYCODE_BACK返回按键
    activity.getSystemService(Context.AUDIO_SERVICE).setStreamVolume(AudioManager.STREAM_MUSIC, 15, AudioManager.FLAG_SHOW_UI)
  end
  return true
end

可以看到 lua 只是找到了 0.mp3 ,之后疯狂循环,并且瞎写控制函数,然后就没有然后了,咱也不知道 java 里有没有瞎写, app 分析实在太难了~~