[RE4]P1
先查看Activity的包名
mainactivity源码
package com.example.admodel;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
/* loaded from: classes2.dex */
public class MainActivity extends AppCompatActivity {
private static String LOG_TAG = rootTest.class.getName();
private Button btnlogin;
private EditText lgpassword;
private String word = "ajksdkljahfklasjhlkdnalsnflkamskldjlkajhflkbalksndlkanfblasd";
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (isDeviceRooted()) {
Toast.makeText(this, "root", 1).show();
}
getSupportActionBar().setTitle("Crack Me!");
this.btnlogin = (Button) findViewById(R.id.btnlogin);
this.lgpassword = (EditText) findViewById(R.id.lgusername);
SPDataUtils.saveInfo(this, this.word);
final Userinfo user = SPDataUtils.getInfo(this);
user.getPassword();
final SM4Utils sm4 = new SM4Utils();
sm4.setSecretKey("JeF8U9wHFOMfs2Y8");
System.out.println("CBC模式");
sm4.setIv("UISwD9fW6cFh9SNS");
this.btnlogin.setOnClickListener(new View.OnClickListener() { // from class: com.example.admodel.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
String password = MainActivity.this.lgpassword.getText().toString();
String cipherText = sm4.encryptData_CBC(password);
if (TextUtils.equals(cipherText, user.getPassword())) {
Toast.makeText(MainActivity.this, "success", 1).show();
} else {
Toast.makeText(MainActivity.this, "Fail", 1).show();
}
}
});
}
private Activity getActivity() {
return null;
}
public static boolean isDeviceRooted() {
return rootTest.checkDeviceDebuggable() || rootTest.checkSuperuserApk() || rootTest.checkBusybox() || rootTest.checkAccessRootData() || rootTest.checkGetRootAuth();
}
}
很明显是sm4加密,给了key和iv
sm4.setSecretKey("JeF8U9wHFOMfs2Y8");
System.out.println("CBC模式");
sm4.setIv("UISwD9fW6cFh9SNS");
代码的主要逻辑是就是实现了一个登录的功能,并且判断密码
public void onClick(View v) {
String password = MainActivity.this.lgpassword.getText().toString();
String cipherText = sm4.encryptData_CBC(password);
if (TextUtils.equals(cipherText, user.getPassword())) {
Toast.makeText(MainActivity.this, "success", 1).show();
} else {
Toast.makeText(MainActivity.this, "Fail", 1).show();
}
将用户输入的密码sm4加密过后与正确密码的sm4值比较
双击就可以看到getPassword的内容也是真实的密码
kYEI25N30vqgSURbd5vEmz/yHt1SMH5YtoRKdXvuPtHrbuuaeOYgeyb1p0fgaq4D$
from gmssl.sm4 import CryptSM4, SM4_DECRYPT
import base64
def sm4_decrypt_cbc(key, iv, encrypted_data):
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key.encode('utf-8'), SM4_DECRYPT)
decrypted_data = crypt_sm4.crypt_cbc(iv.encode('utf-8'), encrypted_data)
return decrypted_data
# 示例用法
if __name__ == "__main__":
# 密钥 (16字节)
key = "JeF8U9wHFOMfs2Y8"
# 初始化向量 (IV) (16字节)
iv = "UISwD9fW6cFh9SNS"
# Base64解码加密后的数据
encrypted_data = base64.b64decode("kYEI25N30vqgSURbd5vEmz/yHt1SMH5YtoRKdXvuPtHrbuuaeOYgeyb1p0fgaq4D")
# 解密
decrypted_data = sm4_decrypt_cbc(key, iv, encrypted_data)
# 输出解密后的数据
print("Decrypted data:", decrypted_data.decode('utf-8'))
flag{9b7fe9357fe94008915ef1e5574bb0a5}
[RE4]P2
先看activity
package com.example.encode1;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
private Button bt;
private EditText input;
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.input = (EditText) findViewById(R.id.input);
Button button = (Button) findViewById(R.id.bt);
this.bt = button;
button.setOnClickListener(new View.OnClickListener() { // from class: com.example.encode1.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
if (new Check().check(MainActivity.this.input.getText().toString())) {
Toast.makeText(MainActivity.this, "success", 1).show();
} else {
Toast.makeText(MainActivity.this, "failed", 1).show();
}
}
});
}
}
主要实现了一个登录登录的功能,由check类中的check函数来判断正确
package com.example.encode1;
import android.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/* loaded from: classes3.dex */
public class Check {
public static String Encrypt(String sSrc) {
try {
byte[] raw = "OcpP0q9cSBfB6jky".getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec("hUgfkF6kl1iaohTf".getBytes());
cipher.init(1, skeySpec, iv);
byte[] encrypted = cipher.doFinal(sSrc.getBytes());
return Base64.encodeToString(encrypted, 2);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/* JADX INFO: Access modifiers changed from: package-private */
public boolean check(String input) {
if (Encrypt(input).equals("RiOMdgTJh9iZxh+SWt1srcjUVZo0RgZ9pdRTiA0YPjZHIPkrOxaQf10NKsjowDlY")) {
return true;
}
return false;
}
}
很明显是个AES加密,给了key和iv
from Crypto.Cipher import AES
import base64
def decrypt_aes_cbc(encrypted_data, key, iv):
# 初始化AES解密器
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
# Base64解码密文
encrypted_data_bytes = base64.b64decode(encrypted_data)
# 解密数据
decrypted_data = cipher.decrypt(encrypted_data_bytes)
# 去除填充
decrypted_data = unpad(decrypted_data)
return decrypted_data.decode('utf-8')
def unpad(data):
# 去除PKCS5Padding填充
padding_len = data[-1]
return data[:-padding_len]
if __name__ == "__main__":
# 密钥和IV必须与加密时使用的一致
key = "OcpP0q9cSBfB6jky"
iv = "hUgfkF6kl1iaohTf"
# 加密后的密文(Base64编码后的)
encrypted_data = "RiOMdgTJh9iZxh+SWt1srcjUVZo0RgZ9pdRTiA0YPjZHIPkrOxaQf10NKsjowDlY"
# 解密
decrypted_text = decrypt_aes_cbc(encrypted_data, key, iv)
print("Decrypted text:", decrypted_text)
flag{2c9c57cb54ce6d2fc5421eb65bf6adbf}
[RE4]P4
查看activity
很明显t是密文,然后t的值传给了n,然后调用了zzsence.a
{127, 41, 32, -23, 53, -113, -59, 154, 5, 16, 52, 188, 91, 150, 43, 163, 140, 170, 158, 36, 145, 140, 211, 17, 18, 79, 200, 177, 122, 78, 219, 247}
很明显是AES加密,但是没用key和 iv
调用了loadlibrary
key和iv是由native层生成的
so代码(不会逆)
使用jeb动态调试 首先要查找入口包名,其实刚才在manifest已经知道了入口 如果包很多的话其实不容器看出来,可以用aapt获取入口包名 用法
aatp dump badging apk的名字
可以看到获取的入口包名和manifest里面是一样的
或者也可以使用
adb shell dumpsys activity top
查看当前位于前台的应用程序的信息(需要启动应用)
开启APK调试模式
adb shell am start -D -n 包名/类名
这里的包名/类名要用adb的写法来 com.zhuotong.easyctf2/.MainActivity
(高版本的安卓系统可能不能直接debug)
进去debug模式后用jeb attach上这个包
应用已经进入主窗口
调用生成ket和iv的地方
下断点
获得key值
[-95, 109, 22, -2, 26, -6, 48, 95, -41, 126, 94, -98, -20, 107, -97, -35]
由于iv不是以赋值的形式生成,需要进入函数内部来查看
步入一个函数看看,iv还没被生成
步入最后一步,可以看到iv已经生成了
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
解密
key
[-95, 109, 22, -2, 26, -6, 48, 95, -41, 126, 94, -98, -20, 107, -97, -35]
iv
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
密文
[127, 41, 32, -23, 53, -113, -59, 154, 5, 16, 52, 188, 91, 150, 43, 163, 140, 170, 158, 36, 145, 140, 211, 17, 18, 79, 200, 177, 122, 78, 219, 247]
from Crypto.Cipher import AES
import base64
def decrypt_aes_cbc(ciphertext, key, iv):
# 将 key, iv, 和 ciphertext 转换为字节数组
key_bytes = bytes([(i + 256) % 256 for i in key])
iv_bytes = bytes([(i + 256) % 256 for i in iv])
ciphertext_bytes = bytes([(i + 256) % 256 for i in ciphertext])
# 创建AES解密器
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
# 解密数据
decrypted_data = cipher.decrypt(ciphertext_bytes)
# 移除填充
decrypted_data = unpad(decrypted_data)
return decrypted_data.decode('utf-8')
def unpad(data):
# 去除PKCS5Padding填充
padding_len = data[-1]
return data[:-padding_len]
if __name__ == "__main__":
key = [-95, 109, 22, -2, 26, -6, 48, 95, -41, 126, 94, -98, -20, 107, -97, -35]
iv = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
ciphertext = [127, 41, 32, -23, 53, -113, -59, 154, 5, 16, 52, 188, 91, 150, 43, 163, 140, 170, 158, 36, 145, 140, 211, 17, 18, 79, 200, 177, 122, 78, 219, 247]
# 执行解密
decrypted_text = decrypt_aes_cbc(ciphertext, key, iv)
print("Decrypted text:", decrypted_text)
[RE4]P5
代码给了很多,直接看flag逻辑部分,代码实现了一个计时器的功能,当时间为66.666s时输出flag
生成flag的代码很长很复杂,但是代码都是java层的,也可以不用frida执行模拟执行
使用frida hook 执行这个函数
setImmediate(function () { //立即执行脚本
Java.perform(function () {
Java.scheduleOnMainThread(function () {
var main = Java.use('com.moible.r15.main');//获取目标类
var mains = main.$new();//创建一个新的实例
var result = mains.getit("66.666s");//调用目标函数
console.log("Result: " + result);//打印结果
});
});
});
这里面有个坑,可能是frida版本的原因,代码不是运行在主线程,会抛出RuntimeException
要解决这个问题,需要确保在主线程(UI线程)上执行代码。Frida 提供了
Java.scheduleOnMainThread
方法,可以确保代码在主线程上运行。
[RE4]P6
引入了一个so
主要判断逻辑
调用的是native层的代码
直接可以看到hello函数,很明显是个换表base64
主要逻辑是将输入的内容base64编码后再与加密过的flag比较,我们只需要找到base64所用的表即可解密
[RE4]P7
有三个反调试函数
但都只是在java层上反调试,所以直接用frida hook掉返回值即可
setImmediate(function () {
Java.perform(function () {
Java.scheduleOnMainThread(function () {
let c = Java.use("sg.vantagepoint.a.c");
c["a"].implementation = function () {
return false;
};
c["b"].implementation = function () {
return false;
};
c["c"].implementation = function () {
return false;
};
});
});
});
可以看到提示已经消失了,而且程序没有退出
flag判断逻辑
很明显是AES加密,第一个是key,第二个是密文
直接解密
Comments | NOTHING