首页
社区
课程
招聘
[原创]第七届山东省大学生网络安全技能大赛. 安卓逆向 MagicImageViewer
发表于: 2024-1-6 07:44 10202

[原创]第七届山东省大学生网络安全技能大赛. 安卓逆向 MagicImageViewer

2024-1-6 07:44
10202

闲来无事刷CTF,今天逛论坛看到了一个CTF《无用的CE》,没有bin文件,只有一个bugku的连接;去首页看了下不少CTF的题目,故注册了账号,每个题下载都要币,幸好初始账号有30个,研究了下,有的题目下载需要3 - 5个币不等,提交flag还能赚1 - 2个币,所以专挑差价多的搞,先下了三个试试水。前两个难度不大,感觉这个有些技巧故分享之。

看后缀是apk,丢到夜神模拟器看看。

随便写个code,点击DECRYPT按钮会Toast提示 Something wrong!

直接把APK丢到Jadx中,根据反编译情况来看没有加壳。幸好以前做过几天的APP,这代码看着很亲切的感觉。

代码解读:

通过上面的代码可以看到2个关键函数getKey readMagicImage;

双击getKey,可以看到public native String getKey(String str);,根据以前的开发经验应该是调用c/c++的so函数了。

双击readMagicImage ,跳到函数代码处,如下

代码解读:

关键函数decrypt

双击decrypt,来到public static native int decrypt(int i, char c);,来自so。

通过google,得知android会使用 System.loadLibrary 加载so文件。

MainActivity的这个函数验证了这个。

通过jadx可以清楚的看到资源lib下有多个版本的libnative-lib.so文件

直接保存下来,然后挑个自己熟悉的来搞,先从x86的下手。

直接拖入IDA中开始分析,通过导出表很快找到了想要的函数

双击函数直接来到函数代码处,因为牵扯到JavaVM JNIEnv的类,反汇编代码很难看清逻辑,直接F5吧。

简单重定义了几个数据类型后,代码看起来舒服多了。

看了好久的getKey函数,有意些代码实在是搞不明白。又因为不会调试分析so文件,所以卡住了。

回头看了下decrypt倒是很简单。

把流程多看了几遍,突然灵机一动,解密的是png,至少前4字节是固定的。这让我突然想起了,破译希特勒电报的故事,电报的开头固定的格式“希特勒万岁”。

通过分析bg.png,网上下png和其他三个png发现,不仅仅是magic是一样的甚至前16字节完全一样,出乎意料呀,刚开始还以只会是前4字节是一样的。

这样知道亦或算法,处理后的数据和原始数据,很容易推算数key[16]。

伪代码如下

计算出xorKey后,就可以解密ENCRYPT_PNG.data了。

解密后的png,得到图片中的flag

一直认为是数据类型影响了IDA对getKey的正常还原。

找了几篇IDA反汇编的so的文章,学了不少技巧,如导入structs,导入JNI.h头文件,以及Type Lib 增加android相关的库,来丰富IDA的还原能力。

又尝试着看看其他几个反编译F5还原的代码都一样吗,接着对x64的so进行了反编译和F5

出乎意料呀getKey如此清爽(设置几个数据类型,对几个函数调用强制Force call )

既然得到了算法,最后得到keygen.c

编译运行

    public native String getKey(String str);
 
    public native String stringFromJNI();
 
    public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView(R.layout.activity_main);
    TextView textView = (TextView) findViewById(R.id.sample_text);
    textView.setText(stringFromJNI());
    ((Button) findViewById(R.id.decrypt_button)).setOnClickListener(new View.OnClickListener() { // from class: re.sdnisc2018.sdnisc_apk2.MainActivity.1
        @Override // android.view.View.OnClickListener
        public void onClick(View view) {
            TextView textView2 = (TextView) MainActivity.this.findViewById(R.id.sample_text);
            String obj = ((EditText) MainActivity.this.findViewById(R.id.Key_text)).getText().toString();
            if (obj.length() != 16) {
                MainActivity.this.showMsgToast("Something wrong!");
                return;
            }
            String key = MainActivity.this.getKey(obj);
            ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.img);
            Bitmap readMagicImage = MagicImageUtils.readMagicImage(MainActivity.this, "png/encrypt_png.dat", key);
            if (readMagicImage == null) {
                MainActivity.this.showMsgToast("Something wrong!");
                return;
            }
            textView2.setText("Congratulations!");
            MainActivity.this.showMsgToast("Congratulations!");
            imageView.setImageBitmap(readMagicImage);
        }
    });
    ImageView imageView = (ImageView) findViewById(R.id.img);
    Bitmap readImage = MagicImageUtils.readImage(this, "png/bg.png");
    if (readImage != null) {
        textView.setText(" ");
        imageView.setImageBitmap(readImage);
        return;
    }
    showMsgToast("Something wrong!");
}
    public native String getKey(String str);
 
    public native String stringFromJNI();
 
    public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView(R.layout.activity_main);
    TextView textView = (TextView) findViewById(R.id.sample_text);
    textView.setText(stringFromJNI());
    ((Button) findViewById(R.id.decrypt_button)).setOnClickListener(new View.OnClickListener() { // from class: re.sdnisc2018.sdnisc_apk2.MainActivity.1
        @Override // android.view.View.OnClickListener
        public void onClick(View view) {
            TextView textView2 = (TextView) MainActivity.this.findViewById(R.id.sample_text);
            String obj = ((EditText) MainActivity.this.findViewById(R.id.Key_text)).getText().toString();
            if (obj.length() != 16) {
                MainActivity.this.showMsgToast("Something wrong!");
                return;
            }
            String key = MainActivity.this.getKey(obj);
            ImageView imageView = (ImageView) MainActivity.this.findViewById(R.id.img);
            Bitmap readMagicImage = MagicImageUtils.readMagicImage(MainActivity.this, "png/encrypt_png.dat", key);
            if (readMagicImage == null) {
                MainActivity.this.showMsgToast("Something wrong!");
                return;
            }
            textView2.setText("Congratulations!");
            MainActivity.this.showMsgToast("Congratulations!");
            imageView.setImageBitmap(readMagicImage);
        }
    });
    ImageView imageView = (ImageView) findViewById(R.id.img);
    Bitmap readImage = MagicImageUtils.readImage(this, "png/bg.png");
    if (readImage != null) {
        textView.setText(" ");
        imageView.setImageBitmap(readImage);
        return;
    }
    showMsgToast("Something wrong!");
}
public static native int decrypt(int i, char c);
 
public static Bitmap readMagicImage(Context context, String str, String str2) {
    Bitmap decodeByteArray;
    ArrayList<Byte> arrayList = new ArrayList();
    Bitmap bitmap = null;
    try {
        InputStream open = context.getAssets().open(str);
        int i = 0;
        while (true) {
            int read = open.read();
            if (read <= -1) {
                break;
            }
            arrayList.add(Byte.valueOf((byte) decrypt(read, str2.charAt(i % 16))));
            i++;
        }
        byte[] bArr = new byte[arrayList.size()];
        int i2 = 0;
        for (Byte b : arrayList) {
            bArr[i2] = b.byteValue();
            i2++;
        }
        decodeByteArray = BitmapFactory.decodeByteArray(bArr, 0, arrayList.size());
    } catch (IOException e) {
        e = e;
    }
    try {
        System.out.println(decodeByteArray);
        return decodeByteArray;
    } catch (IOException e2) {
        bitmap = decodeByteArray;
        e = e2;
        e.printStackTrace();
        return bitmap;
    }
}
public static native int decrypt(int i, char c);
 
public static Bitmap readMagicImage(Context context, String str, String str2) {
    Bitmap decodeByteArray;
    ArrayList<Byte> arrayList = new ArrayList();
    Bitmap bitmap = null;
    try {
        InputStream open = context.getAssets().open(str);
        int i = 0;
        while (true) {
            int read = open.read();
            if (read <= -1) {
                break;
            }
            arrayList.add(Byte.valueOf((byte) decrypt(read, str2.charAt(i % 16))));
            i++;
        }
        byte[] bArr = new byte[arrayList.size()];
        int i2 = 0;
        for (Byte b : arrayList) {
            bArr[i2] = b.byteValue();
            i2++;
        }
        decodeByteArray = BitmapFactory.decodeByteArray(bArr, 0, arrayList.size());
    } catch (IOException e) {
        e = e;
    }
    try {
        System.out.println(decodeByteArray);
        return decodeByteArray;
    } catch (IOException e2) {
        bitmap = decodeByteArray;
        e = e2;
        e.printStackTrace();
        return bitmap;
    }
}
static {
        System.loadLibrary("native-lib");
    }
static {
        System.loadLibrary("native-lib");
    }
Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt 000069E0   
Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey 000067E0   
Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt 000069E0   
Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey 000067E0   
int __cdecl Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey(int a1, JNIEnv *a2, int a3, int a4)
{
  char *v4; // eax
  char *v5; // edi
  JNIEnv *v6; // esi
  const jchar *v7; // eax
  unsigned int v8; // esi
  char v9; // bl
  int v10; // esi
  char *v12; // [esp-34h] [ebp-34h]
  int v13; // [esp-30h] [ebp-30h]
  _DWORD v14[2]; // [esp-28h] [ebp-28h] BYREF
  char *v15; // [esp-20h] [ebp-20h]
 
  v4 = (char *)new(0x20u);
  strcpy(v4, "Welcome_to_sdnisc_2018_By.Zero");
  v12 = v4;
  v5 = (char *)new(0x20u);
  v6 = a2;
  v15 = v5;
  v14[0] = 33;
  v14[1] = 16;
  strcpy(v5, "                ");
  v13 = (*a2)->GetStringLength(a2, (jstring)a4);
  v7 = (*v6)->GetStringChars(v6, (jstring)a4, 0);
  if ( v13 > 0 )
  {
    v8 = 0;
    v9 = 33;
    do
    {
      if ( (v9 & 1) == 0 )
        v5 = (char *)v14 + 1;
      v5[v8] = LOBYTE(v7[v8]) ^ v12[v8 + -30 * (v8 / 0x1E)];
      ++v8;
      v9 = v14[0];
      v5 = v15;
    }
    while ( v13 != v8 );
    if ( (v14[0] & 1) == 0 )
      v5 = (char *)v14 + 1;
    v6 = a2;
  }
  v10 = (int)(*v6)->NewStringUTF(v6, v5);
  if ( (v14[0] & 1) != 0 )
    operator delete(v15);
  operator delete(v12);
  return v10;
}
 
 
int __cdecl Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt(int a1, int a2, int a3, int a4, unsigned __int16 a5)
{
  return (a4 - 1) ^ a5 ^ 0x61;
}
int __cdecl Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey(int a1, JNIEnv *a2, int a3, int a4)
{
  char *v4; // eax
  char *v5; // edi
  JNIEnv *v6; // esi
  const jchar *v7; // eax
  unsigned int v8; // esi
  char v9; // bl
  int v10; // esi
  char *v12; // [esp-34h] [ebp-34h]
  int v13; // [esp-30h] [ebp-30h]
  _DWORD v14[2]; // [esp-28h] [ebp-28h] BYREF
  char *v15; // [esp-20h] [ebp-20h]
 
  v4 = (char *)new(0x20u);
  strcpy(v4, "Welcome_to_sdnisc_2018_By.Zero");
  v12 = v4;
  v5 = (char *)new(0x20u);
  v6 = a2;
  v15 = v5;
  v14[0] = 33;
  v14[1] = 16;
  strcpy(v5, "                ");
  v13 = (*a2)->GetStringLength(a2, (jstring)a4);
  v7 = (*v6)->GetStringChars(v6, (jstring)a4, 0);
  if ( v13 > 0 )
  {
    v8 = 0;
    v9 = 33;
    do
    {
      if ( (v9 & 1) == 0 )
        v5 = (char *)v14 + 1;
      v5[v8] = LOBYTE(v7[v8]) ^ v12[v8 + -30 * (v8 / 0x1E)];

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (3)
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
支持一下,谢谢分享
2024-1-6 10:18
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
楼主,Type Lib 增加android相关的库,来丰富IDA的还原能力这个能分享下相关文章或者能详细讲讲不
2024-1-6 10:19
0
雪    币: 2787
活跃值: (30801)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2024-1-6 23:08
1
游客
登录 | 注册 方可回帖
返回
//