爱飞的猫 发表于 2020-2-12 04:16

2020 鼠年春节红包笔记【2 和 3 题】

本帖最后由 jixun66 于 2020-2-12 04:16 编辑

Android 高级题做不来,因为不会动态调试也不会脱壳 hh


### 第 2 题 (Win32)

看上去像是 aspack/upx 简易版本?在 XP 下直接跳到 OEP 下 dump… 虽然不跨平台但在 XP 下能正常执行。

将 dump 出来的文件拉到 IDR 做分析,然后导出 map 文件导入到 OD 里。

同时在 IDR 软件中寻找密码框更新后的函数地址:

```asm
00617C3C > .55            push ebp                                                          ;_Unit71.TForm1.edtPwdChange_00617C3C
................
00617C5D   .8D55 E0       lea edx,dword ptr ss:
00617C60   .8B83 D0030000 mov eax,dword ptr ds:
00617C66   .E8 4DA1F2FF   call <2_1.Vcl.Controls.TControl.GetText_00541DB8>
00617C6B   .8B45 E0       mov eax,dword ptr ss:                                 ;eax = 键入的密码
00617C6E   .85C0          test eax,eax
00617C70   .74 05         je short 2_1.00617C77
00617C72   .83E8 04       sub eax,0x4
00617C75   .8B00          mov eax,dword ptr ds:
00617C77   >83F8 0F       cmp eax,0xF                                                       ;有 15 位字符
00617C7A   .0F85 C9010000 jnz 2_1.00617E49
00617C80   .B2 01         mov dl,0x1
00617C82   .A1 586A6100   mov eax,dword ptr ds:
00617C87   .E8 F8EEFFFF   call <2_1._Unit71.TIdHashMessageDigest4.Create_00616B84>
00617C8C   .8BF0          mov esi,eax                                                       ;esi = pMd5
00617C8E   .8D45 FC       lea eax,dword ptr ss:                                    ;<---- 储存位置
00617C91   .50            push eax
00617C92   .8D55 D8       lea edx,dword ptr ss:
00617C95   .8B83 D0030000 mov eax,dword ptr ds:
00617C9B   .E8 18A1F2FF   call <2_1.Vcl.Controls.TControl.GetText_00541DB8>
00617CA0   .8B45 D8       mov eax,dword ptr ss:                                 ;eax = 键入的密码
00617CA3   .8D4D DC       lea ecx,dword ptr ss:
00617CA6   .BA 07000000   mov edx,0x7
00617CAB   .E8 3C76EFFF   call <2_1.GetTextLeft>                                          ;取出文本左边 edx 个字符
00617CB0   .8B55 DC       mov edx,dword ptr ss:                                 ;edx = 前 7 个字符
00617CB3   .33C9          xor ecx,ecx                                                       ;2_1.006221A0
00617CB5   .8BC6          mov eax,esi
00617CB7   .E8 E4E5FFFF   call <2_1._Unit71.TIdHash.HashStringAsHex_006162A0>               ;ebp-4 = MD5(left(serial, 7))
00617CBC   .8D45 F8       lea eax,dword ptr ss:                                    ;<---- 储存位置
................
00617CDF   .E8 5076EFFF   call <2_1.System.Generics.Defaults.sub_0050F334_0050F334>         ;后面再取出 4 个字符
00617CE4   .8B55 D4       mov edx,dword ptr ss:
00617CE7   .33C9          xor ecx,ecx                                                       ;2_1.006221A0
00617CE9   .8BC6          mov eax,esi
00617CEB   .E8 B0E5FFFF   call <2_1._Unit71.TIdHash.HashStringAsHex_006162A0>               ;<-- md5
................
00617D12   .8B55 CC       mov edx,dword ptr ss:                                 ;再取出最后 4 个字符
00617D15   .33C9          xor ecx,ecx                                                       ;2_1.006221A0
00617D17   .8BC6          mov eax,esi
00617D19   .E8 82E5FFFF   call <2_1._Unit71.TIdHash.HashStringAsHex_006162A0>               ;<-- md5
................
00617D3D   .8B12          mov edx,dword ptr ds:                                        ;edx = 0x804
00617D3F   .8D45 FC       lea eax,dword ptr ss:                                    ;eax = MD5(前七位)
00617D42   .E8 597DE1FF   call <2_1._Unit9.sub_0042FAA0_0042FAA0>
00617D47   .8B45 F0       mov eax,dword ptr ss:
00617D4A   .BA B87E6100   mov edx,2_1.00617EB8                                              ;E7EE5F4653E31955CACC7CD68E2A7839
00617D4F   .E8 A42DDFFF   call <2_1.System.@UStrEqual_0040AAF8>                           ;第一串
00617D54   .0f9445 e7   sete byte ptr ss:
................
00617DA7   .BA 087F6100   mov edx,2_1.00617F08                                              ;ea6b2efbdd4255a9f1b3bbc6399b58f4
00617DAC   .E8 472DDFFF   call <2_1.System.@UStrEqual_0040AAF8>                           ;第二串
00617DB1   .0f9445 e6   sete byte ptr ss:
................
00617E08   .BA 587F6100   mov edx,2_1.00617F58                                              ;c8d46d341bea4fd5bff866a65ff8aea9
00617E0D   .E8 E62CDFFF   call <2_1.System.@UStrEqual_0040AAF8>                           ;第三串
00617E12   .0f9445 e5   sete byte ptr ss:
................
00617E3F   .B8 A87F6100   mov eax,2_1.00617FA8                                              ;请把答案回复到论坛公众号!
00617E44   .E8 236BF5FF   call <2_1._Unit54.sub_0056E96C_0056E96C>
```

当我找到第一串的时候,就在这个地址上搜寻字符串引用了,结果找到这三串:

| 地址 | 反汇编 | 文本字符串 |
| --: | ----- | :-------- |
| 00617D4A | `mov edx,2_1.00617EB8` | `E7EE5F4653E31955CACC7CD68E2A7839` |
| 00617DA7 | `mov edx,2_1.00617F08` | `ea6b2efbdd4255a9f1b3bbc6399b58f4` |
| 00617E08 | `mov edx,2_1.00617F58` | `c8d46d341bea4fd5bff866a65ff8aea9` |
| 00617E3F | `mov eax,2_1.00617FA8` | 请把答案回复到论坛公众号! |

拿去 MD5 数据在线查询,分别得到:

* `52pojie`
* `2019`
* `game`

三段字样。将其合并起来就是密码 `52pojie2019game`。

### 第 3 题 (安卓题)

因为不太懂怎么搞调试,所以把 so 文件解压出来,然后扔到 IDA 里去静态分析。

```c
int __fastcall Java_com_wuaipojie_crackme01_MainActivity_checkFlag__Ljava_lang_String_2(JNIEnv *a1, int a2, int a3)
{
jbyte *pParams; // r8
int tmpVal; // r10
JNIEnv *env; // r4
int v6; // r5
int v8; // r5
jbyteArray str__52pojie_2020_happy_chinese_new_year; // r11
jbyte *bytes_3_s35; // r6 MAPDST
unsigned int i; // r5
void *v13; // r1
void (__cdecl *fnGetByteArrayRegion)(JNIEnv *, jbyteArray, jsize, jsize, jbyte *); // r6
jsize v15; // r2
jstring str__MD5; // r0 MAPDST
_jmethodID *MD__getInstance; // r2 MAPDST
jobject MD5; // r6
_jmethodID *MD5__digest; // r2 MAPDST
jsize size_of_array; // r5
jsize start; // r6
jboolean char_of_i_mod_9; // r5
void *ClazzSB; // r6
int v26; // r5
int v27; // r5
jobject v29; // r5
_jmethodID *String__substring; // r2
jobject v31; // r6
int v33; // r5
int v34; //
jint v35; //
void *str__this_is_key; //
int v37; //
jbyteArray str__20200125; //
jobject md5_value; //
jint trimmedPassword; //
int String__equals; // MAPDST
int v43; //
int SB__toString; // MAPDST
int v45; //
int v46; //
int sb__init; //
int a3a; //
int StringClazz; //
int pFn; //
int sbInst; //
int MD; //
int a2a; //
jvalue jvalue; //
int v57; //

env = a1;
v6 = 0;
MD = 0;
a2a = 0;
pFn = 0;
sbInst = 0;
a3a = 0;
StringClazz = 0;
MD5__digest = 0;
MD__getInstance = 0;
v46 = 0;
sb__init = 0;
SB__toString = 0;
v45 = 0;
String__equals = 0;
v43 = 0;
((void (__fastcall *)(JNIEnv *, int))(*a1)->NewLocalRef)(a1, a2);
trimmedPassword = ((int (__fastcall *)(JNIEnv *, int))(*env)->NewLocalRef)(env, a3);
if ( !GetJavaFunction(env, &a2a, &a3a, 1, "android/os/Debug", "isDebuggerConnected") )
{
    pParams = (jbyte *)jvalue;
    v8 = (*env)->CallStaticBooleanMethodA(env, (jclass)a2a, (jmethodID)a3a, jvalue);
    if ( !((*env)->ExceptionCheck(env) | v8) )
    {
      tmpVal = (int)(*env)->NewByteArray(env, 9);
      if ( !(*env)->ExceptionCheck(env) )
      goto LABEL_7;
    }
    goto LABEL_4;
}
LABEL_5:
while ( _stack_chk_guard != v57 )
{
LABEL_7:
    v6 = 0;
    (*env)->SetByteArrayRegion(env, (jbyteArray)tmpVal, 0, 9, "thisiskey52pojie_2020_happy_chinese_new_year20200125");
    str__52pojie_2020_happy_chinese_new_year = (*env)->NewByteArray(env, 35);
    if ( !(*env)->ExceptionCheck(env) )
    {
      v6 = 0;
      (*env)->SetByteArrayRegion(
      env,
      str__52pojie_2020_happy_chinese_new_year,
      0,
      35,
      "52pojie_2020_happy_chinese_new_year20200125");
      str__20200125 = (*env)->NewByteArray(env, 8);
      if ( !(*env)->ExceptionCheck(env) )
      {
      v6 = 0;
      (*env)->SetByteArrayRegion(env, str__20200125, 0, 8, "20200125");
      bytes_3_s35 = (jbyte *)(*env)->NewByteArray(env, 35);
      if ( !(*env)->ExceptionCheck(env) )
      {
          i = 0;
          str__this_is_key = (void *)tmpVal;
          do
          {
            if ( !i || i & 3 )
            {
            if ( !str__52pojie_2020_happy_chinese_new_year )
                goto LABEL_41;
            v13 = str__52pojie_2020_happy_chinese_new_year;
            v15 = i;
            fnGetByteArrayRegion = (*env)->GetByteArrayRegion;
            }
            else
            {
            v13 = str__20200125;
            if ( !str__20200125 )
                goto LABEL_41;
            fnGetByteArrayRegion = (*env)->GetByteArrayRegion;
            v15 = (i >> 2) - 1;
            }
            fnGetByteArrayRegion(env, v13, v15, 1, pParams);
            tmpVal = jvalue.z;
            if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_4;
            if ( !bytes_3_s35 )
            {
LABEL_41:
            sub_4EC0(env, "java/lang/NullPointerException", "NullPointerException");
            goto LABEL_4;
            }
            jvalue.z = tmpVal;
            (*env)->SetByteArrayRegion(env, bytes_3_s35, i, 1, pParams);
            if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_4;
          }
          while ( i++ < 34 );                   // for (i = 0; i <= 34; i++)
          str__MD5 = (*env)->NewStringUTF(env, "MD5");
          if ( !MD__getInstance
            && GetJavaFunction(env, &MD, (int *)&MD__getInstance, 1, "java/security/MessageDigest", "getInstance") )
          {
            goto LABEL_89;
          }
          jvalue.i = (jint)str__MD5;
          MD5 = (*env)->CallStaticObjectMethodA(env, (jclass)MD, MD__getInstance, jvalue);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          if ( str__MD5 )
            ((void (__fastcall *)(JNIEnv *, jstring))(*env)->DeleteLocalRef)(env, str__MD5);
          if ( !MD5 )
          {
ThrowException:
            sub_4EC0(env, "java/lang/NullPointerException", "NullPointerException");
            goto LABEL_89;
          }
          if ( !MD5__digest
            && GetJavaFunction(env, &MD, (int *)&MD5__digest, 0, "java/security/MessageDigest", "digest") )
          {
            goto LABEL_89;
          }
          jvalue.i = (jint)bytes_3_s35;
          md5_value = (*env)->CallObjectMethodA(env, MD5, MD5__digest, jvalue);
          if ( (*env)->ExceptionCheck(env) )
            goto LABEL_89;
          (*env)->DeleteLocalRef(env, MD5);
          if ( !md5_value )
            goto ThrowException;
          size_of_array = (*env)->GetArrayLength(env, md5_value);
          if ( (*env)->ExceptionCheck(env) )
            goto LABEL_89;
          start = 0;
          // ???
          while ( 1 )
          {
            pParams = (jbyte *)(0x38E38E39 * (unsigned __int64)(unsigned int)start >> 32);
            if ( start >= size_of_array )
            break;
            //(* env) -> GetByteArrayRegion (env, array, 0, len, (jbyte *)buf);
            (*env)->GetByteArrayRegion(env, md5_value, start, 1, (jbyte *)jvalue);
            tmpVal = jvalue.z;
            if ( (*env)->ExceptionCheck(env) )
            goto LABEL_89;
            if ( !str__this_is_key )
            goto ThrowException;
            // xor:
            //   MD5("???"),
            //   bytes_0_s9
            (*env)->GetByteArrayRegion(env, str__this_is_key, start % 9u, 1, (jbyte *)jvalue);
            char_of_i_mod_9 = jvalue.z;
            if ( !(*env)->ExceptionCheck(env) )
            {
            jvalue.z = char_of_i_mod_9 ^ tmpVal;
            (*env)->SetByteArrayRegion(env, md5_value, start, 1, (jbyte *)jvalue);
            if ( !(*env)->ExceptionCheck(env) )
            {
                size_of_array = (*env)->GetArrayLength(env, md5_value);
                ++start;
                if ( !(*env)->ExceptionCheck(env) )
                  continue;
            }
            }
            goto LABEL_89;
          }
          if ( str__this_is_key )
            ((void (__fastcall *)(JNIEnv *, void *))(*env)->DeleteLocalRef)(env, str__this_is_key);
          if ( !sbInst && CreateClassInstance(env, &sbInst, (int)"java/lang/StringBuilder") )
            goto LABEL_89;
          ClazzSB = (void *)((int (__fastcall *)(JNIEnv *))(*env)->AllocObject)(env);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          v26 = ((int (__fastcall *)(JNIEnv *, jobject))(*env)->GetArrayLength)(env, md5_value);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          if ( !ClazzSB )
            goto ThrowException;
          if ( !sb__init && GetJavaFunction(env, &sbInst, &sb__init, 0, "java/lang/StringBuilder", "<init>") )
            goto LABEL_89;
          jvalue.i = 2 * v26;
          ((void (__fastcall *)(JNIEnv *, void *))(*env)->CallVoidMethodA)(env, ClazzSB);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          v34 = ((int (__fastcall *)(JNIEnv *, jobject))(*env)->GetArrayLength)(env, md5_value);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          if ( v34 >= 1 )
          {
            v37 = 0;
            v35 = 0;
            while ( 1 )
            {
            ((void (__fastcall *)(JNIEnv *, jobject, int, signed int))(*env)->GetByteArrayRegion)(
                env,
                md5_value,
                v37,
                1);
            pParams = (jbyte *)jvalue.z;
            if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
                break;
            if ( (unsigned int)pParams <= 0xF )
            {
                if ( v35 )
                  ((void (__fastcall *)(JNIEnv *, jint))(*env)->DeleteLocalRef)(env, v35);
                v35 = ((int (__fastcall *)(JNIEnv *, const char *))(*env)->NewStringUTF)(env, "0");
                if ( !v46 && GetJavaFunction(env, &sbInst, &v46, 0, "java/lang/StringBuilder", "append") )
                  break;
                jvalue.i = v35;
                v27 = ((int (__fastcall *)(JNIEnv *, void *))(*env)->CallObjectMethodA)(env, ClazzSB);
                if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
                  break;
                if ( v27 )
                  ((void (__fastcall *)(JNIEnv *, int))(*env)->DeleteLocalRef)(env, v27);
            }
            if ( !v45 && GetJavaFunction(env, &pFn, &v45, 1, "java/lang/Integer", "toHexString") )
                break;
            jvalue.i = (jint)pParams;
            pParams = (jbyte *)((int (__fastcall *)(JNIEnv *, int))(*env)->CallStaticObjectMethodA)(env, pFn);
            if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
                break;
            if ( bytes_3_s35 )
                ((void (__fastcall *)(JNIEnv *, jbyte *))(*env)->DeleteLocalRef)(env, bytes_3_s35);
            if ( !v46 && GetJavaFunction(env, &sbInst, &v46, 0, "java/lang/StringBuilder", "append") )
                break;
            jvalue.i = (jint)pParams;
            tmpVal = ((int (__fastcall *)(JNIEnv *, void *))(*env)->CallObjectMethodA)(env, ClazzSB);
            if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
                break;
            if ( tmpVal )
                ((void (__fastcall *)(JNIEnv *, int))(*env)->DeleteLocalRef)(env, tmpVal);
            ++v37;
            bytes_3_s35 = pParams;
            if ( v37 >= v34 )
                goto LABEL_75;
            }
LABEL_89:
            v33 = ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionOccurred)(env);
            ((void (__fastcall *)(JNIEnv *))(*env)->ExceptionClear)(env);
            if ( !sub_50EC(env, v33, "java/lang/Exception") )
            {
            ((void (__fastcall *)(JNIEnv *, int))(*env)->Throw)(env, v33);
            ((void (__fastcall *)(JNIEnv *, int))(*env)->DeleteLocalRef)(env, v33);
            }
LABEL_4:
            v6 = 0;
            goto LABEL_5;
          }
LABEL_75:
          if ( !SB__toString && GetJavaFunction(env, &sbInst, &SB__toString, 0, "java/lang/StringBuilder", "toString") )
            goto LABEL_89;
          v29 = (*env)->CallObjectMethodA(env, ClazzSB, (jmethodID)SB__toString, jvalue);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          ((void (__fastcall *)(JNIEnv *, void *))(*env)->DeleteLocalRef)(env, ClazzSB);
          if ( !v29 )
            goto ThrowException;
          String__substring = (_jmethodID *)v43;
          if ( !v43 )
          {
            if ( GetJavaFunction(env, &StringClazz, &v43, 0, "java/lang/String", "substring") )
            goto LABEL_89;
            String__substring = (_jmethodID *)v43;
          }
          jvalue.i = 31;
          jvalue.i = 1;
          v31 = (*env)->CallObjectMethodA(env, v29, String__substring, jvalue);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
          ((void (__fastcall *)(JNIEnv *, jobject))(*env)->DeleteLocalRef)(env, v29);
          if ( !v31 )
            goto ThrowException;
          if ( !String__equals && GetJavaFunction(env, &StringClazz, &String__equals, 0, "java/lang/String", "equals") )
            goto LABEL_89;
          jvalue.i = trimmedPassword;
          v6 = (*env)->CallBooleanMethodA(env, v31, (jmethodID)String__equals, jvalue);
          if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
            goto LABEL_89;
      }
      }
    }
}
return v6;
}
```

大致过程:

1. 将字符串 "52pojie_2020_happy_chinese_new_year" 进行变形
2. 根据 key 来进行 xor 循环计算
3. 计算 MD5,并转换为 HEX 表示形式 (32 字符长度)
4. 去掉首位各一个字符,得到的字符串即为密码。

其实 `onClick` 函数也有看了一看,不过只是基本的裁剪空白文本并检查长度是否为 `30`。

整理成 JS:

```js
{
const key1 = 'thisiskey'; // 9 个字符
const key2 = '20200125';
const text = '52pojie_2020_happy_chinese_new_year';
const result = [];

for (let i = 0; i < text.length; i++) {
      let str, idx;

      // 我被 IDA F5 出来的这个逻辑给弄晕了,只好照抄了哈哈
      // if (!i || i & 3)
      // 52pojie_2020_happy_chinese_new_year
      //   2   0   2   0   0   1   2   5
      // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
      // 52po2ie_00202hap0y_c0ine1e_n2w_y5ar
      if ((i === 0) || ((i & 3) !== 0)) {
                str = text;
                idx = i;
      } else {
                str = key2;
                idx = (i >> 2) - 1;
      }
      result.push(str);
}

// 我直接粘贴到第三方网站查的 MD5
// result === "52po2ie_00202hap0y_c0ine1e_n2w_y5ar"
// md5(result) === "3ABE761061B420AA4CB37F40A7B257AD"

const md5_bytes = [];
'3ABE761061B420AA4CB37F40A7B257AD'.replace(/../g, z => {
      md5_bytes.push(parseInt(z, 16));
});
const md5_transformed = md5_bytes.map((x, i) => x ^ key1.charCodeAt(i % key1.length));
const password = md5_transformed.map(x => `0${x.toString(16)}`.slice(-2)).join('').slice(1, -1);
}
```

计算后得到密码 `ed61f6308c74bcf35c71729d4db24c`。

爱飞的猫 发表于 2020-2-14 04:01

snrtdwss 发表于 2020-2-13 11:26
请教下 我导入不显示 你分析那样的代码 怎么回事 不显示.thisiskey52pojie_2020_happy_chinese_new_year202 ...
1. 把 a1 变量类型改成 "JNIEnv *",并调整对应变量的类型
2. 之后会出现一个 "&unk1234" 这样的引用,点进去,改成 "char" 之类的类型(xyz 为字符串大小)
3. IDA 内刷新伪代码,应该会出现字符串引用了

爱飞的猫 发表于 2020-2-12 17:44

yaerhuo 发表于 2020-2-12 04:55
特牛的未来教育激活器的大佬,膜拜,第一道题今年解出来了,第二道安卓题一脸懵,不过看了半天还是不大懂

大多数情况下都是连蒙带猜... 外加一点运气w

yaerhuo 发表于 2020-2-12 04:55

特牛的未来教育激活器的大佬,膜拜,第一道题今年解出来了,第二道安卓题一脸懵,不过看了半天还是不大懂

雨夜故园 发表于 2020-2-12 10:09

cptw 发表于 2020-2-12 10:45

感谢分享

hmlhao 发表于 2020-2-12 11:02

感谢楼主分享。。。。。

Hmily 发表于 2020-2-12 11:03

原版UPX,只是修改了下把UPX压缩时候原始PE信息写入那块去掉了。

hnwang 发表于 2020-2-12 11:17

支持楼主 收藏学习一下

工程欧巴 发表于 2020-2-12 16:40

做个笔记,学到了

lixia2018 发表于 2020-2-12 17:12

页: [1] 2 3
查看完整版本: 2020 鼠年春节红包笔记【2 和 3 题】