第 2 题 (Win32)
看上去像是 aspack/upx 简易版本?在 XP 下直接跳到 OEP 下 dump… 虽然不跨平台但在 XP 下能正常执行。
将 dump 出来的文件拉到 IDR 做分析,然后导出 map 文件导入到 OD 里。
同时在 IDR 软件中寻找密码框更新后的函数地址:
00617C3C > . 55 push ebp ; _Unit71.TForm1.edtPwdChange_00617C3C
................
00617C5D . 8D55 E0 lea edx,dword ptr ss:[ebp-0x20]
00617C60 . 8B83 D0030000 mov eax,dword ptr ds:[ebx+0x3D0]
00617C66 . E8 4DA1F2FF call <2_1.Vcl.Controls.TControl.GetText_00541DB8>
00617C6B . 8B45 E0 mov eax,dword ptr ss:[ebp-0x20] ; 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:[eax]
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:[0x616A58]
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:[ebp-0x4] ; <---- 储存位置
00617C91 . 50 push eax
00617C92 . 8D55 D8 lea edx,dword ptr ss:[ebp-0x28]
00617C95 . 8B83 D0030000 mov eax,dword ptr ds:[ebx+0x3D0]
00617C9B . E8 18A1F2FF call <2_1.Vcl.Controls.TControl.GetText_00541DB8>
00617CA0 . 8B45 D8 mov eax,dword ptr ss:[ebp-0x28] ; eax = 键入的密码
00617CA3 . 8D4D DC lea ecx,dword ptr ss:[ebp-0x24]
00617CA6 . BA 07000000 mov edx,0x7
00617CAB . E8 3C76EFFF call <2_1.GetTextLeft> ; 取出文本左边 edx 个字符
00617CB0 . 8B55 DC mov edx,dword ptr ss:[ebp-0x24] ; 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:[ebp-0x8] ; <---- 储存位置
................
00617CDF . E8 5076EFFF call <2_1.System.Generics.Defaults.sub_0050F334_0050F334> ; 后面再取出 4 个字符
00617CE4 . 8B55 D4 mov edx,dword ptr ss:[ebp-0x2C]
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:[ebp-0x34] ; 再取出最后 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] ; edx = 0x804
00617D3F . 8D45 FC lea eax,dword ptr ss:[ebp-0x4] ; eax = MD5(前七位)
00617D42 . E8 597DE1FF call <2_1._Unit9.sub_0042FAA0_0042FAA0>
00617D47 . 8B45 F0 mov eax,dword ptr ss:[ebp-0x10]
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:[ebp-0x19]
................
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:[ebp-0x1a]
................
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:[ebp-0x1b]
................
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 数据在线查询,分别得到:
三段字样。将其合并起来就是密码 52pojie2019game
。
第 3 题 (安卓题)
因为不太懂怎么搞调试,所以把 so 文件解压出来,然后扔到 IDA 里去静态分析。
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; // [sp+18h] [bp-80h]
jint v35; // [sp+1Ch] [bp-7Ch]
void *str__this_is_key; // [sp+20h] [bp-78h]
int v37; // [sp+20h] [bp-78h]
jbyteArray str__20200125; // [sp+28h] [bp-70h]
jobject md5_value; // [sp+28h] [bp-70h]
jint trimmedPassword; // [sp+2Ch] [bp-6Ch]
int String__equals; // [sp+30h] [bp-68h] MAPDST
int v43; // [sp+34h] [bp-64h]
int SB__toString; // [sp+38h] [bp-60h] MAPDST
int v45; // [sp+3Ch] [bp-5Ch]
int v46; // [sp+40h] [bp-58h]
int sb__init; // [sp+44h] [bp-54h]
int a3a; // [sp+50h] [bp-48h]
int StringClazz; // [sp+54h] [bp-44h]
int pFn; // [sp+58h] [bp-40h]
int sbInst; // [sp+5Ch] [bp-3Ch]
int MD; // [sp+60h] [bp-38h]
int a2a; // [sp+64h] [bp-34h]
jvalue jvalue[2]; // [sp+68h] [bp-30h]
int v57; // [sp+78h] [bp-20h]
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[0].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[0].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[0].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[0].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[0].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[0].z;
if ( !(*env)->ExceptionCheck(env) )
{
jvalue[0].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[0].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[0].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[0].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[0].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[0].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[1].i = 31;
jvalue[0].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[0].i = trimmedPassword;
v6 = (*env)->CallBooleanMethodA(env, v31, (jmethodID)String__equals, jvalue);
if ( ((int (__fastcall *)(JNIEnv *))(*env)->ExceptionCheck)(env) )
goto LABEL_89;
}
}
}
}
return v6;
}
大致过程:
- 将字符串 "52pojie_2020_happy_chinese_new_year" 进行变形
- 根据 key 来进行 xor 循环计算
- 计算 MD5,并转换为 HEX 表示形式 (32 字符长度)
- 去掉首位各一个字符,得到的字符串即为密码。
其实 onClick
函数也有看了一看,不过只是基本的裁剪空白文本并检查长度是否为 30
。
整理成 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[idx]);
}
// 我直接粘贴到第三方网站查的 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
。