【Android 逆向】【攻防世界】黑客精神
1. apk 安装到手机,提示输入注册码
2. jadx打开apk
MainActivity.java
@Override // android.app.Activity
public void onCreate(Bundle savedInstanceState) {
String str2;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("com.gdufs.xman m=", "Xman");
MyApp myApp = (MyApp) getApplication();
int m = MyApp.m;
if (m == 0) {
str2 = "未注册";
} else if (m == 1) {
str2 = "已注册";
} else {
str2 = "已混乱";
}
setTitle("Xman" + str2);
this.btn1 = (Button) findViewById(R.id.button1);
this.btn1.setOnClickListener(new View.OnClickListener() { // from class: com.gdufs.xman.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
MyApp myApp2 = (MyApp) MainActivity.this.getApplication();
int m2 = MyApp.m;
if (m2 == 0) {
MainActivity.this.doRegister();
return;
}
((MyApp) MainActivity.this.getApplication()).work();
Toast.makeText(MainActivity.this.getApplicationContext(), MainActivity.workString, 0).show();
}
});
}
public void doRegister() {
new AlertDialog.Builder(this).setTitle("注册").setMessage("Flag就在前方!").setPositiveButton("注册", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.MainActivity.3
@Override // android.content.DialogInterface.OnClickListener
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
ComponentName cpname = new ComponentName(BuildConfig.APPLICATION_ID, "com.gdufs.xman.RegActivity");
intent.setComponent(cpname);
MainActivity.this.startActivity(intent);
MainActivity.this.finish();
}
}).setNegativeButton("不玩了", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.MainActivity.2
@Override // android.content.DialogInterface.OnClickListener
public void onClick(DialogInterface dialog, int which) {
Process.killProcess(Process.myPid());
}
}).show();
}
public void work(String str) {
workString = str;
MyApp.java
public class MyApp extends Application {
public static int m = 0;
public native void initSN();
public native void saveSN(String str);
public native void work();
static {
System.loadLibrary("myjni");
}
@Override // android.app.Application
public void onCreate() {
initSN();
Log.d("com.gdufs.xman m=", String.valueOf(m));
super.onCreate();
}
}
大概理了一下逻辑,App 先执行 initSN ,然后点击界面按钮,按钮根据APP.m的值判断是走RegActivity还是往下执行App.work(),然后弹出toast
3. IDA 打开so看看
函数都是JNI_OnLoad中注册的
int __fastcall initSN(JNIEnv *env)
{
FILE *v2; // r0
FILE *v3; // r4
JNIEnv *t_env; // r0
int file_len; // r7
void *v6; // r5
JNIEnv *tt_env; // r0
int v9; // r1
v2 = fopen("/sdcard/reg.dat", "r+");
v3 = v2;
if ( !v2 )
{
t_env = env;
return setValue(t_env, 0);
}
fseek(v2, 0, 2);
file_len = ftell(v3);
v6 = malloc(file_len + 1);
if ( !v6 )
{
fclose(v3);
t_env = env;
return setValue(t_env, 0);
}
fseek(v3, 0, 0);
fread(v6, file_len, '\x01', v3);
*((_BYTE *)v6 + file_len) = 0;
if ( !strcmp((const char *)v6, "EoPAoY62@ElRD") )
{
tt_env = env;
v9 = 1;
}
else
{
tt_env = env;
v9 = 0;
}
setValue(tt_env, v9);
return j_fclose(v3);
}
initSn 大概逻辑,就是读一个文件,看文件的值是不是EoPAoY62@ElRD,是就反射写App.m = 1 其他情况写0;(得到关键信息EoPAoY62@ElRD)
jmethodID __fastcall n3Work(JNIEnv *env)
{
jint Value; // r0
JNIEnv *t_env; // r0
const char *v4; // r1
bool v5; // zf
initSN(env);
Value = getValue(env);
if ( Value )
{
v5 = Value == 1;
t_env = env;
if ( v5 )
v4 = "输入即是flag,格式为xman{……}!";
else
v4 = "状态不太对。。";
}
else
{
t_env = env;
v4 = "还不行呢!";
}
return callWork(t_env, (int)v4);
}
work的逻辑就是执行以下initSN 然后就判断m的值是不是1,如果是1,就输出输入即是flag,格式为xman{……}!
int __fastcall n2_saveSn(JNIEnv *env, jobject a2, jstring snKey)
{
FILE *v5_fp; // r7
_DWORD *p_v21; // r4
const char *v8; // r3
int v9; // r0
int v10; // r1
_WORD *v11; // r5
JNIEnv *v12; // r0
int v13; // r4
JNIEnv v14; // r3
signed int v15_i; // r6
const char *t_snKey_chars; // r9
char *p_snKey_chars; // r5
signed int v18_len; // r10
char v19; // r2
char v20; // r3
_BYTE v21[56]; // [sp+0h] [bp-38h] BYREF
v5_fp = fopen("/sdcard/reg.dat", "w+");
if ( !v5_fp )
return j___android_log_print(3, "com.gdufs.xman", byte_D0C96DCA);
p_v21 = v21;
v8 = "W3_arE_whO_we_ARE";
do
{
v9 = *(_DWORD *)v8;
v8 += 8;
v10 = *((_DWORD *)v8 - 1);
*p_v21 = v9;
p_v21[1] = v10;
v11 = p_v21 + 2;
p_v21 += 2;
}
while ( v8 != "E" );
v12 = env;
v13 = 2016;
*v11 = *(_WORD *)v8;
v14 = *env;
v15_i = 0;
t_snKey_chars = v14->GetStringUTFChars(v12, snKey, 0);
p_snKey_chars = (char *)t_snKey_chars;
v18_len = strlen(t_snKey_chars);
while ( v15_i < v18_len )
{
if ( v15_i % 3 == 1 )
{
v13 = (v13 + 5) % 16;
v19 = v21[v13 + 1]; // v21[v13 - 23]
}
else if ( v15_i % 3 == 2 )
{
v13 = (v13 + 7) % 15;
v19 = v21[v13 + 2];
}
else
{
v13 = (v13 + 3) % 13;
v19 = v21[v13 + 3]; // v21[v13 -21]
}
v20 = *p_snKey_chars;
++v15_i;
*p_snKey_chars++ = v20 ^ v19;
}
fputs(t_snKey_chars, v5_fp);
return j_fclose(v5_fp);
}
里面有两个重要逻辑,
- 算出初始密码
v8 = "W3_arE_whO_we_ARE";
do
{
v9 = *(_DWORD *)v8;
v8 += 8;
v10 = *((_DWORD *)v8 - 1);
*p_v21 = v9;
p_v21[1] = v10;
v11 = p_v21 + 2;
p_v21 += 2;
}
while ( v8 != "E" );
v12 = env;
v13 = 2016;
这里有个重要细节,这里卡了我很久: (_DWORD *)v8 相当于把v8 转为一个 _DWORD * 指针,对这种指针取值时 一次取四个字节的数据, *((_DWORD *)v8 - 1) 相当于指针值 - 4 后再取四个字节的数据
转换位python为
v8 = 'W3_arE_whO_we_ARE'
i = 0
key_v8 = v8[0]
ret = ''
while (v8[i] != 'E'):
v9 = v8[i:i+4]
i += 8
v10 = v8[i-4:i]
ret += v9
ret += v10
ret+='E'
print(ret)
# 输出还是 W3_arE_whO_we_ARE,开始觉得搞错了,其实时障眼法
然后时加密计算逻辑
v18_len = strlen(t_snKey_chars);
while ( v15_i < v18_len )
{
if ( v15_i % 3 == 1 )
{
v13 = (v13 + 5) % 16;
v19 = v21[v13 + 1]; // v21[v13 - 23]
}
else if ( v15_i % 3 == 2 )
{
v13 = (v13 + 7) % 15;
v19 = v21[v13 + 2];
}
else
{
v13 = (v13 + 3) % 13;
v19 = v21[v13 + 3]; // v21[v13 -21]
}
v20 = *p_snKey_chars;
++v15_i;
*p_snKey_chars++ = v20 ^ v19;
}
从算法上看时不用细看时怎么操作的,可以发现输入和输出时一一对应的,且密钥也是一一对应的,那么把输出当作输入,就可以得到原来的输入
python还原
v8 = 'W3_arE_whO_we_ARE'
i = 0
key_v8 = v8[0]
ret = ''
while (v8[i] != 'E'):
v9 = v8[i:i+4]
i += 8
v10 = v8[i-4:i]
ret += v9
ret += v10
ret+='E'
print(ret)
in_str = 'EoPAoY62@ElRD'
v13 = 2016
result = ''
for i in range(0, len(in_str)):
if (i % 3 == 1):
v13 = (v13 + 5) % 16
v19 = ret[v13+1]
elif i % 3 == 2:
v13 = (v13 + 7) % 15
v19 = ret[v13+2]
else:
v13 = (v13 + 3) % 13
v19 = ret[v13+3]
v20 = in_str[i]
result += chr(ord(v20) ^ ord(v19))
print(result)
# 日志
W3_arE_whO_we_ARE
201608Am!2333
成功得到flag