学习目标:了解一些app的防护措施,能对自己写的app做一些简单防护
反编译一个app时搜索不到关键字
- HTML5的App
- 字符串被加密了
- 反射调用相关类
- 动态加载dex
- 热修复
简单的字符串加密的实现
dex的字符串加密可以用代码自动实现,需要使用到dexlib2这个库
若是不加任何防护,在登陆操作时,我们将密码发送给服务器,攻击者再抓包后直接反编译然后搜索password便可以定位到关键代码
package com.example.encrypt;
import android.os.Build; import android.os.Bundle;
import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat;
import java.security.MessageDigest; import android.util.Base64; import android.util.Log;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); String password = "a12345678"; try{ MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(password.getBytes()); byte[] digest = md5.digest(); HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("password", Base64.encodeToString(digest, 0)); Log.d("hyq", hashMap.toString()); }catch (Exception e){ e.printStackTrace(); } } }
|
反编译后直接查找,轻轻松松就找到了

我么便可以使用一些简单的方式,将password字段进行加密
先将password进行一下base64编码得到cGFzc3dvcmQ=,接下来我们使用这个字段代替password
package com.example.encrypt;
import android.os.Bundle;
import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat;
import java.security.MessageDigest; import android.util.Base64; import android.util.Log;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); String password = "a12345678"; try{ MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(password.getBytes()); byte[] digest = md5.digest(); HashMap<String, String> hashMap = new HashMap<>(); hashMap.put(decrypt("cGFzc3dvcmQ="), Base64.encodeToString(digest, 0)); Log.d("hyq", hashMap.toString()); }catch (Exception e){ e.printStackTrace(); }
} private String decrypt(String str){ return new String(Base64.decode(str, 0)); } }
|

这样就不能直接搜到了
注意下细节:System.out.println(Base64.decode(str, 0));和 System.out.println(Base64.decode(str, 0).toString());输出的是哈希值不是字节数组的内容
使用反射进一步加密
package com.example.encrypt;
import android.os.Bundle;
import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat;
import java.lang.reflect.Method; import java.security.MessageDigest; import android.util.Base64; import android.util.Log;
import java.util.HashMap; import java.util.Objects;
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); String password = "12345678"; try{
Class<?> a = Class.forName(d("amF2YS5zZWN1cml0eS5NZXNzYWdlRGlnZXN0")); Method b = a.getMethod(d("Z2V0SW5zdGFuY2U="), String.class); Object c = b.invoke(null, d("TUQ1"));
Method e = a.getMethod(d("dXBkYXRl"), byte[].class); e.invoke(c, password.getBytes());
Method f= a.getMethod(d("ZGlnZXN0")); byte[] cipherBytes = (byte[]) f.invoke(c);
HashMap<String, String> hashMap = new HashMap<>(); hashMap.put(d("cGFzc3dvcmQ="), Base64.encodeToString(cipherBytes,0)); Log.d("hyq", hashMap.toString()); }catch (Exception e){ e.printStackTrace(); }
} private String d(String str){ return new String(Base64.decode(str, 0)); } }
|
反编译出来长这样

package com.example.fctf;
import android.os.Bundle; import android.util.Base64; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast;
import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat;
import com.example.fctf.databinding.ActivityMainBinding;
import org.w3c.dom.Text;
import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.MessageDigest; import java.security.spec.KeySpec; import java.util.Objects;
import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec;
import okio.ByteString;
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding;
private int num = 1; private static String key0 = "82305002"; private static String iv0 = "82505002"; public final int getNum(){ return this.num; } public void setNum(int i){ this.num = i; }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); final TextView textView = binding.textView; Button button = binding.button; button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { MainActivity.a(MainActivity.this, textView, button); } catch (IllegalBlockSizeException e) { throw new RuntimeException(e); } catch (BadPaddingException e) { throw new RuntimeException(e); } } }); try { System.out.println(decrypt("whyysqwmstoryhzcontinues")); } catch (IllegalBlockSizeException e) { throw new RuntimeException(e); } catch (BadPaddingException e) { throw new RuntimeException(e); } } public static final void a(MainActivity this$0, TextView tv, Button button) throws IllegalBlockSizeException, BadPaddingException { MainActivity mainActivity = this$0; tv.setText(String.valueOf(this$0.num)); if(this$0.check() == 20220422){ Toast.makeText(mainActivity, "Congratuations!!!", Toast.LENGTH_SHORT).show(); tv.setText("flag{" + this$0.decrypt("whyysqwmstoryhzcontinues") + "}"); } } public final int check(){ int i = this.num + 1; this.num = i; return i; } public final String decrypt(String str) throws IllegalBlockSizeException, BadPaddingException { Object ddd = null; byte[] bytes =null; try { Class<?> a = Class.forName(d("amF2YS5zZWN1cml0eS5NZXNzYWdlRGlnZXN0")); Method b = a.getMethod(d("Z2V0SW5zdGFuY2U="), String.class); Object c = b.invoke(null, d("TUQ1")); Method e = a.getMethod(d("dXBkYXRl"), byte[].class); e.invoke(c, key0.getBytes()); Method f = a.getMethod(d("ZGlnZXN0")); byte[] k = (byte[]) f.invoke(c);
Class<?> aa= Class.forName(d("amF2YXguY3J5cHRvLnNwZWMuREVTS2V5U3BlYw==")); Constructor<?> desKeySpecConstructor = aa.getConstructor(byte[].class); Object dsk = desKeySpecConstructor.newInstance(k);
Class<?> bb = Class.forName(d("amF2YXguY3J5cHRvLlNlY3JldEtleUZhY3Rvcnk=")); Method cc = bb.getMethod(d("Z2V0SW5zdGFuY2U="), String.class); Object keyFactory = cc.invoke(null, "DES");
Method dd = bb.getMethod(d("Z2VuZXJhdGVTZWNyZXQ="), KeySpec.class); Object key = dd.invoke(keyFactory, dsk);
Class<?> ee = Class.forName(d("amF2YXguY3J5cHRvLnNwZWMuSXZQYXJhbWV0ZXJTcGVj")); Constructor<?> ff = ee.getConstructor(byte[].class); Object iv =ff.newInstance(iv0.getBytes());
Class<?> gg = Class.forName(d("amF2YXguY3J5cHRvLkNpcGhlcg==")); Method hh = gg.getMethod(d("Z2V0SW5zdGFuY2U="), String.class); ddd = hh.invoke(null, d("REVTL0NCQy9QS0NTNVBhZGRpbmc="));
Method ii = gg.getMethod(d("aW5pdA=="), int.class, java.security.Key.class, java.security.spec.AlgorithmParameterSpec.class); ii.invoke(ddd, Cipher.ENCRYPT_MODE, key, iv);
Method jj = gg.getMethod(d("ZG9GaW5hbA=="), byte[].class); bytes = (byte[]) jj.invoke(ddd, str.getBytes());
} catch (Exception e) { e.printStackTrace(); } ByteString byteString = ByteString.of(bytes); return byteString.hex(); }
private String d(String str){ return new String(Base64.decode(str, 0)); }
}
|