0x00拯救鲁班七号
CM下载网址:链接: https://pan.baidu.com/s/10EPc2KCWqcaT9wLw5eyxWw 密码: swkn
界面流程:一个输入框,一个按钮。
0x01JAVA层分析
- 反汇编代码逻辑分析
加载了humen.so文件,说明这是写在native层的加密方法。说明已在代码注释中,不再重复。static { System.loadLibrary("humen"); } public void checkPass(String paramString) { String str = CheckUtil.checkPass(paramString); //②来到CheckUtil.checkPass进行判断,返回对应状态赋给str if ((paramString == null) || (paramString.equals(""))) { Toast.makeText(getApplicationContext(), getString(2131034114), 1).show(); return; } if (str.equals("a")) { startActivity(new Intent(this, WindowActivity.class)); return; } Toast.makeText(getApplicationContext(), getString(2131034115), 1).show(); } protected void onCreate(Bundle paramBundle) { super.onCreate(paramBundle); setContentView(2130903040); ((Button)findViewById(2131165184)).setOnClickListener(new View.OnClickListener() { public void onClick(View paramAnonymousView) { paramAnonymousView = (TextView)MainActivity.this.findViewById(2131165185); MainActivity.this.checkPass(paramAnonymousView.getText().toString().trim()); //①传入提交的String } }); }
public class CheckUtil { public static native String checkPass(String paramString); //③看到这是JNI方法,固需要分析native层的checkPass方法 }
0x02Native层分析
- 找到反汇编目录下的humen.so文件,用32位IDA载入
64位IDA好像分析不了,但是这里会有两个,这里我选择x86中的so文件,因为另一个经我测试分析不到checkPass方法。 - 双击该方法后在右侧汇编代码部分F5查看伪C语言代码
贴上反汇编有部分错误的CheckPass方法的全部代码:int __usercall Java_com_humen_crackme010_CheckUtil_checkPass@<eax>(int a1@<ebx>, int *a2, int a3, int a4) { int v4; // ebx@1 const char *v5; // edi@1 signed __int32 v6; // ebp@1 char v7; // dl@3 signed int v8; // eax@3 char *v9; // edx@5 char *v10; // eax@5 char v11; // si@5 int v12; // esi@8 int v13; // eax@9 char *v14; // edx@10 int v15; // eax@10 char v16; // cl@11 bool v17; // sf@12 unsigned __int8 v18; // of@12 int v19; // eax@12 int result; // eax@13 signed int i; // [sp+14h] [bp-28h]@4 signed __int32 v22; // [sp+14h] [bp-28h]@7 signed __int32 v23; // [sp+18h] [bp-24h]@1 sub_82B(); v4 = a1 + 9207; v5 = (const char *)getStringUnicodeChars(a2, a4, &aUtf8[v4 - 12208]); v23 = 0; v6 = strlen(v5); while ( 1 ) { v23 += 2; if ( v6 <= v23 ) break; v7 = v5[v23 - 2]; v5[v23 - 2] = v5[v23 - 1]; v8 = 0; v5[v23 - 1] = v7; if ( v6 > 4 ) { for ( i = 4; ; i += 4 ) { v10 = (char *)&v5[v8]; v9 = v10; v11 = *v10; v10 += 4; *v9 = *v10; *v10 = v11; v8 = i; if ( v6 <= i + 4 ) break; } } } v22 = strlen(v5); if ( v22 > 0 ) { v12 = 0; do { v13 = v5[v12++]; __android_log_print(4, &aHumen[v4 - 12208], &aC[v4 - 12208], v13); } while ( v22 != v12 ); } printf(&aS[v4 - 12208], v5); v14 = (&t_ptr)[v4 - 12208]; v15 = 0; if ( *v5 == *v14 ) { do v16 = v14[v15++ + 1]; while ( v5[v15] == v16 ); } v18 = __OFSUB__(v6, v15); v17 = v6 - v15 < 0; v19 = *a2; if ( v17 ^ v18 ) result = (*(int (__cdecl **)(int *, char *))(v19 + 668))(a2, &aA[v4 - 12208]); else result = (*(int (__cdecl **)(int *, char *))(v19 + 668))(a2, &aB[v4 - 12208]); return result; }
- 分析主要逻辑
理一下这么多的v..变量,可以发现v5是传入的string字符串,v6是通过strlen取得的v5的长度。
于是上面的代码加密逻辑整理为下面的。v23 = 0; length = strlen(input); while ( 1 ) { v23 += 2; if ( length <= v23 ) //字符串长度不够2、4、6..就退出while循环 break; v7 = input[v23 - 2]; //v7 = input[0] input[v23 - 2] = input[v23 - 1]; //input[0] = input[1] v8 = 0; input[v23 - 1] = v7; //input[1] = v7 <!--所以上面是每两位(0和1、2和3...)替换一次--> if ( length > 4 ) { for ( i = 4; ; i += 4 ) { v10 = (char *)&input[v8]; //v10 = &input[0]; v9 = v10; v11 = *v10; //v11 = input[0]; v10 += 4; //v10 = input[4]; *v9 = *v10; //input[0] = input[4]; *v10 = v11; //input[4] = input[0]; v8 = i; //v8 = 4; if ( length <= i + 4 ) //当length=16时,当i=12时就退出。此时交换了(0和4,i=4,v8=0)、(4和8,i=8,v8=4)、(8和12,i=12,v8=8,break;) break; } } }
v14 = (&t_ptr)[v4 - 12208]; //v14是比对的字符串,双击t_ptr进入,其值为"S!@#@1FD23154A34" v15 = 0; if ( *input == *v14 ) { do v16 = v14[v15++ + 1]; //这里v15和v16会影响后面判断的返回值,固当input经过上述流程后等于v14,则成功,我们需要做的是还原算法,倒着走。 while ( input[v15] == v16 ); }
0x03 C语言代码还原算法
Part I:
#include <stdio.h>
#include <stdlib.h>
void swap(char *p,char *q){
char temp = *p;
*p = *q;
*q = temp;
}
int getLength(char *p){
int len = 0;
while((*p) != '\0'){
len++;
p++;
}
return len;
}
int main(){
char str[] = "S!@#@1FD23154A34";
int length = getLength(str);
for(int j=14; j>0;j=j-2){ //最外层需要总循环次数,倒着来,即14、13...
if(length>4){
for(int i=12;i>1;i=i-4){ //内层循环交换,12和8、8和4、4和0
swap(&str[i-4],&str[i]);
}
}
swap(&str[j-1],&str[j-2]);
}
printf("%s\n",str);
system("pause");
}
跑出真码{!@#@ASDF34511234}
Part II:测试成功截图
0x04总结
在native层保护加密的代码,能起到一定的保护。用IDA分析伪代码可以为我们提供算法的逻辑,以此可以构造还原的算法。安卓逆向,我在路上!2018年3月11日 21:18:39
参考网址:http://blog.csdn.net/Magic1an/article/details/77418294
https://www.52pojie.cn/thread-602744-1-1.html