打开某ctf的apk运行结果如下
使用jadx打开apk后,查看关键代码如下:
package org.isclab.iscc;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button button;
private EditText editFlag;
public native int checkFlag(byte[] bArr, int i);
static {
System.loadLibrary("ISCC");
}
/* access modifiers changed from: protected */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.editFlag = (EditText) findViewById(R.id.textView3);
this.button = (Button) findViewById(R.id.button);
this.button.setOnClickListener(new View.OnClickListener() {
/* class org.isclab.iscc.MainActivity.AnonymousClass1 */
public void onClick(View v) {
String flag = Digest.encode(MainActivity.this.editFlag.getText().toString()).trim();
Log.i("ISCC", flag);
if (MainActivity.this.checkFlag(flag.getBytes(), flag.length()) == 1) {
Toast.makeText(MainActivity.this, "Flag验证成功!", 0).show();
} else {
Toast.makeText(MainActivity.this, "Flag验证失败!", 0).show();
}
}
});
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_settings) {
return true;
}
去看checkflag函数
package org.isclab.iscc;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button button;
private EditText editFlag;
public native int checkFlag(byte[] bArr, int i);
static {
System.loadLibrary("ISCC");
}
/* access modifiers changed from: protected */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.editFlag = (EditText) findViewById(R.id.textView3);
this.button = (Button) findViewById(R.id.button);
this.button.setOnClickListener(new View.OnClickListener() {
/* class org.isclab.iscc.MainActivity.AnonymousClass1 */
public void onClick(View v) {
String flag = Digest.encode(MainActivity.this.editFlag.getText().toString()).trim();
Log.i("ISCC", flag);
if (MainActivity.this.checkFlag(flag.getBytes(), flag.length()) == 1) {
Toast.makeText(MainActivity.this, "Flag验证成功!", 0).show();
} else {
Toast.makeText(MainActivity.this, "Flag验证失败!", 0).show();
}
}
});
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
出现native明显so层 IDA看下
IDA载入so文件,发现没有函数名类似“org_isclab_iscc”,那就是动态注册了。查看F5之后的JNI_OnLoad,修正参数后代码如下:
signed int __cdecl JNI_OnLoad(int a1)
{
int v1; // eax
signed int v2; // edx
int v4; // [esp+1Ch] [ebp-10h]
v4 = 0;
v1 = (*(int (__cdecl **)(int, int *, signed int))(*(_DWORD *)a1 + 24))(a1, &v4, 65540);
v2 = -1;
if ( !v1 )
{
register_ndk_load(v4);
v2 = 65540;
}
return v2;
}
去看register_ndk_load(v4);
看下地址调用了这个:
off_2020
可以看到调用了Checkflag 函数
整体程序到了这里
F5c代码如下
BOOL4 __cdecl native_checkFlag(int a1, int a2, int a3, size_t size)
{
char *v4; // ebp
unsigned int v5; // edi
bool v6; // cf
bool v7; // zf
unsigned int v8; // eax
size_t v9; // edx
char v10; // ST2B_1
signed int v11; // ecx
const char *v12; // edi
_BYTE *v13; // esi
int v14; // esi
void *src; // [esp+2Ch] [ebp-20h]
src = malloc(size);
(*(void (__cdecl **)(int, int, _DWORD, size_t, void *))(*(_DWORD *)a1 + 800))(a1, a3, 0, size, src);
v4 = (char *)malloc(size + 1);
memset(v4, 0, size + 1);
memcpy(v4, src, size);
v5 = (signed int)size / 2;
v6 = 0;
v7 = (signed int)size / 2 == 0;
if ( (signed int)size / 2 > 0 )
{
v8 = 0;
v9 = (size_t)&v4[size];
do
{
v10 = v4[v8] - 5;
v4[v8++] = *(_BYTE *)(v9 - 1);
v6 = v8 < v5;
v7 = v8 == v5;
*(_BYTE *)(v9-- - 1) = v10;
}
while ( v8 != v5 );
}
v11 = 29;
v12 = "=0HWYl1SE5UQWFfN?I+PEo.UcshU";
v13 = v4;
v4[size] = 0;
do
{
if ( !v11 )
break;
v6 = *v13 < (const unsigned __int8)*v12;
v7 = *v13++ == *v12++;
--v11;
}
while ( v7 );
v14 = (char)((!v6 && !v7) - v6);
free(v4);
free(src);
return v14 == 0;
}
逻辑很简单,遍历前一半的数据,每一个加5,然后跟对称的数据进行交换。我们还原的代码就是从后面开始遍历,遍历后一半,然后跟对称的数据进行交换,编写java代码如下:
public class decode{
public static void main(String[] args) {
char Key[] = "=0HWYl1SE5UQWFfN?I+PEo.UcshU".toCharArray();
int len = Key.length;
for (int i = len - 1; i >= len / 2; i--) {
Key[i] += 5;
char temp = Key[len - i - 1];
Key[len - i - 1] = Key[i];
Key[i] = temp;
}
System.out.println(Key);
}
}