吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 28913|回复: 91
收起左侧

[Android 原创] 2020春节解题领红包之一、二、三WP

  [复制链接]
AssassinQ 发表于 2020-2-9 16:01
本帖最后由 AssassinQ 于 2020-2-11 19:19 编辑

时隔一年又参加了解题领红包活动,这次有两道 Android 的题目,也花了四五天时间深一步地学习。关于脱壳和 so 分析的一些基本步骤都没有做详细记录,解题过程主要是静态分析。

PS:第三题在 IDA 中的分析中,是导入了 jni.h 头文件后我对各个参数分析过后的结果,具体的步骤在论坛中的“教我兄弟学 Android 逆向教程”系列里有涉及。

【春节】解题领红包之一

公众号回复直接得到口令:

1.jpg

【春节】解题领红包之二

查壳发现有 ASPack 壳,直接上 ESP 定律把壳脱掉:

2.png

然后用 OD 看一下 dump 下来的程序,先搜索字符串,可以看到输入正确后返回的字符串:

3.png

然后定位到具体的函数位置,看下来感觉有点复杂,但大概可以看到涉及到了三个字符串,可以判断出是类似 MD5 的哈希摘要:

00617D34   .  8D4D F0       LEA ECX,DWORD PTR SS:[EBP-0x10]
00617D37   .  8B15 04786200 MOV EDX,DWORD PTR DS:[0x627804]   ;  dumped_.0062B6AC
00617D3D   .  8B12          MOV EDX,DWORD PTR DS:[EDX]
00617D3F   .  8D45 FC       LEA EAX,DWORD PTR SS:[EBP-0x4]
00617D42   .  E8 597DE1FF   CALL dumped_.0042FAA0
00617D47   .  8B45 F0       MOV EAX,DWORD PTR SS:[EBP-0x10]
00617D4A   .  BA B87E6100   MOV EDX,dumped_.00617EB8          ;  E7EE5F4653E31955CACC7CD68E2A7839
00617D4F   .  E8 A42DDFFF   CALL dumped_.0040AAF8
00617D54   .  0F9445 E7     SETE BYTE PTR SS:[EBP-0x19]
00617D58   .  33C0          XOR EAX,EAX
00617D5A   .  5A            POP EDX
00617D5B   .  59            POP ECX
00617D5C   .  59            POP ECX
00617D5D   .  64:8910       MOV DWORD PTR FS:[EAX],EDX
00617D60   .  68 757D6100   PUSH dumped_.00617D75
00617D65   >  8D45 F0       LEA EAX,DWORD PTR SS:[EBP-0x10]
00617D68   .  E8 4F1FDFFF   CALL dumped_.00409CBC
00617D6D   .  C3            RETN
00617D6E   .- E9 6515DFFF   JMP dumped_.004092D8
00617D73   .^ EB F0         JMP SHORT dumped_.00617D65
00617D75   .  807D E7 00    CMP BYTE PTR SS:[EBP-0x19],0x0
00617D79   .  74 57         JE SHORT dumped_.00617DD2
00617D7B   .  33C0          XOR EAX,EAX
00617D7D   .  55            PUSH EBP
00617D7E   .  68 CB7D6100   PUSH dumped_.00617DCB
00617D83   .  64:FF30       PUSH DWORD PTR FS:[EAX]
00617D86   .  64:8920       MOV DWORD PTR FS:[EAX],ESP
00617D89   .  8D45 EC       LEA EAX,DWORD PTR SS:[EBP-0x14]
00617D8C   .  E8 2B1FDFFF   CALL dumped_.00409CBC
00617D91   .  8D4D EC       LEA ECX,DWORD PTR SS:[EBP-0x14]
00617D94   .  8B15 04786200 MOV EDX,DWORD PTR DS:[0x627804]   ;  dumped_.0062B6AC
00617D9A   .  8B12          MOV EDX,DWORD PTR DS:[EDX]
00617D9C   .  8D45 F8       LEA EAX,DWORD PTR SS:[EBP-0x8]
00617D9F   .  E8 7C7CE1FF   CALL dumped_.0042FA20
00617DA4   .  8B45 EC       MOV EAX,DWORD PTR SS:[EBP-0x14]
00617DA7   .  BA 087F6100   MOV EDX,dumped_.00617F08          ;  ea6b2efbdd4255a9f1b3bbc6399b58f4
00617DAC   .  E8 472DDFFF   CALL dumped_.0040AAF8
00617DB1   .  0F9445 E6     SETE BYTE PTR SS:[EBP-0x1A]
00617DB5   .  33C0          XOR EAX,EAX
00617DB7   .  5A            POP EDX
00617DB8   .  59            POP ECX
00617DB9   .  59            POP ECX
00617DBA   .  64:8910       MOV DWORD PTR FS:[EAX],EDX
00617DBD   .  68 D67D6100   PUSH dumped_.00617DD6
00617DC2   >  8D45 EC       LEA EAX,DWORD PTR SS:[EBP-0x14]
00617DC5   .  E8 F21EDFFF   CALL dumped_.00409CBC
00617DCA   .  C3            RETN
00617DCB   .- E9 0815DFFF   JMP dumped_.004092D8
00617DD0   .^ EB F0         JMP SHORT dumped_.00617DC2
00617DD2   >  C645 E6 00    MOV BYTE PTR SS:[EBP-0x1A],0x0
00617DD6   .  807D E6 00    CMP BYTE PTR SS:[EBP-0x1A],0x0
00617DDA   .  74 6D         JE SHORT dumped_.00617E49
00617DDC   .  33C0          XOR EAX,EAX
00617DDE   .  55            PUSH EBP
00617DDF   .  68 2C7E6100   PUSH dumped_.00617E2C
00617DE4   .  64:FF30       PUSH DWORD PTR FS:[EAX]
00617DE7   .  64:8920       MOV DWORD PTR FS:[EAX],ESP
00617DEA   .  8D45 E8       LEA EAX,DWORD PTR SS:[EBP-0x18]
00617DED   .  E8 CA1EDFFF   CALL dumped_.00409CBC
00617DF2   .  8D4D E8       LEA ECX,DWORD PTR SS:[EBP-0x18]
00617DF5   .  8B15 04786200 MOV EDX,DWORD PTR DS:[0x627804]   ;  dumped_.0062B6AC
00617DFB   .  8B12          MOV EDX,DWORD PTR DS:[EDX]
00617DFD   .  8D45 F4       LEA EAX,DWORD PTR SS:[EBP-0xC]
00617E00   .  E8 1B7CE1FF   CALL dumped_.0042FA20
00617E05   .  8B45 E8       MOV EAX,DWORD PTR SS:[EBP-0x18]
00617E08   .  BA 587F6100   MOV EDX,dumped_.00617F58          ;  c8d46d341bea4fd5bff866a65ff8aea9
00617E0D   .  E8 E62CDFFF   CALL dumped_.0040AAF8
00617E12   .  0F9445 E5     SETE BYTE PTR SS:[EBP-0x1B]
00617E16   .  33C0          XOR EAX,EAX
00617E18   .  5A            POP EDX
00617E19   .  59            POP ECX
00617E1A   .  59            POP ECX
00617E1B   .  64:8910       MOV DWORD PTR FS:[EAX],EDX
00617E1E   .  68 337E6100   PUSH dumped_.00617E33
00617E23   >  8D45 E8       LEA EAX,DWORD PTR SS:[EBP-0x18]
00617E26   .  E8 911EDFFF   CALL dumped_.00409CBC
00617E2B   .  C3            RETN
00617E2C   .- E9 A714DFFF   JMP dumped_.004092D8
00617E31   .^ EB F0         JMP SHORT dumped_.00617E23
00617E33   .  807D E5 00    CMP BYTE PTR SS:[EBP-0x1B],0x0
00617E37   .  74 10         JE SHORT dumped_.00617E49
00617E39   .  83C9 FF       OR ECX,-0x1
00617E3C   .  83CA FF       OR EDX,-0x1
00617E3F   .  B8 A87F6100   MOV EAX,dumped_.00617FA8          ;  请把答案回复到论坛公众号!
00617E44   .  E8 236BF5FF   CALL dumped_.0056E96C

抛弃 OD,把 dump 下来的程序导入 IDA,根据前面找到的 unicode 字符串定位到函数,反编译后得到如下代码:

int __fastcall TForm1_edtPwdChange(int a1)
{
  int v1; // ebx
  int v2; // edx
  int len; // eax
  int md5Handler; // esi
  char v5; // zf
  unsigned int v7; // [esp-18h] [ebp-58h]
  int *v8; // [esp-14h] [ebp-54h]
  char *v9; // [esp-10h] [ebp-50h]
  unsigned int v10; // [esp-Ch] [ebp-4Ch]
  void *v11; // [esp-8h] [ebp-48h]
  int *v12; // [esp-4h] [ebp-44h]
  int v13; // [esp+8h] [ebp-38h]
  int v14; // [esp+Ch] [ebp-34h]
  int v15; // [esp+10h] [ebp-30h]
  int v16; // [esp+14h] [ebp-2Ch]
  int v17; // [esp+18h] [ebp-28h]
  int v18; // [esp+1Ch] [ebp-24h]
  int v19; // [esp+20h] [ebp-20h]
  char v20; // [esp+25h] [ebp-1Bh]
  char v21; // [esp+26h] [ebp-1Ah]
  char v22; // [esp+27h] [ebp-19h]
  char *string3; // [esp+28h] [ebp-18h]
  char *string2; // [esp+2Ch] [ebp-14h]
  char *string1; // [esp+30h] [ebp-10h]
  char s3; // [esp+34h] [ebp-Ch]
  char s2; // [esp+38h] [ebp-8h]
  char s1; // [esp+3Ch] [ebp-4h]
  int savedregs; // [esp+40h] [ebp+0h]

  v1 = a1;
  v12 = &savedregs;
  v11 = &loc_617E9C;
  v10 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v10);
  sub_541DB8(*(Controls::TControl **)(a1 + 976), &v19);
  len = v19;
  if ( v19 )
    len = *(_DWORD *)(v19 - 4);
  if ( len == 15 )                              // input's length should be 15
  {
    LOBYTE(v2) = 1;
    md5Handler = sub_616B84(&cls_IdHashMessageDigest_TIdHashMessageDigest5, v2);// get MD5 handler
    v9 = &s1;
    sub_541DB8(*(Controls::TControl **)(v1 + 976), &v17);
    sub_50F2EC(v17, 7, &v18);
    registerFunc(md5Handler, v18, 0, (int)&s1);
    v9 = &s2;
    v8 = &v16;
    sub_541DB8(*(Controls::TControl **)(v1 + 976), &v15);
    Compprod::TComponentsPageProducer::HandleTag(&v16);
    registerFunc(md5Handler, v16, 0, (int)&s2);
    v9 = &s3;
    sub_541DB8(*(Controls::TControl **)(v1 + 976), &v13);
    unknown_libname_807(v13, 4, &v14);
    registerFunc(md5Handler, v14, 0, (int)&s3);
    v9 = (char *)&savedregs;
    v8 = (int *)&loc_617D6E;
    v7 = __readfsdword(0);
    __writefsdword(0, (unsigned int)&v7);
    freeMem(&string1);
    sub_42FAA0((int *)&s1, 0, (int *)&string1);
    compareStr(string1, (char *)L"E7EE5F4653E31955CACC7CD68E2A7839");// compare string1
    v22 = v5;
    __writefsdword(0, v7);
    v9 = (char *)&loc_617D75;
    freeMem(&string1);
    if ( v22 )
    {
      v9 = (char *)&savedregs;
      v8 = (int *)&loc_617DCB;
      v7 = __readfsdword(0);
      __writefsdword(0, (unsigned int)&v7);
      freeMem(&string2);
      sub_42FA20(&s2, 0, &string2);
      compareStr(string2, (char *)L"ea6b2efbdd4255a9f1b3bbc6399b58f4");// compare string2
      v21 = v5;
      __writefsdword(0, v7);
      v9 = (char *)&loc_617DD6;
      freeMem(&string2);
    }
    else
    {
      v21 = 0;
    }
    if ( v21 )
    {
      v9 = (char *)&savedregs;
      v8 = (int *)&loc_617E2C;
      v7 = __readfsdword(0);
      __writefsdword(0, (unsigned int)&v7);
      freeMem(&string3);
      sub_42FA20(&s3, 0, &string3);
      compareStr(string3, (char *)L"c8d46d341bea4fd5bff866a65ff8aea9");// compare string3
      v20 = v5;
      __writefsdword(0, v7);
      v9 = (char *)&loc_617E33;
      freeMem(&string3);
      if ( v20 )                                // Success
        createDialog((int)L"请把答案回复到论坛公众号!", -1, -1);
    }
  }
  __writefsdword(0, v10);
  v12 = (int *)&loc_617EA3;
  freeMem(&v13);
  freeMem(&v14);
  freeMem(&v15);
  freeMem(&v16);
  freeMem(&v17);
  freeMem(&v18);
  freeMem(&v19);
  return sub_409D1C(&string3, 6);
}

反编译后的代码也比较含糊,但可以猜到,输入的字符串长度为 15,字符串被分成了 3 部分,每部分分别进行 MD5 哈希,并与内存中的字符串进行比较,字符串正确就会弹出一个正确的对话窗口。MD5 在理论上是不可逆的,但可以在通过搜索引擎查找网上已经被爆破出的对应的明文。第一部分的解密结果:

4.png

第二部分的解密结果:

5.png

第三部分的解密结果:

6.png

将得到的字符串拼接并进行验证:

7.png

回复公众号得到口令:

8.jpg

【春节】解题领红包之三

这题给的是一个 apk,先使用 jdax 打开,查看程序入口点 MainActivity,得到如下代码:

package com.wuaipojie.crackme01;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity implements OnClickListener {
    private Button btn_click;
    private EditText editText;

    private native boolean checkFlag(String str);

    public native void onClick(View view);

    static {
        /* JADX: method processing error */
/*
Error: java.lang.NullPointerException
        at jadx.core.dex.visitors.regions.ProcessTryCatchRegions.searchTryCatchDominators(ProcessTryCatchRegions.java:75)
        at jadx.core.dex.visitors.regions.ProcessTryCatchRegions.process(ProcessTryCatchRegions.java:45)
        at jadx.core.dex.visitors.regions.RegionMakerVisitor.postProcessRegions(RegionMakerVisitor.java:63)
        at jadx.core.dex.visitors.regions.RegionMakerVisitor.visit(RegionMakerVisitor.java:58)
        at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:31)
        at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:17)
        at jadx.core.ProcessClass.process(ProcessClass.java:37)
        at jadx.api.JadxDecompiler.processClass(JadxDecompiler.java:280)
        at jadx.api.JavaClass.decompile(JavaClass.java:62)
*/
        /*
        r0 = "crack_j2c";         Catch:{ UnsatisfiedLinkError -> 0x0005 }
        java.lang.System.loadLibrary(r0);         Catch:{ UnsatisfiedLinkError -> 0x0005 }
    L_0x0005:
        return;
        */
        throw new UnsupportedOperationException("Method not decompiled: com.wuaipojie.crackme01.MainActivity.<clinit>():void");
    }

    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) R.layout.activity_main);
        this.editText = (EditText) findViewById(R.id.input_flag);
        Button button = (Button) findViewById(R.id.button);
        this.btn_click = button;
        button.setOnClickListener(this);
    }
}

主要有三个函数,onCreate() 在 Java 层中实现,可以看出整个界面中有一个文本框和一个按钮,并设置了一个按钮的监听事件,即 onClick;onClick()checkFlag() 可以看到是在 Native 层进行实现的,可以从 lib 文件夹中找到 so 文件,接下来用 IDA 对 so 中的两个函数进行分析。导入 IDA 后,通过函数名可以看出两个函数通过静态注册:

9.png

然后先来看 onClick 函数。这边略过一些导入 jni.h 等一些分析的过程(一般静态注册函数的第一个参数是 JNIEnv 等等),在分析的过程中大致猜测出两个函数 sub_5288sub_539C 两个函数分别用来获取指定的方法(getMethod)或者是域(getField)。接下来直接来看分析过后的代码:

int __fastcall Java_com_wuaipojie_crackme01_MainActivity_onClick__Landroid_view_View_2(_JNIEnv *env, int a2, int a3)
{
  _JNIEnv *env_; // r4
  int v4; // r5
  int v5; // r9
  int v6; // r0
  int v7; // r5
  jstring (__cdecl *v8)(JNIEnv *, const char *); // r2
  int v9; // r6
  int v10; // r8
  int v11; // r6
  int v12; // r8
  int len; // r5
  int v14; // r5
  const char *v15; // r1
  int v16; // r6
  int v17; // r5
  int result; // r0
  JNINativeMethod method; // [sp+4h] [bp-74h]
  int v20; // [sp+10h] [bp-68h]
  int v21; // [sp+14h] [bp-64h]
  int v22; // [sp+18h] [bp-60h]
  int v23; // [sp+1Ch] [bp-5Ch]
  int v24; // [sp+20h] [bp-58h]
  int v25; // [sp+24h] [bp-54h]
  int a3a; // [sp+28h] [bp-50h]
  int v27; // [sp+2Ch] [bp-4Ch]
  int v28; // [sp+30h] [bp-48h]
  int v29; // [sp+34h] [bp-44h]
  int v30; // [sp+38h] [bp-40h]
  int a2a; // [sp+3Ch] [bp-3Ch]
  int v32; // [sp+40h] [bp-38h]
  int v33; // [sp+48h] [bp-30h]
  int v34; // [sp+50h] [bp-28h]
  int v35; // [sp+58h] [bp-20h]

  env_ = env;
  v4 = a3;
  a2a = 0;
  v29 = 0;
  v30 = 0;
  v27 = 0;
  v28 = 0;
  v25 = 0;
  a3a = 0;
  v23 = 0;
  v24 = 0;
  v21 = 0;
  v22 = 0;
  method.fnPtr = 0;
  v20 = 0;
  v5 = ((int (__fastcall *)(_JNIEnv *, int))env->functions->NewLocalRef)(env, a2);
  v6 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->NewLocalRef)(env_, v4);
  if ( !v5 )
    goto LABEL_38;
  v7 = v6;
  method.name = "editText";
  method.signature = "Landroid/widget/EditText;";
  if ( getFields(env_, &a2a, &a3a, 0, "com/wuaipojie/crackme01/MainActivity", method) )
    goto LABEL_39;
  v9 = ((int (__fastcall *)(_JNIEnv *, int, int))env_->functions->GetObjectField)(env_, v5, a3a);
  if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
    goto LABEL_39;
  if ( v7 )
    ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v7);
  if ( !v9 )
    goto LABEL_38;
  if ( !v25 )
  {
    method.name = "getText";
    method.signature = "()Landroid/text/Editable;";
    if ( getMethods(env_, &v30, &v25, 0, "android/widget/EditText", method) )
      goto LABEL_39;
  }
  v10 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v9);// get input string
  if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
    goto LABEL_39;
  ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v9);
  if ( !v10 )
    goto LABEL_38;
  if ( !v24 )
  {
    method.name = "toString";
    method.signature = "()Ljava/lang/String;";
    if ( getMethods(env_, &v29, &v24, 0, "java/lang/Object", method) )
      goto LABEL_39;
  }
  v11 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v10);// convert object to string
  if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
    goto LABEL_39;
  ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v10);
  if ( !v11 )
    goto LABEL_38;
  if ( !v23 )
  {
    method.name = "trim";
    method.signature = "()Ljava/lang/String;";
    if ( getMethods(env_, &v28, &v23, 0, "java/lang/String", method) )
      goto LABEL_39;
  }
  v12 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v11);// trim string
  if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
    goto LABEL_39;
  ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v11);
  if ( !v12 )
    goto LABEL_38;
  if ( !v22 )
  {
    method.name = "length";
    method.signature = "()I";
    if ( getMethods(env_, &v28, &v22, 0, "java/lang/String", method) )
      goto LABEL_39;
  }
  len = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallIntMethodA)(env_, v12);// get string's length
  if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
    goto LABEL_39;
  if ( len == 30 )                              // len(flag) == 30
  {
    if ( !v21 )
    {
      method.name = "checkFlag";
      method.signature = "(Ljava/lang/String;)Z";
      if ( getMethods(env_, &a2a, &v21, 0, "com/wuaipojie/crackme01/MainActivity", method) )
        goto LABEL_39;
    }
    v32 = v12;
    v14 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallBooleanMethodA)(env_, v5);// invoke checkFlag method
    if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
      goto LABEL_39;
    ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v12);
    v8 = env_->functions->NewStringUTF;
    if ( !v14 )
      goto LABEL_40;
    v15 = "正确!!!回复你输入的内容到吾爱破解论坛公众号";            // correct
  }
  else
  {
    ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v12);
    v15 = "flag长度必须为30位";                       // flag's length must equal to 30
    v8 = env_->functions->NewStringUTF;
  }
  while ( 1 )
  {
    v16 = ((int (__fastcall *)(_JNIEnv *, const char *))v8)(env_, v15);
    if ( v20
      || (method.name = "makeText",
          method.signature = "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;",
          !getMethods(env_, &v27, &v20, 1, "android/widget/Toast", method)) )
    {
      v33 = v16;
      v32 = v5;
      v34 = 0;
      v17 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallStaticObjectMethodA)(env_, v27);
      if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
      {
        if ( v16 )
          ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v16);
        if ( v17 )
        {
          if ( method.fnPtr
            || (method.name = "show",
                method.signature = "()V",
                !getMethods(env_, &v27, (int *)&method.fnPtr, 0, "android/widget/Toast", method)) )
          {
            ((void (__fastcall *)(_JNIEnv *, int))env_->functions->CallVoidMethodA)(env_, v17);
            ((void (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_);
          }
        }
        else
        {
LABEL_38:
          sub_4EC0(env_, "java/lang/NullPointerException", "NullPointerException");
        }
      }
    }
LABEL_39:
    result = _stack_chk_guard - v35;
    if ( _stack_chk_guard == v35 )
      break;
LABEL_40:
    v15 = "验证错误,继续加油";                          // wrong
  }
  return result;
}

onClick 函数中的内容主要为在点击按钮后获取输入内容,并判断输入的字符串长度是否为 30,然后调用 checkFlag 函数对字符串进行判断。接下来再看看 checkFlag 函数,这个函数比较长,分成几段来看。首先调用了 isDebuggerConnected() 函数,猜测应该是用来反调试:

  method1.name = "isDebuggerConnected";
  method1.signature = "()Z";
  if ( !getMethods(env_, &jclass, &jmethodid, 1, "android/os/Debug", method1) )// anti-debug??
  {
    t1 = (unsigned int)&t_;
    v8 = ((int (__fastcall *)(_JNIEnv *, int, int, int *))env_->functions->CallStaticBooleanMethodA)(
           env_,
           jclass,
           jmethodid,
           &t_);
    if ( !(((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) | v8) )
    {
      key1 = ((int (__fastcall *)(_JNIEnv *, signed int))env_->functions->NewByteArray)(env_, 9);
      if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
        goto LABEL_7;
    }
    goto LABEL_4;
  }

接下来将一串字符串分成三部分(key1 = "thisiskey",key2 = "52pojie_2020_happy_chinese_new_year",key3 = "20200125")并分给了三个变量:

    v6 = 0;
    ((void (__fastcall *)(_JNIEnv *, int, _DWORD, signed int, const char *))env_->functions->SetByteArrayRegion)(
      env_,
      key1,
      0,
      9,
      "thisiskey52pojie_2020_happy_chinese_new_year20200125");// key1 = "thisiskey"
    key2 = ((int (__fastcall *)(_JNIEnv *, signed int))env_->functions->NewByteArray)(env_, 35);
    if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
    {
      v6 = 0;
      ((void (__fastcall *)(_JNIEnv *, int, _DWORD, signed int, char *))env_->functions->SetByteArrayRegion)(
        env_,
        key2,
        0,
        35,
        "52pojie_2020_happy_chinese_new_year20200125");// key2 = "52pojie_2020_happy_chinese_new_year"
      key3 = ((int (__fastcall *)(_JNIEnv *, signed int))env_->functions->NewByteArray)(env_, 8);
      if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
      {
        v6 = 0;
        ((void (__fastcall *)(_JNIEnv *, int, _DWORD, signed int, char *))env_->functions->SetByteArrayRegion)(
          env_,
          key3,
          0,
          8,
          "20200125");                          // key3 = "20200125"

然后新建了一个 35 位的 Byte 数组,做一个循环,当 i 不为 0 且 i 是 4 的倍数时,下标设置为 (i >> 2) - 1,取 key3 中的值来 append 到数组中;反之,下标设置为 i,取 key2 中的值来 append 到数组中:

        arr = ((int (__fastcall *)(_JNIEnv *, signed int))env_->functions->NewByteArray)(env_, 35);
        if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
        {
          i = 0;
          arr_ = arr;
          key1_ = key1;
          do
          {
            if ( !i || i & 3 )
            {
              if ( !key2 )
                goto LABEL_41;
              pointer = key2;
              i_ = i;
              GetByteArrayRegion_ = env_->functions->GetByteArrayRegion;
            }
            else                                // if i != 0 and i % 4 == 0
            {
              pointer = key3;
              if ( !key3 )
                goto LABEL_41;
              GetByteArrayRegion_ = env_->functions->GetByteArrayRegion;
              i_ = (i >> 2) - 1;                // 0,1,2,3,4,5,6,7
            }
            ((void (__fastcall *)(_JNIEnv *, int, int, signed int, unsigned int))GetByteArrayRegion_)(
              env_,
              pointer,
              i_,
              1,
              t1);
            key1 = (unsigned __int8)t_;
            if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
              goto LABEL_4;
            if ( !arr_ )
            {
LABEL_41:
              sub_4EC0(env_, "java/lang/NullPointerException", "NullPointerException");
              goto LABEL_4;
            }
            LOBYTE(t_) = key1;
            ((void (__fastcall *)(_JNIEnv *, int, unsigned int, signed int, unsigned int))env_->functions->SetByteArrayRegion)(
              env_,
              arr_,
              i,
              1,
              t1);
            if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
              goto LABEL_4;
          }
          while ( i++ < 0x22 );                 // for i in range(35)

接下来对 byte 数组进行 MD5 哈希,然后取摘要生成 16 位的 byte 数组:

          md5Str = ((int (__fastcall *)(_JNIEnv *, const char *))env_->functions->NewStringUTF)(env_, "MD5");
          if ( !v47 )
          {
            method2.name = "getInstance";
            method2.signature = "(Ljava/lang/String;)Ljava/security/MessageDigest;";
            if ( getMethods(env_, &v52, &v47, 1, "java/security/MessageDigest", method2) )
              goto LABEL_88;
          }
          t_ = md5Str;
          v18 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallStaticObjectMethodA)(env_, v52);// md5 function
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;
          if ( md5Str )
            ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, md5Str);
          if ( !v18 )
          {
LABEL_87:
            sub_4EC0(env_, "java/lang/NullPointerException", "NullPointerException");
            goto LABEL_88;
          }
          if ( !v46 )
          {
            method2.name = "digest";
            method2.signature = "([B)[B";
            if ( getMethods(env_, &v52, &v46, 0, "java/security/MessageDigest", method2) )
              goto LABEL_88;
          }
          t_ = arr_;
          md5Digest = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v18);// get hash digest
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;
          ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v18);
          if ( !md5Digest )
            goto LABEL_87;

然后做一个循环,对数组中的元素和 key1 进行逐位异或:

          len = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->GetArrayLength)(env_, md5Digest);
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;
          idx = 0;
          while ( 1 )                           // for i in range(16)
          {
            t1 = 0x38E38E39 * (unsigned __int64)(unsigned int)idx >> 32;// div 9? useless
            if ( idx >= len )
              break;
            ((void (__fastcall *)(_JNIEnv *, int, int, signed int, int *))env_->functions->GetByteArrayRegion)(
              env_,
              md5Digest,
              idx,
              1,
              &t_);
            key1 = (unsigned __int8)t_;
            if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
              goto LABEL_88;
            if ( !key1_ )
              goto LABEL_87;
            ((void (__fastcall *)(_JNIEnv *, int, unsigned int, signed int, int *))env_->functions->GetByteArrayRegion)(
              env_,
              key1_,
              idx % 9u,                         // mod 9
              1,
              &t_);
            ch = t_;
            if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            {
              LOBYTE(t_) = ch ^ key1;           // xor
              ((void (__fastcall *)(_JNIEnv *, int, int, signed int, int *))env_->functions->SetByteArrayRegion)(
                env_,
                md5Digest,
                idx,
                1,
                &t_);
              if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
              {
                len = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->GetArrayLength)(env_, md5Digest);
                ++idx;
                if ( !((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
                  continue;
              }
            }
            goto LABEL_88;
          }

接下来,将得到的 byte 数组逐位转成 hex 字符串,如果小于 0xF,即只有一位,高位补 0。将结果逐位 append 到一个新的字符串中,得到一个 32 位的字符串:

          len__ = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->GetArrayLength)(env_, md5Digest);
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;
          if ( len__ >= 1 )
          {
            j = 0;
            classInteger = "java/lang/Integer";
            toHexString_ = "toHexString";
            method2.fnPtr = "(I)Ljava/lang/String;";
            zeroPad = 0;
            while ( 1 )
            {
              ((void (__fastcall *)(_JNIEnv *, int, int, signed int, int *))env_->functions->GetByteArrayRegion)(
                env_,
                md5Digest,
                j,
                1,
                &t_);
              t1 = (unsigned __int8)t_;
              if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
                break;
              if ( t1 <= 0xF )                  // if x < 0xF then append a zero
              {
                if ( zeroPad )
                  ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, zeroPad);
                zeroPad = ((int (__fastcall *)(_JNIEnv *, const char *))env_->functions->NewStringUTF)(env_, "0");
                if ( !v44 )
                {
                  method2.name = "append";
                  method2.signature = "(Ljava/lang/String;)Ljava/lang/StringBuilder;";
                  if ( getMethods(env_, &v51, &v44, 0, "java/lang/StringBuilder", method2) )
                    break;
                }
                t_ = zeroPad;
                v24 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v22);// append zero
                if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
                  break;
                if ( v24 )
                  ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v24);
              }
              if ( !v43 )
              {
                *(_QWORD *)&method2.name = __PAIR__((unsigned int)method2.fnPtr, (unsigned int)toHexString_);
                if ( getMethods(env_, &v50, &v43, 1, classInteger, method2) )// get toHexString method
                  break;
              }
              t_ = t1;
              t1 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallStaticObjectMethodA)(env_, v50);
              if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
                break;
              if ( arr_ )
                ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, arr_);
              if ( !v44 )
              {
                method2.name = "append";
                method2.signature = "(Ljava/lang/String;)Ljava/lang/StringBuilder;";
                if ( getMethods(env_, &v51, &v44, 0, "java/lang/StringBuilder", method2) )
                  break;
              }
              t_ = t1;
              key1 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v22);
              if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
                break;
              if ( key1 )
                ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, key1);
              ++j;
              arr_ = t1;
              if ( j >= len__ )
                goto LABEL_75;
            }

最后将取字符串字符串的 1~31 位作为新的字符串,并与我们的输入进行比较:

if ( !v42 )
          {
            method2.name = "toString";
            method2.signature = "()Ljava/lang/String;";
            if ( getMethods(env_, &v51, &v42, 0, "java/lang/StringBuilder", method2) )
              goto LABEL_88;
          }
          v25 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v22);// convert StringBuilder to string
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;
          ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v22);
          if ( !v25 )
            goto LABEL_87;
          if ( !v41 )
          {
            method2.name = "substring";
            method2.signature = "(II)Ljava/lang/String;";
            if ( getMethods(env_, &v49, &v41, 0, "java/lang/String", method2) )
              goto LABEL_88;
          }
          v55 = 31;                             // slice 1-31
          t_ = 1;
          v26 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallObjectMethodA)(env_, v25);
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;
          ((void (__fastcall *)(_JNIEnv *, int))env_->functions->DeleteLocalRef)(env_, v25);
          if ( !v26 )
            goto LABEL_87;
          if ( !v40 )
          {
            method2.name = "equals";
            method2.signature = "(Ljava/lang/Object;)Z";
            if ( getMethods(env_, &v49, &v40, 0, "java/lang/String", method2) )
              goto LABEL_88;
          }
          t_ = v39;
          v6 = ((int (__fastcall *)(_JNIEnv *, int))env_->functions->CallBooleanMethodA)(env_, v26);// compare string
          if ( ((int (__fastcall *)(_JNIEnv *))env_->functions->ExceptionCheck)(env_) )
            goto LABEL_88;

只需要正向地实现就能得到对应的字符串,我这里用 Python 实现了一下:

#!/usr/bin/env python
import hashlib

key = 'thisiskey52pojie_2020_happy_chinese_new_year20200125'
key1 = key[:0x7D-0x74]
key2 = key[0x7D-0x74:0xA0-0x74]
key3 = key[0xA0-0x74:]

arr = ''
for i in range(35):
    if not i or i & 3:
        arr += key2[i]
    else:
        arr += key3[(i >> 2) - 1]
print arr

md5str = hashlib.md5(arr).digest()
print md5str.encode('hex')

xorlist = []
for i in range(16):
    xorlist.append(ord(key1[i % 9]) ^ ord(md5str[i]))
print xorlist

flag = ''
for i in range(16):
    flag += hex(xorlist[i])[2:].zfill(2)
print flag
flag = flag[1:31]
print flag
assert len(flag) == 30

跑出来后在手机上验证一下结果的正确性:

10.png

回复公众号得到口令:

11.jpg

免费评分

参与人数 28吾爱币 +31 热心值 +27 收起 理由
mzscc + 1 我很赞同!
AKB0048110 + 1 我很赞同!
不谙世事的骚年 + 1 + 1 热心回复!
ttao88 + 1 谢谢@Thanks!
explorer126 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
此用户无法显示 + 1 + 1 用心讨论,共获提升!
CHILAS_LEE + 1 我很赞同!
cpj1203 + 1 + 1 谢谢@Thanks!
yixi + 1 + 1 谢谢@Thanks!
linghun + 1 + 1 谢谢@Thanks!
zl天涯行客 + 1 我很赞同!
yaoyao7 + 1 + 1 我很赞同!
Pations + 1 + 1 用心讨论,共获提升!
乌龙小八戒 + 1 谢谢@Thanks!
Spwpun + 1 + 1 用心讨论,共获提升!
Liufei2019 + 1 + 1 用心讨论,共获提升!
zsygood + 1 + 1 我很赞同!
Wmbzyutian + 1 + 1 谢谢@Thanks!
pinangeryi + 1 谢谢@Thanks!
sunnylds7 + 1 + 1 热心回复!
Reena + 1 + 1 不明觉厉
CrazyNut + 3 + 1 膜拜大佬
JuncoJet + 3 + 1 说实话 我的IDA并不能显示 env_-&amp;gt;functions-&amp;gt;GetArrayLength,求详解
苏紫方璇 + 1 + 1 膜拜
FleTime + 1 + 1 大佬,赞一个!(PS:第二题原来是ASP壳。。。)
windy_ll + 2 + 1 谢谢@Thanks!
GenW + 3 + 1 用心讨论,共获提升!
你与明日 + 3 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2020-2-9 16:26
本题思路看点在第三题,所以还是移动到移动安全区来。
JuncoJet 发表于 2020-2-9 17:21
然而我的IDA是这样的,分析全靠猜
Image 143.jpg
Image 144.jpg
Image 146.jpg

求问下楼主什么IDA什么插件
kai-memory 发表于 2020-2-9 16:37
340621 发表于 2020-2-9 16:55
小白一个,不是很懂。勉强可以举一反三。
JammyLin 发表于 2020-2-9 16:59
emmmm,第三题看见了公众号提示hook,然后去学了皮毛的frida,各种尝试,最后查到可以hook equal函数,然后直接自己输出结果了。
头像被屏蔽
Zeno___Lee 发表于 2020-2-9 17:22
真心不会-.- 坐等大佬
MaHaMa 发表于 2020-2-9 17:24
先666666为敬
 楼主| AssassinQ 发表于 2020-2-9 17:29
JuncoJet 发表于 2020-2-9 17:21
然而我的IDA是这样的,分析全靠猜

学一下论坛里教程“叫我兄弟学逆向教程”,里面有提到IDA分析so的时候可以导入jni.h头文件来分析,而且一般so里静态注册的函数的参数是特征的,第一个参数一般都是JNIEnv。

免费评分

参与人数 2吾爱币 +3 热心值 +1 收起 理由
下一站左转 + 1 谢谢@Thanks!
JuncoJet + 2 + 1 热心回复!

查看全部评分

暗夜星光i 发表于 2020-2-9 17:34
前来膜拜大佬
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-26 02:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表