Android逆向:看雪20210501W2练习题
发布日期:2021-06-29 11:29:30 浏览次数:3 分类:技术文章

本文共 21408 字,大约阅读时间需要 71 分钟。

案例与目标

总体:输入flag,屏幕上出现“恭喜你!”则为正确flag。要求得到正确flag
目标1:提示五位数:比如:24680
目标2:在没有提示五位数的情况下解开。
在这里插入图片描述

目标1

使用jadx分析【提交】点击事件,发现是把username放入Native层变换后与【57fdeca2cac0509b2e9e5c52a5b573c1608a33ac1ffb9e8210d2e129557e7f1b】作比较。

在这里插入图片描述
IDA查看导出函数,没有 Sign() 踪迹,应该是动态注册了,从 registerMethods() 里寻找,尽管这里有一部分干扰代码,还做了ptrace反调试,但并不影响定位函数。
在这里插入图片描述
发现 Sign() 动态注册成了 f**k()
在这里插入图片描述
**f**k()**函数伪代码如下,首先它会采取一定的反调试措施,之后再进行加密变换。

jstring __fastcall fuck(JNIEnv *env, jclass jcls, jstring str_){
jstring v4; // [sp+14h] [bp-C4h] _jmethodID *methodID; // [sp+18h] [bp-C0h] jstring v6; // [sp+28h] [bp-B0h] int i; // [sp+30h] [bp-A8h] jbyte *v8; // [sp+34h] [bp-A4h] _jbyteArray *array; // [sp+38h] [bp-A0h] jobject v10; // [sp+3Ch] [bp-9Ch] _jmethodID *v11; // [sp+40h] [bp-98h] _jclass *v12; // [sp+44h] [bp-94h] _jmethodID *v13; // [sp+48h] [bp-90h] _jobject *v14; // [sp+4Ch] [bp-8Ch] _jclass *v15; // [sp+54h] [bp-84h] _jobject *obj; // [sp+58h] [bp-80h] _jstring *v17; // [sp+5Ch] [bp-7Ch] _jfieldID *fieldID; // [sp+60h] [bp-78h] _jclass *clazz; // [sp+64h] [bp-74h] const char *v20; // [sp+68h] [bp-70h] unsigned __int8 *src; // [sp+6Ch] [bp-6Ch] char *v25; // [sp+8Ch] [bp-4Ch] char *v26; // [sp+A4h] [bp-34h] _WORD v27[4]; // [sp+ABh] [bp-2Dh] BYREF if ( !str_ ) return 0; src = _JNIEnv::GetStringUTFChars(env, str_, 0); v20 = "REAL"; clazz = _JNIEnv::FindClass(env, "android/os/Build"); fieldID = _JNIEnv::GetStaticFieldID(env, clazz, "FINGERPRINT", "Ljava/lang/String;"); v17 = _JNIEnv::GetStaticObjectField(env, clazz, fieldID); if ( function_check_tracerPID() // TracerPID反调试 || system_getproperty_check() // 设备反调试 || (v25 = _JNIEnv::GetStringUTFChars(env, v17, 0), strstr(v25, "aosp")) ) // 系统版本反调试 {
v20 = "FAKE"; } strcat(src, v20); obj = j_o0OoOOOO(env, src); v15 = _JNIEnv::FindClass(env, "java/security/MessageDigest"); methodID = _JNIEnv::GetStaticMethodID(env, v15, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;"); v4 = j_o0OoOOOO(env, "MD5"); v14 = _JNIEnv::CallStaticObjectMethod(env, v15, methodID, v4); v13 = _JNIEnv::GetMethodID(env, v15, "digest", "([B)[B"); v12 = _JNIEnv::FindClass(env, "java/lang/String"); v11 = _JNIEnv::GetMethodID(env, v12, "getBytes", "()[B"); v10 = _JNIEnv::CallObjectMethod(env, obj, v11); array = _JNIEnv::CallObjectMethod(env, v14, v13, v10); v8 = _JNIEnv::GetByteArrayElements(env, array, 0); for ( i = 0; i <= 15; ++i ) sprintf(&v27[i], "%02x", v8[i]); v26 = j_ll11l1l1ll(src); strcat(v26, v27); v6 = j_o0OoOOOO(env, v26); _JNIEnv::ReleaseStringUTFChars(env, str_, src); free(v26); return v6;}

function_check_tracerPID() 伪代码如下,通过 /proc/{PID}/status 中的 TracerPid 来检测是否被调试(返回0说明没有被调试,否则返回调试进程PID)。

int function_check_tracerPID(){
std::string::size_type v0; // r0 const char *v1; // r0 int v3; // [sp+8h] [bp-148h] unsigned __int8 *v4; // [sp+Ch] [bp-144h] const char *v5; // [sp+10h] [bp-140h] std::istream *v6; // [sp+28h] [bp-128h] const char *v7; // [sp+30h] [bp-120h] std::string::size_type __pos1; // [sp+34h] [bp-11Ch] __pid_t val; // [sp+4Ch] [bp-104h] char v10; // [sp+53h] [bp-FDh] std::string str; // [sp+54h] [bp-FCh] BYREF std::string v12; // [sp+60h] [bp-F0h] BYREF std::string __str; // [sp+6Ch] [bp-E4h] BYREF std::string v14; // [sp+78h] [bp-D8h] BYREF std::string __s; // [sp+84h] [bp-CCh] BYREF std::ifstream v16; // [sp+90h] [bp-C0h] BYREF v10 = 0; val = getpid(); std::string::basic_string
(&__s, "/proc/pid/status"); std::string::basic_string(&v14); __pos1 = std::string::find(&__s, (const std::string::value_type *)"pid", 0); std::to_string(&__str, val); std::string::replace(&__s, __pos1, 3u, &__str); std::string::~string(&__str); v7 = (const char *)std::string::c_str(&__s); _android_log_print(6, "roysuejni", "replace file name => %s", v7); std::ifstream::basic_ifstream(&v16, &__s, 8u); if ( std::ifstream::is_open(&v16) << 31 ) {
while ( 1 ) {
v6 = std::getline
,std::allocator
>((std::istream *)&v16, &v14); if ( !(std::ios::operator bool((const std::ios *)((char *)v6 + *((_DWORD *)v6->_vptr$basic_istream - 3))) << 31) ) break; if ( !std::string::find(&v14, (const std::string::value_type *)"TracerPid", 0) ) {
v0 = std::string::find(&v14, (const std::string::value_type *)":", 0); std::string::substr(&v12, &v14, v0 + 1, 0xFFFFFFFF); std::string::operator=(&v14, &v12); std::string::~string(&v12); v5 = (const char *)std::string::c_str(&v14); _android_log_print(6, "roysuejni", "file line => %s", v5); v4 = (unsigned __int8 *)std::string::c_str(&v14); std::string::basic_string
(&str, v4); v3 = std::stoi(&str, 0, 10); std::string::~string(&str); if ( v3 ) {
v1 = (const char *)std::string::c_str(&v14); _android_log_print(6, "roysuejni", "trace pid => %s, i want to exit.", v1); v10 = 1; break; } } } std::ifstream::close(&v16); } std::ifstream::~ifstream(&v16); std::string::~string(&v14); std::string::~string(&__s); return v10 & 1;}

system_getproperty_check() 伪代码如下,检测是否是逆向人员常用的Google手机。

int system_getproperty_check(){
const char *v1; // [sp+Ch] [bp-1F4h] const char *v2; // [sp+18h] [bp-1E8h] size_t maxlen; // [sp+1Ch] [bp-1E4h] char v4; // [sp+27h] [bp-1D9h] char *v5; // [sp+28h] [bp-1D8h] int v6; // [sp+2Ch] [bp-1D4h] int v7; // [sp+30h] [bp-1D0h] int v8; // [sp+34h] [bp-1CCh] char v9[156]; // [sp+58h] [bp-1A8h] BYREF char v10[256]; // [sp+F4h] [bp-10Ch] BYREF v8 = _system_property_get("ro.product.manufacturer", v10); v7 = _system_property_get("ro.product.model", v9); v6 = v8 + v7; v5 = 0; if ( v8 + v7 >= 1 ) {
v5 = (char *)malloc(v6 + 2); maxlen = v6 + 2; if ( v8 < 1 ) v2 = &byte_753C3; else v2 = v10; if ( v7 < 1 ) snprintf(v5, maxlen, "%s/%s", v2, &byte_753C3); else snprintf(v5, maxlen, "%s/%s", v2, v9); } v4 = 0; if ( strstr(v5, "Google") || strstr(v5, "Nexus") ) v4 = 1; if ( v5 ) v1 = v5; else v1 = "N/A"; _android_log_print(6, "roysuejni", "[roysue device]: [%s] result is => %d\n", v1, v4 & 1); return v4 & 1;}

最后一道反调试通过设备信息检测是否是逆向人员常用的Google系统。

src = (unsigned __int8 *)_JNIEnv::GetStringUTFChars(env, str_, 0);v20 = "REAL";clazz = _JNIEnv::FindClass(env, "android/os/Build");fieldID = _JNIEnv::GetStaticFieldID(env, clazz, "FINGERPRINT", "Ljava/lang/String;");v17 = (_jstring *)_JNIEnv::GetStaticObjectField(env, clazz, fieldID);...省略...(v25 = (char *)_JNIEnv::GetStringUTFChars(env, v17, 0), strstr(v25, "aosp"))

其加密部分除了明显的MD5,还有一个 j_ll11l1l1ll() 函数,根据其内部特征暂且认为这是常见的AES加密。

unsigned __int8 *__fastcall ll11l1l1ll(const unsigned __int8 *input){
unsigned __int8 *v2; // [sp+8h] [bp-30h] uint8_t *output; // [sp+Ch] [bp-2Ch] size_t byte_count; // [sp+10h] [bp-28h] uint8_t *iv; // [sp+18h] [bp-20h] uint8_t *key; // [sp+1Ch] [bp-1Ch] char *v8; // [sp+2Ch] [bp-Ch] key = (uint8_t *)ll11lll1l1(); iv = (uint8_t *)ll11l1l1l1(); v8 = (char *)ll11l1l11l(input); byte_count = strlen(v8); output = (uint8_t *)malloc(byte_count); j_qpppqp(output, (uint8_t *)v8, byte_count, key, iv); v2 = j_bbddbbdbb(output, byte_count); free(v8); free(output); free(key); free(iv); return v2;}

综上推测代码流程如图。

在这里插入图片描述

Unidbg暴力破解

由于题目给出了答案是“5位数”这一条件,那就可以穷举10000~99999的整数作为字符串传入进行尝试,直到试出正确答案。Unidbg模拟运行不用担心 TracerPid 问题,只需要hook _system_property_get() 返回值,并补充 android.os.Build.FINGERPRINT 环境即可通过反调试。

package com.zyc.demo;import com.github.unidbg.AndroidEmulator;import com.github.unidbg.arm.backend.DynarmicFactory;import com.github.unidbg.linux.android.AndroidEmulatorBuilder;import com.github.unidbg.linux.android.AndroidResolver;import com.github.unidbg.linux.android.SystemPropertyHook;import com.github.unidbg.linux.android.SystemPropertyProvider;import com.github.unidbg.linux.android.dvm.*;import com.github.unidbg.memory.Memory;import java.io.File;import java.io.IOException;public class Zuoye4_3 extends AbstractJni {
private final AndroidEmulator emulator; private final VM vm; private final String RESULT = "57fdeca2cac0509b2e9e5c52a5b573c1608a33ac1ffb9e8210d2e129557e7f1b"; //need this result public Zuoye5_1() {
emulator = AndroidEmulatorBuilder.for32Bit().addBackendFactory(new DynarmicFactory(true)).build(); Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver(23)); SystemPropertyHook systemPropertyHook = new SystemPropertyHook(emulator); systemPropertyHook.setPropertyProvider(new MyProvider()); memory.addHookListener(systemPropertyHook); vm = emulator.createDalvikVM(null); vm.setJni(this); vm.setVerbose(false); DalvikModule dalvikModule = vm.loadLibrary(new File("unidbg-android/src/test/resources/example_binaries/armeabi-v7a/libroysue4_3.so"), false); dalvikModule.callJNI_OnLoad(emulator); } /** * 返回非Google手机信息 */ class MyProvider implements SystemPropertyProvider {
@Override public String getProperty(String key) {
switch (key) {
case "ro.product.manufacturer": return "zycmanufacturer"; case "ro.product.model": return "zycmodel"; } return ""; } } private String crack() {
DvmClass dvmClass = vm.resolveClass("com/roysue/easyso1/MainActivity"); for (int i = 10000; i <= 99999; i++) {
//brute force attack DvmObject ret = dvmClass.callStaticJniMethodObject(emulator, "Sign(Ljava/lang/String;)Ljava/lang/String;", new Object[]{
String.valueOf(i)}); String forceStr = String.valueOf(ret.getValue()); if (forceStr.compareTo(RESULT) == 0) {
return String.valueOf(i); } } return ""; } public static void main(String[] args) {
Zuoye4_3 zuoye = new Zuoye4_3(); System.out.println("need input ==> " + zuoye.crack()); zuoye.destroy(); } /** * 补全FINGERPRINT,与aosp不沾边即可。 */ @Override public DvmObject
callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
if (signature.equals("android/os/Build->FINGERPRINT:Ljava/lang/String;")) {
String ret = "zyc"; return new StringObject(vm, ret); } return super.callStaticObjectMethod(vm, dvmClass, signature, varArg); } private void destroy() {
try {
emulator.close(); System.out.println("emulator destroy..."); } catch (IOException e) {
e.printStackTrace(); } }}

运行得到【87654】,使用非Google(会被反调试阻碍)手机运行通过。

在这里插入图片描述

Frida暴力破解

和上面的原理相同,只要枚举10000~99999的整数即可,这里需要 -f 启动,hook function_check_tracerPID()system_getproperty_check()FINGERPRINT三处。

function hookGuard(){
var libroysue = Process.findModuleByName("libroysue.so"); var symbols = libroysue.enumerateExports(); for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i]; if (symbol.name.indexOf("tracerPID") >= 0) {
var address = symbol.address; Interceptor.attach(address, {
onLeave(retval) {
retval.replace(0); console.log("hook function_check_tracerPID"); } }); } if (symbol.name.indexOf("getproperty") >= 0) {
Interceptor.attach(symbol.address, {
onLeave(retval) {
retval.replace(0); console.log("hook system_getproperty_check"); } }); } } var clazz = Java.use("android.os.Build"); clazz.FINGERPRINT.value = "zyc123" console.log("hook FINGERPRINT" + clazz.FINGERPRINT.value);}function hook5_1() {
Java.perform(function () {
hookGuard(); var MainActivity = Java.use("com.roysue.easyso1.MainActivity"); var num = 10000; while (num < 99999) {
var res = MainActivity.Sign(num + ""); if (res == "57fdeca2cac0509b2e9e5c52a5b573c1608a33ac1ffb9e8210d2e129557e7f1b") {
console.log(num, " ==> ", res); break; } else {
console.error(num, " ==> ", res); } num++; } });}

运行:

在这里插入图片描述
 

目标2

如果没有“5位数”的提示,我们只好更深入研究其加密算法。上面的逆向分析仍存在一片迷雾——暂定为AES加密的部分。首先查看生成key的伪代码:

const uint8_t *ll11lll1l1(){
_BYTE *v1; // [sp+8h] [bp-18h] int i; // [sp+10h] [bp-10h] if ( li11li1o2() ) li11li1o1(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); if ( li11li1o2() ) li11li1o3(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); v1 = malloc(0x11u); for ( i = 0; i <= 15; ++i ) {
switch ( i ) {
case 0: v1[i] = 103; break; case 1: v1[i] = 111; break; case 2: v1[i] = 111; if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o2() ) li11li1o0(); if ( li11li1o0() ) li11li1o1(); if ( li11li1o2() ) li11li1o1(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o3() ) li11li1o1(); break; case 3: v1[i] = 100; break; case 4: v1[i] = 108; break; case 5: v1[i] = 45; break; case 6: v1[i] = 97; break; case 7: v1[i] = 101; break; case 8: v1[i] = 115; break; case 9: v1[i] = 45; if ( li11li1o1() ) li11li1o0(); if ( li11li1o2() ) li11li1o3(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); if ( li11li1o0() ) li11li1o2(); if ( li11li1o3() ) li11li1o0(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o2() ) li11li1o3(); break; case 10: v1[i] = 107; break; case 11: v1[i] = 101; break; case 12: v1[i] = 121; break; case 13: v1[i] = 49; break; case 14: v1[i] = 50; break; case 15: v1[i] = 52; break; default: continue; } } v1[16] = 0; if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o2() ) li11li1o0(); if ( li11li1o0() ) li11li1o1(); if ( li11li1o2() ) li11li1o1(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o3() ) li11li1o1(); return v1;}

生成iv伪代码:

const uint8_t *ll11l1l1l1(){
_BYTE *v1; // [sp+8h] [bp-18h] int i; // [sp+10h] [bp-10h] if ( li11li1o1() ) li11li1o0(); if ( li11li1o2() ) li11li1o3(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); if ( li11li1o0() ) li11li1o2(); if ( li11li1o3() ) li11li1o0(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o2() ) li11li1o3(); v1 = malloc(0x11u); for ( i = 0; i <= 15; ++i ) {
switch ( i ) {
case 0: v1[i] = 103; if ( li11li1o2() ) li11li1o1(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); if ( li11li1o2() ) li11li1o3(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); break; case 1: v1[i] = 111; break; case 2: v1[i] = 111; break; case 3: v1[i] = 100; break; case 4: v1[i] = 108; break; case 5: v1[i] = 45; break; case 6: v1[i] = 97; break; case 7: v1[i] = 101; break; case 8: v1[i] = 115; break; case 9: v1[i] = 45; break; case 10: v1[i] = 105; break; case 11: v1[i] = 118; if ( li11li1o0() ) li11li1o2(); if ( li11li1o3() ) li11li1o0(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o2() ) li11li1o3(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o2(); if ( li11li1o2() ) li11li1o0(); if ( li11li1o0() ) li11li1o1(); break; case 12: v1[i] = 49; break; case 13: v1[i] = 50; break; case 14: v1[i] = 51; break; case 15: v1[i] = 53; break; default: continue; } } v1[16] = 0; if ( li11li1o1() ) li11li1o0(); if ( li11li1o2() ) li11li1o3(); if ( li11li1o3() ) li11li1o1(); if ( li11li1o1() ) li11li1o0(); if ( li11li1o0() ) li11li1o2(); if ( li11li1o3() ) li11li1o0(); if ( li11li1o0() ) li11li1o3(); if ( li11li1o2() ) li11li1o3(); return v1;}

生成v8变量伪代码,这个函数显然是在做补位填充,像是“AES/CBC/PKCS7Padding”:

uint8_t *__fastcall ll11l1l11l(const unsigned __int8 *input){
unsigned int i; // [sp+4h] [bp-34h] unsigned int v3; // [sp+Ch] [bp-2Ch] _BYTE *v4; // [sp+14h] [bp-24h] signed int v5; // [sp+1Ch] [bp-1Ch] v5 = strlen(input); v3 = 16 * (v5 / 16) + 16; v4 = malloc(16 * (v5 / 16) + 17); for ( i = 0; i < v3; ++i ) {
if ( i >= v5 ) {
if ( v5 % 16 ) v4[i] = HEX[v3 - v5]; else v4[i] = HEX[0]; } else {
v4[i] = input[i]; } } v4[v3] = 0; return v4;}

主要的加密变换伪代码,可以确认是AES加密

void __fastcall qpppqp(uint8_t *output, uint8_t *input, uint32_t length, const uint8_t *key, const uint8_t *iv){
__int64 v5; // d17 unsigned __int8 v6; // [sp+Bh] [bp-2Dh] uint32_t i; // [sp+Ch] [bp-2Ch] v6 = length & 0xF; if ( key ) {
Key = key; KeyExpansion(); } if ( iv ) Iv = iv; for ( i = 0; i < length; i += 16 ) {
v5 = *(input + 1); *output = *input; *(output + 1) = v5; XorWithIv(output); state = output; Cipher(); Iv = output; input += 16; output += 16; } if ( v6 ) {
qmemcpy(output, input, v6); memset(&output[v6], 0, 16 - v6); XorWithIv(output); state = output; Cipher(); }}

做完AES之后还有一道Hex变换

unsigned __int8 *__fastcall bbddbbdbb(const unsigned __int8 *input, size_t len){
size_t i; // [sp+4h] [bp-1Ch] _BYTE *v4; // [sp+8h] [bp-18h] if ( !input || !len ) return 0; v4 = malloc(2 * len + 1); for ( i = 0; i < len; ++i ) {
v4[2 * i] = _0123456789abcdef[input[i] >> 4]; v4[2 * i + 1] = _0123456789abcdef[input[i] & 0xF]; } v4[2 * len] = 0; return v4;}

综上分析本题加密流程大致为:

在这里插入图片描述
要逆推算法,首先需要获取AES的keyivpadding mode,其中padding mode已经可以推测是“AES/CBC/PKCS7Padding”,剩余两个我使用Frida hook出来:

var soAddress = Module.findBaseAddress("libroysue.so");if (soAddress) {
var methodKeyAddress = soAddress.add(0x0003CC68 + 1); // thrumb指令集要+1 if (methodKeyAddress) {
console.log("函数地址:" + methodKeyAddress); Interceptor.attach(methodKeyAddress, {
onLeave(retval) {
console.log("key:\n" + hexdump(retval)); } }); }}var methodivAddress = soAddress.add(0x0003D254 + 1); // thrumb指令集要+1if (methodivAddress) {
console.log("函数地址:" + methodivAddress); Interceptor.attach(methodivAddress, {
onLeave(retval) {
console.log("iv:\n" + hexdump(retval)); } });}

得出key为【goodl-aes-key124】,iv为【goodl-aes-iv1235】:

在这里插入图片描述
Hex变换结果为最终字符串去掉末尾MD5部分,即【57fdeca2cac0509b2e9e5c52a5b573c1】,这里用Z3Prover库计算变换之前的每一位数。

from z3 import *RES = "57fdeca2cac0509b2e9e5c52a5b573c1"inputStr = [BitVec('%d' % i, 32) for i in range(16)]solverInputStr = Solver()for i in range(16):    solverInputStr.add(inputStr[i] >> 4 == int(RES[i*2], 16))    solverInputStr.add(inputStr[i] & 0xF == int(RES[i*2+1], 16))if solverInputStr.check() == sat:    m = solverInputStr.model()    inputRes = [0 for _ in range(16)]    for i in range(16):        inputRes[int(str(m[i]))] = int((m[m[i]]).as_long())    print("最终变换之前数组:")    print(inputRes)else:    print('solverInputStr unsat')

得到Hex变换之前(AES变换之后)的数据:

在这里插入图片描述
只要将这段做AES解密,并去掉 REAL 即正确flag,完整代码:

from z3 import *from Crypto.Cipher import AESRES = "57fdeca2cac0509b2e9e5c52a5b573c1"inputStr = [BitVec('%d' % i, 32) for i in range(16)]solverInputStr = Solver()for i in range(16):    solverInputStr.add(inputStr[i] >> 4 == int(RES[i*2], 16))    solverInputStr.add(inputStr[i] & 0xF == int(RES[i*2+1], 16))if solverInputStr.check() == sat:    m = solverInputStr.model()    inputRes = [0 for _ in range(16)]    for i in range(16):        inputRes[int(str(m[i]))] = int((m[m[i]]).as_long())    print("最终变换之前数组:")    print(inputRes)else:    print('solverInputStr unsat')    def unpad(text):    pad = ord(text[-1])    return text[:-pad]def AESDecrypt(key, iv, enc):    cipher = AES.new(bytearray(key,'utf-8') , AES.MODE_CBC, bytearray(iv,'utf-8') )    return unpad(cipher.decrypt(enc).decode("utf-8"))source = AESDecrypt('goodl-aes-key124','goodl-aes-iv1235', (bytearray(inputRes)))print("AES加密前字符串:")print(source)print("flag:")print(source.replace("REAL",""))

得到正确答案:

在这里插入图片描述

转载地址:https://blog.csdn.net/zyc3545/article/details/117627955 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:## Arduino控制LED灯的渐变效果
下一篇:JavaScript逆向:GlidedSky 字体加密题

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月16日 13时26分32秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章