2019ISCC Mobile
2019-05-27 本文已影响0人
丿feng
0x01 Mobile1
用Android killer载入找到入口函数
package com.iscc.crackme;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity
extends AppCompatActivity
{
static
{
System.loadLibrary("native-lib");
}
private boolean checkFirst(String paramString)
{
if (paramString.length() != 16) {
return false;
}
int i = 0;
while (i < paramString.length()) {
if (paramString.charAt(i) <= '8')
{
if (paramString.charAt(i) < '1') {
return false;
}
i += 1;
}
else
{
return false;
}
}
return true;
}
public native boolean checkSecond(String paramString);
protected void onCreate(final Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2131296284);
paramBundle = (EditText)findViewById(2131165240);
((Button)findViewById(2131165218)).setOnClickListener(new View.OnClickListener()
{
public void onClick(View paramAnonymousView)
{
paramAnonymousView = paramBundle.getText().toString().trim();
if ((MainActivity.this.checkFirst(paramAnonymousView)) && (MainActivity.this.checkSecond(paramAnonymousView)))
{
Toast.makeText(MainActivity.this, "注册成功!", 0).show();
return;
}
Toast.makeText(MainActivity.this, "注册失败!", 0).show();
}
});
}
}
代码非常简单,对注册码进行两次check,第一次check是
private boolean checkFirst(String paramString)
{
if (paramString.length() != 16) {
return false;
}
int i = 0;
while (i < paramString.length()) {
if (paramString.charAt(i) <= '8')
{
if (paramString.charAt(i) < '1') {
return false;
}
i += 1;
}
else
{
return false;
}
}
return true;
}
功能是check注册码是否为16位,以及注册码是否为1~8的数字组合
第二个check函数在native层,分析so文件
public native boolean checkSecond(String paramString);
用ida64载入64位的so文件,观察入口函数伪代码
char __fastcall Java_com_iscc_crackme_MainActivity_checkSecond(__int64 a1, __int64 a2, __int64 a3)
{
char result; // al
char v4; // [rsp+6h] [rbp-8Ah]
char v5; // [rsp+13h] [rbp-7Dh]
char v6; // [rsp+40h] [rbp-50h]
char v7; // [rsp+58h] [rbp-38h]
char v8; // [rsp+70h] [rbp-20h]
unsigned __int64 v9; // [rsp+88h] [rbp-8h]
v9 = __readfsqword(0x28u);
jstring2str(&v8, a1, a3);
v5 = 0;
std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(&v7, &v8);
v4 = 0;
if ( checkfirst((__int64)&v7) & 1 )
{
std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(&v6, &v8);
v5 = 1;
v4 = checkAgain(&v6);
}
if ( v5 & 1 )
std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v6);
std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v7);
std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v8);
result = v4 & 1;
if ( __readfsqword(0x28u) == v9 )
result = v4 & 1;
return result;
}
发现也进行了两次check分别是checkfirst 以及 checkAgain,所以只要过了这两个check就完事了
值得注意的是jstring2str函数是将输入转换为字节数组
观察checkfirst函数
__int64 __fastcall checkfirst(__int64 a1)
{
signed __int64 v2; // [rsp+0h] [rbp-118h]
signed __int64 v3; // [rsp+18h] [rbp-100h]
signed int i; // [rsp+30h] [rbp-E8h]
char v5; // [rsp+37h] [rbp-E1h]
for ( i = 1; i < 8; ++i )
{
if ( *(_BYTE *)a1 & 1 )
v3 = *(_QWORD *)(a1 + 16);
else
v3 = a1 + 1;
if ( *(_BYTE *)a1 & 1 )
v2 = *(_QWORD *)(a1 + 16);
else
v2 = a1 + 1;
if ( *(char *)(v3 + i) <= *(char *)(v2 + i - 1) )// 升序
{
v5 = 0;
return v5 & 1;
}
}
v5 = 1;
return v5 & 1;
}
关键代码是
if ( *(char *)(v3 + i) <= *(char *)(v2 + i - 1) )// 升序
由于输入只有16位,由此我们可以大胆猜测*(_BYTE *)a1 & 1
的值为0
即
check first
*flag&1==0 && *(flag+i) > *f(lag+i-1) (1<=i<=7) 即前八位为 1 2 3 4 5 6 7 8
故可以得到注册码前八位是升序的又因注册码是1~8所以可以得到注册码为
12345678********
进入checkAgain函数
char __fastcall checkAgain(__int64 a1)
{
char result; // al
signed __int64 v2; // [rsp+10h] [rbp-170h]
signed __int64 v3; // [rsp+20h] [rbp-160h]
signed int l; // [rsp+3Ch] [rbp-144h]
signed int k; // [rsp+40h] [rbp-140h]
int j; // [rsp+44h] [rbp-13Ch]
signed int i; // [rsp+48h] [rbp-138h]
char v8; // [rsp+4Fh] [rbp-131h]
int v9; // [rsp+130h] [rbp-50h]
int v10; // [rsp+134h] [rbp-4Ch]
int v11; // [rsp+148h] [rbp-38h]
int v12; // [rsp+14Ch] [rbp-34h]
int v13[10]; // [rsp+150h] [rbp-30h]
unsigned __int64 v14; // [rsp+178h] [rbp-8h]
v14 = __readfsqword(0x28u);
for ( i = 0; i < 8; ++i )
{
if ( *(_BYTE *)a1 & 1 )
v3 = *(_QWORD *)(a1 + 16);
else
v3 = a1 + 1;
v13[i] = *(char *)(v3 + i) - 49;
}
for ( j = 0; j < 8; ++j )
{
if ( *(_BYTE *)a1 & 1 )
v2 = *(_QWORD *)(a1 + 16);
else
v2 = a1 + 1;
*(&v9 + j) = *(char *)(v2 + j + 8) - 49;
}
if ( v12 + v9 == 5 )
{
if ( v11 + v10 == 12 )
{
if ( v9 < v12 )
{
for ( k = 1; k < 8; ++k )
{
for ( l = 0; l < k; ++l )
{
if ( v13[l] == v13[k] )
{
v8 = 0;
goto LABEL_34;
}
if ( *(&v9 + l) == *(&v9 + k) )
{
v8 = 0;
goto LABEL_34;
}
if ( v13[k] - v13[l] == *(&v9 + k) - *(&v9 + l) )
{
v8 = 0;
goto LABEL_34;
}
if ( v13[k] - v13[l] == *(&v9 + l) - *(&v9 + k) )
{
v8 = 0;
goto LABEL_34;
}
}
}
v8 = 1;
}
else
{
v8 = 0;
}
}
else
{
v8 = 0;
}
}
else
{
v8 = 0;
}
LABEL_34:
result = v8;
if ( __readfsqword(0x28u) == v14 )
result = v8 & 1;
return result;
}
代码颇长,逐步分析
check主要进行以下功能
check again
i -> range(0,8)
(int)v13[i] = *(flag+i) - 49
(int)v9[i] = *(flag+i) - 49
v9[7] + v9[0] == 5 && v9[6] + v9[1] == 12 && v9[0] < v9[7]
for k in range(1,8)
for l in range(0,k)
v13[l] != v13[k]
v9[l] != v9[k]
v13[k] - v13[l] != abs(v9[k] - v9[l])
易得注册码后八位也是不相同的
对v9[7] + v9[0] == 5 && v9[6] + v9[1] == 12 && v9[0] < v9[7]
结合
v13[k] - v13[l] != abs(v9[k] - v9[l])
可以约束求解注册码后八位
最终解得注册码为 1234567836275184