zoukankan      html  css  js  c++  java
  • Android 逆向入门

    Android Re 入门

    必备工具:

    IDA : 反编译.so文件

    AndroidKiller: 反编译apk文件及再次编译为apk

    jd-gui : 将.jar文件反编译为java代码

    dex2jar: 反编译.dex文件

    apktool: 能够反编译及回编译apk

    夜神模拟器: 运行apk文件

    例题 1 [easy-so]

    下载

    来源:攻防世界

    使用夜神模拟器运行该apk文件, 发现需要输入flag

    zip解压apk文件, 用 dex2jar反编译 classes.dex文件得到classes-dex2jar.jar,

    d2j-dex2jar.bat classes.dex
    

    再用jd-gui打开classes-dex2jar.jar反编译为java代码.

    public class MainActivity extends AppCompatActivity {
      protected void onCreate(Bundle paramBundle) {
        super.onCreate(paramBundle);
        setContentView(2131296283);
        ((Button)findViewById(2131165218)).setOnClickListener(new View.OnClickListener() {
              public void onClick(View param1View) {
                if (cyberpeace.CheckString(((EditText)MainActivity.this.findViewById(2131165233)).getText().toString()) == 1) {
                  Toast.makeText((Context)MainActivity.this, ", 1).show();
                  return;
                } 
                Toast.makeText((Context)MainActivity.this, ", 1).show();
              }
            });
      }
    }
    

    在这里可以发现, cyberpeace加载了'cyberpeace'动态库, CheckString函数是native层的, 这个函数的实现就在libcyberpeace.so文件中

    package com.testjava.jack.pingan2;
    
    public class cyberpeace {
      static {
        System.loadLibrary("cyberpeace");
      }
      
      public static native int CheckString(String paramString);
    }
    
    

    使用ida打开lib/x86/libcyberpeace.so文件, 找到 _BOOL4 __cdecl Java_com_testjava_jack_pingan2_cyberpeace_CheckString(int a1, int a2, int a3)函数, 该函数实现如下:

    _BOOL4 __cdecl Java_com_testjava_jack_pingan2_cyberpeace_CheckString(int a1, int a2, int a3)
    {
      const char *get_str; // ST1C_4
      size_t len; // edi
      char *str; // esi
      size_t i; // edi
      char v7; // al
      char v8; // al
      size_t v9; // edi
      char v10; // al
    
      get_str = (const char *)(*(int (__cdecl **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0); // 从java层获取所输入的字符串
      len = strlen(get_str);
      str = (char *)malloc(len + 1);
      memset(&str[len], 0, len != -1);
      memcpy(str, get_str, len);
      if ( strlen(str) >= 2 ) // 加密1
      {
        i = 0;
        do
        {
          v7 = str[i];
          str[i] = str[i + 16];
          str[i++ + 16] = v7;
        }
        while ( i < strlen(str) >> 1 );
      }
      // 加密2
      v8 = *str;
      if ( *str )
      {
        *str = str[1];
        str[1] = v8;
        if ( strlen(str) >= 3 )
        {
          v9 = 2;
          do
          {
            v10 = str[v9];
            str[v9] = str[v9 + 1];
            str[v9 + 1] = v10;
            v9 += 2;
          }
          while ( v9 < strlen(str) );
        }
      }
      return strcmp(str, "f72c5a36569418a20907b55be5bf95ad") == 0; // 与字符串作比较
    }
    

    从以上发现, 对我们所输入的字符串进行了加密, 然后再与f72c5a36569418a20907b55be5bf95ad进行比较. 现在只需逆一下以上代码即可得到flag, exp代码如下

    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
    	char str[] = "f72c5a36569418a20907b55be5bf95ad";
    	int i, v7, v8, v9, v10;
    	v8 = *str;
    	if ( *str ) {
    		*str = str[1];
    		str[1] = v8;
    		if ( strlen(str) >= 3 ) {
          		v9 = 2;
          		do {
            		v10 = str[v9];
            		str[v9] = str[v9 + 1];
            		str[v9 + 1] = v10;
            		v9 += 2;
          		} while ( v9 < strlen(str) );
    		}
    	}
    	
    	// 交换 
    	if ( strlen(str) >= 2 )	{
       	 i = 0;
        	do {
          		v7 = str[i];
          		str[i] = str[i + 16];
         		str[i++ + 16] = v7;
       	 	} while ( i < strlen(str) >> 1 );
    	}
    	printf("%s", str);
      
    	return 0;
    }
    
    

    运行以上代码即可获取flag

    例题 2 [app2]

    下载

    来源:攻防世界

    对输入的账号和密码在SecondActivity类中进行加密判断, 而加密调用了native层的加密函数

    protected void onCreate(Bundle paramBundle) {
        super.onCreate(paramBundle);
        setContentView(2130903041);
        Intent intent = getIntent();
        String str1 = intent.getStringExtra("ili");
        String str2 = intent.getStringExtra("lil");
        if (Encryto.doRawData(this, str1 + str2).equals("VEIzd/V2UPYNdn/bxH3Xig==")) {
          intent.setAction("android.test.action.MoniterInstallService");
          intent.setClass((Context)this, MoniterInstallService.class);
          intent.putExtra("company", "tencent");
          intent.putExtra("name", "hacker");
          intent.putExtra("age", 18);
          startActivity(intent);
          startService(intent);
        } 
        SharedPreferences.Editor editor = getSharedPreferences("test", 0).edit();
        editor.putString("ilil", str1);
        editor.putString("lili", str2);
        editor.commit();
      }
    

    IDA反编译doRawData函数, 因为a为对象, 选择a按下y 键 然后输入 JNIEnv*就可以显示对象的函数调用, 如下

    int __cdecl doRawData(JNIEnv *a1, int a2, int a3, int a4)
    {
      char *v4; // esi
      const char *v5; // ST10_4
      int result; // eax
      char *v7; // esi
      jstring (*v8)(JNIEnv *, const jchar *, jsize); // ST10_4
      size_t v9; // eax
      int v10; // [esp+4h] [ebp-28h]
      int v11; // [esp+8h] [ebp-24h]
      int v12; // [esp+Ch] [ebp-20h]
      int v13; // [esp+10h] [ebp-1Ch]
      char v14; // [esp+14h] [ebp-18h]
      unsigned int v15; // [esp+18h] [ebp-14h]
    
      v15 = __readgsdword(0x14u);
      if ( checkSignature((int)a1, a2, a3) == 1 )
      {
        v14 = 0;
        v13 = 0x3D3D7965;
        v12 = 0x6B747365;
        v11 = 0x74617369;
        v10 = 0x73696874;
        v4 = (char *)(*a1)->GetStringUTFChars(a1, (jstring)a4, 0);
        v5 = (const char *)AES_128_ECB_PKCS5Padding_Encrypt(v4, (int)&v10);
        (*a1)->ReleaseStringUTFChars(a1, (jstring)a4, v4);
        result = (int)(*a1)->NewStringUTF(a1, v5);
      }
      else
      {
        v7 = UNSIGNATURE[0];
        v8 = (*a1)->NewString;
        v9 = strlen(UNSIGNATURE[0]);
        result = (int)v8(a1, (const jchar *)v7, v9);
      }
      return result;
    }
    

    可以发现, 加密方式为aes加密, key 为 v10中的内容.为thisisatestkey==

    对VEIzd/V2UPYNdn/bxH3Xig== 解密为aimagetencent, 发现提交flag错误, 重新找另一个字符串,在FileDataActivity类中找到如下.

    public class FileDataActivity extends a {
      private TextView c;
      
      protected void onCreate(Bundle paramBundle) {
        super.onCreate(paramBundle);
        setContentView(2130903042);
        this.c = (TextView)findViewById(2131165184);
        this.c.setText(Encryto.decode(this, "9YuQ2dk8CSaCe7DTAmaqAA=="));
      }
    }
    

    调用了decode函数, 而decode函数与doRawData实现一样, 直接与之前一样的AES ecb解密, 得到flag

    Ph0en1x-100

    下载

    来源:攻防世界

    程序流程, 输入flag

    jd-gui反编译如下:

     public void onGoClick(View paramView)
      {
        paramView = this.etFlag.getText().toString();
        if (getSecret(getFlag()).equals(getSecret(encrypt(paramView)))) {
          Toast.makeText(this, "Success", 1).show();
        }
        for (;;)
        {
          return;
          Toast.makeText(this, "Failed", 1).show();
        }
      }
    

    getFlag函数与encrypt函数是native层

      static
      {
        System.loadLibrary("phcm");
      }
      
      public native String encrypt(String paramString);
      
      public native String getFlag();
    

    反编译libphcm.so文件

    int __cdecl Java_com_ph0en1x_android_1crackme_MainActivity_encrypt(JNIEnv *a1, int a2, int a3)
    {
      size_t i; // esi
      const char *s; // edi
    
      i = 0;
      for ( s = (*a1)->GetStringUTFChars(a1, (jstring)a3, 0); i < strlen(s); --s[i++] )
        ;
      return (*a1)->NewStringUTF(a1, s);
    }
    

    以上加密就是对字符串中的每个字符-1

    int __cdecl Java_com_ph0en1x_android_1crackme_MainActivity_getFlag(JNIEnv *a1)
    {
      signed int v1; // esi
      char *v2; // edi
      char v3; // al
      int result; // eax
      int v5; // [esp+26h] [ebp-46h]
      int v6; // [esp+2Ah] [ebp-42h]
      int v7; // [esp+2Eh] [ebp-3Eh]
      __int16 v8; // [esp+32h] [ebp-3Ah]
      int v9; // [esp+34h] [ebp-38h]
      int v10; // [esp+38h] [ebp-34h]
      int v11; // [esp+3Ch] [ebp-30h]
      int v12; // [esp+40h] [ebp-2Ch]
      int v13; // [esp+44h] [ebp-28h]
      int v14; // [esp+48h] [ebp-24h]
      int v15; // [esp+4Ch] [ebp-20h]
      int v16; // [esp+50h] [ebp-1Ch]
      int v17; // [esp+54h] [ebp-18h]
      int v18; // [esp+58h] [ebp-14h]
      unsigned int v19; // [esp+5Ch] [ebp-10h]
    
      v1 = 38;
      v2 = (char *)&v18 + 2;
      v9 = 1279407662;
      v10 = 987807583;
      v19 = __readgsdword(0x14u);
      v11 = 1663091624;
      v12 = 482391945;
      v13 = 683820061;
      v14 = 235072895;
      v15 = 2559534685;
      v16 = 382777269;
      v17 = 4227367757;
      v18 = 4670209;
      v5 = 1819043144;
      v6 = 1750081647;
      v7 = 829318448;
      v8 = 120;
      do
      {
        v3 = *v2--;
        v2[1] = (*((_BYTE *)&v5 + v1-- % 13) ^ (v3 + 1 - *v2)) - 1;
      }
      while ( v1 );
      LOBYTE(v9) = (v9 ^ 0x48) - 1;
      result = (int)(*a1)->NewStringUTF(a1, (const char *)&v9);
      if ( __readgsdword(0x14u) != v19 )
        sub_4B0();
      return result;
    }
    

    这个加密稍微有点复杂. 若想获取flag, 先对上面这给逆出来, 在对encrypt加密加密函数给再逆出来即可获得flag, 但是, 我写了一个c脚本, 上面这个有问题, 主要是LOBYTE(v9) = (v9 ^ 0x48) - 1;这个语句不好写.换另一种思路.动态调试(瞎弄半天, 啥也没弄出来), 再换另一种, 就是使用Android killer修改smali源码, 将其getFlag函数的字符串给打印出来, 只需将打印失败逻辑添加一下getFlag函数, 将getFlag字符串覆盖为打印失败的字符串, 复制上面调用getFlag的即可, 再更变一下变量, 如下

     .line 37
        :cond_0
        const-string v1, "Failed"
        
        invoke-virtual {p0}, Lcom/ph0en1x/android_crackme/MainActivity;->getFlag()Ljava/lang/String;
    
        move-result-object v1   // getFlag()函数的返回值,(字符串)
        
        invoke-static {p0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
    
        move-result-object v1
    
        invoke-virtual {v1}, Landroid/widget/Toast;->show()V
    

    然后点击Android->编译, 即可再次编译为apk文件, 安装再nox中运行, 随便输入就会出现ekfz@q2^x/t^fn0mF^6/^rbqanqntfg^E`hq|

    再次让每个字符+1就可得到flag

    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
    	char flag[] = "ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|";
    	for(int i = 0; i < strlen(flag); ++i) {
    		putchar(++flag[i]);
    	}
    
    	return 0;
    }
    

    app1

     public void onClick(View paramView)
      {
        for (;;)
        {
          try
          {
            str = this.this$0.text.getText().toString();
            PackageInfo localPackageInfo = this.this$0.getPackageManager().getPackageInfo("com.example.yaphetshan.tencentgreat", 16384);
            paramView = localPackageInfo.versionName;
            int i = localPackageInfo.versionCode;
            j = 0;
            if ((j >= str.length()) || (j >= paramView.length())) {
              continue;
            }
            if (str.charAt(j) != (paramView.charAt(j) ^ i))
            {
              Toast.makeText(this.this$0, "再接再励~", 1).show();
              return;
            }
          }
          catch (PackageManager.NameNotFoundException paramView)
          {
            String str;
            int j;
            Toast.makeText(this.this$0, "不要玩小聪明", 1).show();
            continue;
          }
          j++;
          continue;
          if (str.length() != paramView.length()) {
            continue;
          }
          Toast.makeText(this.this$0, "恭喜开启芝麻之门", 1).show();
        }
      }
    

    在BuildConfig class中找到versionName和versionCode

    package com.example.yaphetshan.tencentgreat;
    
    public final class BuildConfig
    {
      public static final String APPLICATION_ID = "com.example.yaphetshan.tencentgreat";
      public static final String BUILD_TYPE = "debug";
      public static final boolean DEBUG = Boolean.parseBoolean("true");
      public static final String FLAVOR = "";
      public static final int VERSION_CODE = 15;
      public static final String VERSION_NAME = "X<cP[?PHNB<P?aj";
    }
    
    

    解密脚本

    #include <stdio.h>
    #include <string.h>
    int main(void) {
    	char name[] = "X<cP[?PHNB<P?aj";
    	for(int i = 0; i < strlen(name); ++i)
    		putchar(name[i] ^15);
    
    	return 0;
    }
    
    
  • 相关阅读:
    到底有多少种智能指针(smart pointer)
    QSettings提供了非常方便的注册表读写
    阿里腾讯亿元级投资版图:阿里爱买超市,腾讯娱乐至上(恐怖)
    gcc安装教程(学习Linux编程只需安装cygwin)
    苹果抛弃的芯片公司Imagination被中资49亿溢价收购
    NET Core2
    HelloWorld RabbitMQ
    redis 集群
    kafka
    Impala简介PB级大数据实时查询分析引擎
  • 原文地址:https://www.cnblogs.com/lyxf/p/13617228.html
Copyright © 2011-2022 走看看