内容简介:静态分析原生层程序基本的过程如下利用jadx反编译apk,确定应用的主活动程序的主活动为 com.example.mobicrackndk.CrackMe。
基本方法
静态分析原生层程序基本的过程如下
- 直接解压提取 so 文件(/lib文件夹)
- ida 反编译 so 文件阅读Arm汇编or反汇编代码
- 根据 java 层的代码来分析 so 代码。
- 根据 so 代码的逻辑辅助整个程序的分析。
原生层静态分析例子
2015-福建海峡两岸CTF-APK逆向,逆向试试吧
反编译
利用jadx反编译apk,确定应用的主活动
<!--?xml version="1.0" encoding="utf-8"?--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:versioncode="1" android:versionname="1.0" package="com.example.mobicrackndk"> <uses-sdk android:minsdkversion="8" android:targetsdkversion="17"> <application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:allowbackup="true"> <activity android:label="@string/app_name" android:name="com.example.mobicrackndk.CrackMe"> <intent-filter> <action android:name="android.intent.action.MAIN"> <category android:name="android.intent.category.LAUNCHER"> </category></action></intent-filter> </activity> </application> </uses-sdk></manifest>
程序的主活动为 com.example.mobicrackndk.CrackMe。
分析主活动
程序的基本情况就是利用 native 函数 testFlag 判断用户传入的 pwdEditText 是否满足要求。
public native boolean testFlag(String str);
static {
System.loadLibrary("mobicrackNDK");
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView((int) R.layout.activity_crack_me);
this.inputButton = (Button) findViewById(R.id.input_button);
this.pwdEditText = (EditText) findViewById(R.id.pwd);
this.inputButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
CrackMe.this.input = CrackMe.this.pwdEditText.getText().toString();
if (CrackMe.this.input == null) {
return;
}
if (CrackMe.this.testFlag(CrackMe.this.input)) {
Toast.makeText(CrackMe.this, CrackMe.this.input, 1).show();
} else {
Toast.makeText(CrackMe.this, "Wrong flag", 1).show();
}
}
});
}
分析so文件
自然我们首先会去直接找 testFlag 函数,凡是并没有直接找到。我们只好首先分析 JNI_Onload 函数,如下
signed int __fastcall JNI_OnLoad(JNIEnv *a1)
{
JNIEnv *v1; // r4
JNIEnv *env; // r5
char *v3; // r7
int class_; // r1
const char *v5; // r1
JNIEnv *v7; // [sp+Ch] [bp-1Ch]
v1 = a1;
v7 = 0;
printf("JNI_OnLoad");
if ( ((int (__fastcall *)(JNIEnv *, JNIEnv **, signed int))(*v1)->FindClass)(v1, &v7, 65540) )
goto LABEL_7;
env = v7;
v3 = classPathName[0];
fprintf((FILE *)((char *)&_sF + 168), "RegisterNatives start for '%s'", classPathName[0]);
class_ = ((int (__fastcall *)(JNIEnv *, char *))(*env)->FindClass)(env, v3);
if ( !class_ )
{
v5 = "Native registration unable to find class '%s'";
LABEL_6:
fprintf((FILE *)((char *)&_sF + 168), v5, v3);
LABEL_7:
fputs("GetEnv failed", (FILE *)((char *)&_sF + 168));
return -1;
}
if ( ((int (__fastcall *)(JNIEnv *, int, char **, signed int))(*env)->RegisterNatives)(env, class_, off_400C, 2) < 0 )
{
v5 = "RegisterNatives failed for '%s'";
goto LABEL_6;
}
return 65540;
}
可以发现,程序在这里动态注册了类和相应的函数 off_400C。仔细看一下该函数
.data:0000400C off_400C DCD aTestflag ; DATA XREF: JNI_OnLoad+68↑o .data:0000400C ; .text:off_1258↑o .data:0000400C ; "testFlag" .data:00004010 DCD aLjavaLangStrin_0 ; "(Ljava/lang/String;)Z" .data:00004014 DCD abcdefghijklmn+1 .data:00004018 DCD aHello ; "hello" .data:0000401C DCD aLjavaLangStrin_1 ; "()Ljava/lang/String;" .data:00004020 DCD native_hello+1 .data:00004020 ; .data ends
可以发现,确实就是 testflag 函数,其对应的函数名为 abcdefghijklmn。
分析abcdefghijklmn
可以发现,程序主要在三个部分对输入进行判断和计算
bool __fastcall abcdefghijklmn(JNIEnv *a1, int a2, void *a3)
{
void *input; // r6
JNIEnv *env; // r7
_BOOL4 rel; // r4
size_t i; // r6
const char *v7; // r2
jmethodID clackeyID; // r2
int keyID; // r4
void *key_; // r0
const char *key; // r5
jclass class_calc; // [sp+4h] [bp-C4h]
const char *password; // [sp+8h] [bp-C0h]
char firstPart[8]; // [sp+14h] [bp-B4h]
char v16; // [sp+1Ch] [bp-ACh]
char secondPart[8]; // [sp+20h] [bp-A8h]
char v18; // [sp+28h] [bp-A0h]
char s; // [sp+2Ch] [bp-9Ch]
input = a3;
env = a1;
if ( !jniEnv )
jniEnv = a1;
memset(&s, 0, 0x80u);
password = (*jniEnv)->GetStringUTFChars(jniEnv, input, 0);
rel = 0;
if ( strlen(password) == 16 )
{
i = 0;
do
{
firstPart[i] = password[i] - i;
++i;
}
while ( i != 8 ); // i==8的时候退出
rel = 0;
v16 = 0;
if ( !strcmp(seed[0], firstPart) ) // QflMn`fH
{
class_calc = (*jniEnv)->FindClass(jniEnv, "com/example/mobicrackndk/Calc");
if ( !class_calc )
{
v7 = "class,failed";
LABEL_11:
_android_log_print(4, "log", v7);
exit(1);
}
clackeyID = (*jniEnv)->GetStaticMethodID(jniEnv, class_calc, "calcKey", "()V");
if ( !clackeyID )
{
v7 = "method,failed";
goto LABEL_11;
}
_JNIEnv::CallStaticVoidMethod(jniEnv, class_calc, clackeyID);
keyID = ((int (__fastcall *)(JNIEnv *, jclass, const char *, const char *))(*env)->GetStaticFieldID)(
env,
class_calc,
"key",
"Ljava/lang/String;");
if ( !keyID )
_android_log_print(4, "log", "fid,failed");
key_ = (void *)((int (__fastcall *)(JNIEnv *, jclass, int))(*env)->GetStaticObjectField)(env, class_calc, keyID);// forceCallType
key = (*jniEnv)->GetStringUTFChars(jniEnv, key_, 0);// ,ZHVW^7c
while ( i < strlen(key) + 8 )
{
secondPart[i - 8] = password[i] - i;
++i;
}
v18 = 0;
rel = (unsigned int)strcmp(key, secondPart) <= 0;
}
}
return rel;
}
并在之后获得了key的内容。
public static String key;
public static void calcKey() {
key = new StringBuffer("c7^WVHZ,").reverse().toString();
}
}
获取flag
根据这三个判断,我们可以得到输入的字符串内容
s = "QflMn`fH,ZHVW^7c" flag = "" for idx,c in enumerate(s): flag +=chr(ord(c)+idx) print flag
结果如下
QgnPrelO4cRackEr
输入之后并不对。
再次分析
想到这里就要考虑下,程序是不是在哪里修改了对应的字符串。这里首先看一下seed。按 x 进行交叉引用,发现其在 _init_my 中使用了,如下
size_t _init_my()
{
size_t i; // r7
char *v1; // r4
size_t result; // r0
for ( i = 0; ; ++i )
{
v1 = seed[0];
result = strlen(seed[0]);
if ( i >= result )
break;
t[i] = v1[i] - 3;
}
seed[0] = t;
byte_4038 = 0;
return result;
}
所以最初程序对 seed 进行了修改。
再次获取flag
修改脚本如下
s = "QflMn`fH,ZHVW^7c" flag="" for i in xrange(0,8): flag = flag + chr(ord(s[i]) - 3 + i) for i in xrange(8,16): flag = flag + chr(ord(s[i]) + i) print "flag: " + flag
flag 如下
➜ 2015-海峡两岸一个APK,逆向试试吧 python exp.py NdkMobiL4cRackEr
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Hades:移动端静态分析框架
- Hades:移动端静态分析框架
- iOS 常用调试方法:静态分析
- iOS常用调试方法:静态分析
- Android静态分析之初级篇
- so静态分析进阶练习——一个CreakeMe的分析思路
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Microsoft Windows程序设计
佩措尔德 / 章立民 / 华中科技 / 2004-1 / 118.00元
Charles Petzold是全球最权威且知名的Windows程序设计专家,他将其最畅销Programming Microsoft Windows with C#一书加以改写,使之能完全适用于Visual Basic.NET的开发人员。这位畅销书的作家示范了如何使用Visual Basic.NET将Windows Forms的功能发挥到极致(Windows Forms是新一代的Windows程序......一起来看看 《Microsoft Windows程序设计》 这本书的介绍吧!