Writeups Backdoor CTF 2023 | REV

Writeups Backdoor CTF 2023 | REV

In this CTF, I didn't get a chance to do many rev challenges, partly because I had too many deadlines, and also partly because my old machine was brea

·

12 min read

Open Sesame

Attachment: open_sesame.apk

Here is an easy challenge apk. Use Decompiler.com to decompile this apk file

Check sources/com/example/open_sesame/MainActivity.java:

package com.example.open_sesame;

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;
import androidx.appcompat.app.AppCompatDelegate;

public class MainActivity extends AppCompatActivity {
    private static final int[] valid_password = {52, AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR, 49, 98, 97, 98, 97};
    private static final String valid_user = "Jack Ma";
    private Button buttonLogin;
    private EditText editTextPassword;
    private EditText editTextUsername;

    /* access modifiers changed from: protected */
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        this.editTextUsername = (EditText) findViewById(R.id.editTextUsername);
        this.editTextPassword = (EditText) findViewById(R.id.editTextPassword);
        Button button = (Button) findViewById(R.id.buttonLogin);
        this.buttonLogin = button;
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                MainActivity.this.validateCredentials();
            }
        });
    }

    /* access modifiers changed from: private */
    public void validateCredentials() {
        String trim = this.editTextUsername.getText().toString().trim();
        String trim2 = this.editTextPassword.getText().toString().trim();
        if (!trim.equals(valid_user) || !n4ut1lus(trim2)) {
            showToast("Invalid credentials. Please try again.");
            return;
        }
        "flag{" + flag(Integer.toString(sl4y3r(sh4dy(trim2))), "U|]rURuoU^PoR_FDMo@X]uBUg") + "}";
    }

    private boolean n4ut1lus(String str) {
        int[] it4chi = it4chi(str);
        if (it4chi.length != valid_password.length) {
            return false;
        }
        for (int i = 0; i < it4chi.length; i++) {
            if (it4chi[i] != valid_password[i]) {
                return false;
            }
        }
        return true;
    }

    private int[] it4chi(String str) {
        int[] iArr = new int[str.length()];
        for (int i = 0; i < str.length(); i++) {
            iArr[i] = str.charAt(i);
        }
        return iArr;
    }

    private String sh4dy(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char charAt = str.charAt(i);
            if (Character.isDigit(charAt)) {
                sb.append(charAt);
            }
        }
        return sb.toString();
    }

    private int sl4y3r(String str) {
        return Integer.parseInt(str) - 1;
    }

    private String flag(String str, String str2) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str2.length(); i++) {
            sb.append((char) (str2.charAt(i) ^ str.charAt(i % str.length())));
        }
        return sb.toString();
    }

    private void showToast(String str) {
        Toast.makeText(this, str, 0).show();
    }
}

We should pay attention to some section:

  • "flag{" + flag(Integer.toString(sl4y3r(sh4dy(trim2))), "U|]rURuoU^PoR_FDMo@X]uBUg") + "}";

  • valid_password = {52, AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR, 49, 98, 97, 98, 97};

  • valid_user = "Jack Ma";

Here there are functions we need to pay attention to: SL4Y3R, SH4DY, IT4CHI and valid_user - valid_password Here is my script to reverse this program to get flag:

def get_flag():
    valid_password = [52, 108, 49, 98, 97, 98, 97]
    valid_user = "Jack Ma"
    str2 = "U|]rURuoU^PoR_FDMo@X]uBUg"

    def it4chi(str):
        return [ord(c) for c in str]

    def sh4dy(str):
        return ''.join(c for c in str if c.isdigit())

    def sl4y3r(str):
        return int(str) - 1

    def flag(str, str2):
        return ''.join(chr(ord(str2[i]) ^ ord(str[i % len(str)])) for i in range(len(str2)))

    password = ''.join(chr(i) for i in valid_password)
    str1 = sh4dy(password)
    str_res = str1
    str_res = str(sl4y3r(str_res))
    return "flag{" + flag(str_res, str2) + "}"

print(get_flag())

Secret Door

Attachment:

This is a C program written quite simply like to check the password and if correct, will decrypt the encoded file.bin to issue the flag image

dump IDA (main function):

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  char v5; // [rsp+13h] [rbp-EDh] BYREF
  int v6; // [rsp+14h] [rbp-ECh]
  int *v7; // [rsp+18h] [rbp-E8h]
  char v8[32]; // [rsp+20h] [rbp-E0h] BYREF
  char v9[32]; // [rsp+40h] [rbp-C0h] BYREF
  char v10[32]; // [rsp+60h] [rbp-A0h] BYREF
  char v11[32]; // [rsp+80h] [rbp-80h] BYREF
  int v12[18]; // [rsp+A0h] [rbp-60h]
  unsigned __int64 v13; // [rsp+E8h] [rbp-18h]

  v13 = __readfsqword(0x28u);
  if ( argc != 2 )
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "Just try to get the door");
    exit(0);
  }
  if ( strlen(argv[1]) != 17 )
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "that's not even a door :p");
    exit(0);
  }
  v6 = 0;
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v8);
  v12[0] = 66;
  v12[1] = 119;
  v12[2] = 101;
  v12[3] = 113;
  v12[4] = 123;
  v12[5] = 98;
  v12[6] = 114;
  v12[7] = 125;
  v12[8] = 119;
  v12[9] = 89;
  v12[10] = 115;
  v12[11] = 125;
  v12[12] = 111;
  v12[13] = 109;
  v12[14] = 62;
  v12[15] = 1;
  v12[16] = 0;
  while ( v6 <= 16 )
  {
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::push_back(
      v8,
      (unsigned int)(char)(LOBYTE(v12[v6]) ^ (v6 + 17)));
    ++v6;
  }
  std::allocator<char>::allocator(&v5);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string<std::allocator<char>>(
    v9,
    "ThatsHardcoded!!!",
    &v5);
  std::allocator<char>::~allocator(&v5);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v11, v9);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v10, v8);
  func_5(v10, v11);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v10);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v11);
  v7 = (int *)operator new[](0x44uLL);
  v3 = func_4(v8, argv[1]);
  v7 = (int *)func_3(v3, v9);
  if ( (unsigned __int8)func_2(v7) )
    func_1(*v7, v7[16]);
  else
    std::operator<<<std::char_traits<char>>(&std::cout, "Wrong door");
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v9);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v8);
  return 0;
}

In this execute file, we must input 1 argv and length of argv is 17

if ( argc != 2 )
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "Just try to get the door");
    exit(0);
  }
  if ( strlen(argv[1]) != 17 )
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "that's not even a door :p");
    exit(0);
  }
  • In each iteration, v8 will append the value ((v12[i]) ^ (i + 17)) (i ascending)

--> v8 += chr((v12[i]) ^ (i + 17))

we can find v8:

v6 = 0
v8 = ""
v12 = [
    66, 119, 101, 113, 123, 98, 114, 125,
    119, 89, 115, 125, 111, 109, 62, 1, 0
]
while v6 <= 16:
    v8 += chr(v12[v6] ^ (v6 + 17))
    v6 += 1
print(v8)
# v8 = "SeventeenChars!!!"
  • v9 = "ThatsHardcoded!!!"

func_5

__int64 __fastcall func_5(__int64 a1, __int64 a2)
{
  int v2; // ebx
  _BYTE *v3; // rax
  char v4; // bl
  _BYTE *v5; // rax
  __int64 v6; // rbx
  char v8; // [rsp+1Bh] [rbp-55h] BYREF
  int v9; // [rsp+1Ch] [rbp-54h]
  int i; // [rsp+20h] [rbp-50h]
  int j; // [rsp+24h] [rbp-4Ch]
  _BYTE *v12; // [rsp+28h] [rbp-48h]
  char v13[40]; // [rsp+30h] [rbp-40h] BYREF
  unsigned __int64 v14; // [rsp+58h] [rbp-18h]

  v14 = __readfsqword(0x28u);
  v9 = 0;
  for ( i = 0; i <= 16; ++i )
  {
    v2 = *std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, i);
    v9 += v2 + *std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a2, i);
  }
  v3 = operator new(1uLL);
  *v3 = 0;
  v12 = v3;
  std::allocator<char>::allocator(&v8);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string<std::allocator<char>>(
    v13,
    "R{{xqpMuYu`qKLPLP",
    &v8);
  std::allocator<char>::~allocator(&v8);
  for ( j = 0; j <= 16; ++j )
  {
    v4 = v9 + 46;
    v5 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](v13, j);
    v12[j] = v4 ^ *v5;
  }
  v6 = v12;
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v13);
  return v6;
}

i have script to find result of func_5:

def func_5(a1, a2):
    v9 = 0
    for i in range(17):
        v2 = ord(a1[i])
        v9 += v2 + ord(a2[i])

    v12 = bytearray(17)
    v8 = bytearray(1)
    v8[0] = 0

    v13 = "R{{xqpMuYu`qKLPLP"
    for j in range(17):
        v4 = (v9 + 46) % 256
        v5 = ord(v13[j])
        v12[j] = v4 ^ v5

    return bytes(v12)

a1 = "SeventeenChars!!!"
a2 = "ThatsHardcoded!!!"
result = func_5(a1, a2)
print(result)
    
# b'FooledYaMate_XDXD'

But func_5 is a joke hix :(((

func_4

__int64 __fastcall func_4(__int64 a1, __int64 a2)
{
  unsigned __int64 i; // [rsp+18h] [rbp-18h]
  unsigned __int64 v4; // [rsp+20h] [rbp-10h]
  __int64 v5; // [rsp+28h] [rbp-8h]

  v4 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::size(a1);
  v5 = operator new[](0x44uLL);
  for ( i = 0LL; i < v4; ++i )
    *(v5 + 4 * i) = (*(a2 + i) ^ *std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                                    a1,
                                    i));
  return v5;
}

func_3

__int64 __fastcall func_3(__int64 a1, __int64 a2)
{
  int i; // [rsp+14h] [rbp-Ch]
  __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = operator new[](0x44uLL);
  for ( i = 0; i <= 16; ++i )
    *(4LL * i + v4) = *std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a2, i) ^ *(4LL * i + a1);
  return v4;
}

func_2

_BOOL8 __fastcall func_2(int *a1)
{
  return *a1 == 78
      && a1[1] != (*a1 == 15)
      && a1[2] == 120
      && a1[3] != (a1[2] == 31)
      && a1[4] == 120
      && a1[5] != (a1[4] == 11)
      && a1[6] == 116
      && a1[6] != (a1[7] == 6)
      && a1[8] == 100
      && a1[9] != (a1[8] == 33)
      && a1[10] == 99
      && a1[11] != (a1[10] == 34)
      && a1[12] == 120
      && a1[13] == a1[12]
      && a1[14] == 114
      && a1[15] == a1[14] + 1
      && a1[16] == 33;
}

so here is logic of func_3 & func_4:

def func_4(a1, a2):
    v4 = len(a1)
    v5 = bytearray()
    for i in range(v4):
        v5.append((ord(a2[i]) ^ ord(a1[i])) % 256)
    return v5

def func_3(a1, a2):
    v4 = [0]*17
    for i in range(17):
        v4[i] = ord(a2[i]) ^ a1[i]
    return v4

So we can find a1 to func_2 is True with z3 python then we can use func_3, func_4 to find key:

from z3 import *

flags = [BitVec(f'char_{i}', 8) for i in range(17)]
unk1 = [BitVec(f'z1_{i}', 8) for i in range(17)]

s = Solver()
s.add(len(flags) == 17)

for i in range(17):
    s.add(And(flags[i] >= 33, flags[i] <= 125))

str1 = "SeventeenChars!!!"
str2 = "ThatsHardcoded!!!" 

for i in range(17):
    unk1[i] = flags[i] ^ ord(str1[i])

for i in range(17):
    unk1[i] = unk1[i] ^ BitVecVal(ord(str2[i]), 8)

condition = And(
    unk1[0] == 78,
    unk1[1] != If(unk1[0] == 15, BitVecVal(1, 8), BitVecVal(0, 8)),
    unk1[2] == 120,
    unk1[3] != If(unk1[2] == 31, BitVecVal(1, 8), BitVecVal(0, 8)),
    unk1[4] == 120,
    unk1[5] != If(unk1[4] == 11, BitVecVal(1, 8), BitVecVal(0, 8)),
    unk1[6] == 116,
    unk1[7] != If(unk1[6] == 6, BitVecVal(1, 8), BitVecVal(0, 8)),
    unk1[8] == 100,
    unk1[9] != If(unk1[8] == 33, BitVecVal(1, 8), BitVecVal(0, 8)),
    unk1[10] == 99,
    unk1[11] != If(unk1[10] == 34, BitVecVal(1, 8), BitVecVal(0, 8)),
    unk1[12] == 120,
    unk1[13] == unk1[12],
    unk1[14] == 114,
    unk1[15] == unk1[14] + 1,
    unk1[16] == 33
)

s.add(condition)

if s.check() == sat:
    m = s.model()
    flag = ''.join([chr(m[flags[i]].as_long()) for i in range(17)])
    print(f"Pass: {flag}")
else:
    print("No solution found!")

# Pass: I$o0e!p0n0d0oors!

When i put it like as argv it false:

image

I think $ in the key is special character in Linux, so i edit this command from ./chall.out I$o0e!p0n0d0oors! to ./chall.out $(echo SSRvMGUhcDBuMGQwb29ycyE= | base64 -d) and here is the flag:

the_door

baby eBPF

Attachment: babyebpf.o

eBPF (extended Berkeley Packet Filter) is a technology in the Linux kernel that allows the execution of custom programs within the kernel itself. These programs are typically used for monitoring and filtering network events and can be attached to various hook points in the kernel.

In this chall, we must use process EBPF from https://github.com/zandi/eBPF_processor to dissasemble this file

image

image

detect_execve:
    mov            r1, 0x1C050444
    stxw           [r10-8], r1
    lddw           r1, 0x954094701340819
    stxdw          [r10-0x10], r1
    lddw           r1, 0x10523251403E5713
    stxdw          [r10-0x18], r1
    lddw           r1, 0x43075A150E130D0B
    stxdw          [r10-0x20], r1
    mov            r1, 0

LBB0_1:
    lddw           r2, 0
    add            r2, r1
    ldxb           r2, [r2]
    mov            r3, r10
    add            r3, -0x20
    add            r3, r1
    ldxb           r4, [r3]
    xor            r2, r4
    stxb           [r3], r2
    add            r1, 1
    jeq            r1, 0x1C, LBB0_2
    ja             LBB0_1
--------------------------------------
LBB0_2:
    mov            r3, r10
    add            r3, -0x20
    lddw           r1, 0x1C
    mov            r2, 4
    call           6

    mov            r0, 1
    ret
--------------------------------------   
szmarinkitagawa:db "marinkitagawamarinkitagawama",0

This is simple assembly code, so I have the script to get the flag, notice that the key is marinkitagawamarinkitagawama:

key = "marinkitagawamarinkitagawama"
data = [
    0x1C050444,
    0x954094701340819,
    0x10523251403E5713,
    0x43075A150E130D0B
]
rote = [((value >> i) & 0xFF) for value in reversed(data) for i in range(0, 64, 8)]
flag = ''.join(chr(b ^ ord(key[i % len(key)])) for i, b in enumerate(rote))
print(flag)
# flag{1n7r0_70_3bpf_h3h3h3eh}mari

or we can guess easily xor with length of key and data xD

Sl4ydroid

Attachment: sl4ydroid.apk like the Open Sesame challenge, we will use Decompiler.com to decompile this apk

In this challenge, during the ctf, I only completed 3/4 of flag, which is quite unfortunate

I use jadx to decompile this apk and export lib because nothing java usefull in source :<<

image

When i load libsl4ydroid.so to IDA, the first function i see RC4 hash:

int __stdcall firsts(int a1)
{
  unsigned int v1; // eax
  unsigned int v2; // eax
  int v4; // [esp+10h] [ebp-58h]
  int v5; // [esp+18h] [ebp-50h]
  char v6; // [esp+1Fh] [ebp-49h]
  unsigned int i; // [esp+30h] [ebp-38h]
  char v8[16]; // [esp+40h] [ebp-28h] BYREF
  char v9[16]; // [esp+50h] [ebp-18h] BYREF
  unsigned int v10; // [esp+60h] [ebp-8h]

  v10 = __readgsdword(0x14u);
  std::string::basic_string<decltype(nullptr)>(a1, "b5)c]d/ZP1:\"");
  std::string::basic_string<decltype(nullptr)>(v9, "R00rkee");
  std::string::basic_string<decltype(nullptr)>(v8, "R1zz");
  for ( i = 0; i <= sub_1AC20(a1) - 1; ++i )
  {
    v4 = *sub_1AC50(a1, i);
    v1 = sub_1AC20(v9);
    v5 = *sub_1AC50(v9, i % v1) ^ v4;
    v2 = sub_1AC20(v8);
    v6 = *sub_1AC50(v8, i % v2) ^ v5;
    *sub_1AC50(a1, i) = v6;
  }
  std::string::~string(v8);
  std::string::~string(v9);
  return a1;
}

when i convert it to python and run, i receive b4ckd00r2023, so it will be RC4

Java_com_backdoor_sl4ydroid_MainActivity_kim

unsigned int __cdecl Java_com_backdoor_sl4ydroid_MainActivity_kim(_JNIEnv *a1, int a2, int a3)
{
    _BYTE *v3; // eax
    const char *v4; // eax
    int v6; // [esp+34h] [ebp-64h]
    int StringUTFChars; // [esp+40h] [ebp-58h]
    int MethodID; // [esp+4Ch] [ebp-4Ch]
    int v9; // [esp+50h] [ebp-48h]
    int ObjectClass; // [esp+54h] [ebp-44h]
    char v11[8]; // [esp+58h] [ebp-40h] BYREF
    char v12[8]; // [esp+60h] [ebp-38h] BYREF
    char v13[16]; // [esp+68h] [ebp-30h] BYREF
    char v14[16]; // [esp+78h] [ebp-20h] BYREF
    unsigned int v15; // [esp+88h] [ebp-10h]

    v15 = __readgsdword(0x14u);
    ObjectClass = _JNIEnv::GetObjectClass(a1, a2);
    ring(v14, &s1, &us3_m3);
    v9 = sub_1BCB0(v14);
    MethodID = _JNIEnv::GetMethodID(a1, ObjectClass, v9, "(Ljava/lang/String;)V");
    StringUTFChars = _JNIEnv::GetStringUTFChars(a1, a3, 0);
    std::string::basic_string<decltype(nullptr)>(v13, StringUTFChars);
    _JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
    sub_1B850(v12);
    sub_1B8A0(v11);
    while ( (sub_1B910(v12, v11) & 1) != 0 )
    {
        v3 = sub_1B950(v12);
        *v3 += 8;
        *v3 ^= 7u;
        sub_1B960(v12);
    }

    v4 = sub_1BCB0(v13);
    v6 = _JNIEnv::NewStringUTF(a1, v4);
    _JNIEnv::CallVoidMethod(a1, a2, MethodID, v6);
    _JNIEnv::DeleteLocalRef(a1, v6);
    std::string::~string(v13);
    std::string::~string(v14);
    return __readgsdword(0x14u);
}

Here is simple encrypt with +8 and ^7 so i use script:

def encrypt_string(input_str, key):
    result = bytearray()

    for i in range(len(key)):
        encrypted_char = (ord(key[i]) + 8) ^ 7
        result += encrypted_char.to_bytes(1, byteorder="little")

    return result

k1 = "Yc^XtMfu"
m1 = "d23ba52679c5e1"

encrypted_result = encrypt_string(m1, k1)
print("Encrypted Result:", encrypted_result.hex())
# 666c61677b52697a --> flag{Riz

Java_com_backdoor_sl4ydroid_MainActivity_damn

unsigned int __cdecl Java_com_backdoor_sl4ydroid_MainActivity_damn(_JNIEnv *a1, int a2, int a3)
{
    int v3; // eax
    const char *v4; // eax
    char v6; // [esp+27h] [ebp-71h]
    int v7; // [esp+38h] [ebp-60h]
    int i; // [esp+3Ch] [ebp-5Ch]
    int StringUTFChars; // [esp+40h] [ebp-58h]
    int MethodID; // [esp+4Ch] [ebp-4Ch]
    int v11; // [esp+50h] [ebp-48h]
    int ObjectClass; // [esp+54h] [ebp-44h]
    char v13[16]; // [esp+58h] [ebp-40h] BYREF
    char v14[16]; // [esp+68h] [ebp-30h] BYREF
    char v15[16]; // [esp+78h] [ebp-20h] BYREF
    unsigned int v16; // [esp+88h] [ebp-10h]

    v16 = __readgsdword(0x14u);
    ObjectClass = _JNIEnv::GetObjectClass(a1, a2);
    ring(v15, &r1, &us3_m3);
    v11 = sub_1BCB0(v15);
    MethodID = _JNIEnv::GetMethodID(a1, ObjectClass, v11, "(Ljava/lang/String;)V");
    StringUTFChars = _JNIEnv::GetStringUTFChars(a1, a3, 0);
    std::string::basic_string<decltype(nullptr)>(v14, StringUTFChars);
    _JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
    std::string::basic_string(v13, v14);
    for ( i = sub_1AC20(v13) - 1; i >= 0; --i )
    {
        v6 = *sub_1AC50(v13, i) ^ 0xC;
        v3 = sub_1AC20(v14);
        *sub_1AC50(v14, v3 - 1 - i) = v6;
    }

    v4 = sub_1BCB0(v14);
    v7 = _JNIEnv::NewStringUTF(a1, v4);
    _JNIEnv::CallVoidMethod(a1, a2, MethodID, v7);
    _JNIEnv::DeleteLocalRef(a1, v7);
    std::string::~string(v13);
    std::string::~string(v14);
    std::string::~string(v15);
    return __readgsdword(0x14u);
}

Here is simple xor with 0xC and reverse strings

def damn(k3):
    v14 = k3
    for i in range(len(k3)-1, -1, -1):
        v6 = ord(k3[i]) ^ 0xC
        v14 = v14[:len(k3)-1-i] + chr(v6) + v14[len(k3)-1-i+1:]
    return v14

k3 = "~?z?^S8o"
result = damn(k3)
print(result)

# c4_R3v3r

Java_com_backdoor_sl4ydroid_MainActivity_k2

unsigned int __cdecl Java_com_backdoor_sl4ydroid_MainActivity_k2(_JNIEnv *a1, int a2, int a3)
{
    unsigned int v3; // eax
    const char *v4; // eax
    char v6; // [esp+1Ch] [ebp-6Ch]
    char v7; // [esp+23h] [ebp-65h]
    int v8; // [esp+38h] [ebp-50h]
    unsigned int i; // [esp+3Ch] [ebp-4Ch]
    int StringUTFChars; // [esp+40h] [ebp-48h]
    int MethodID; // [esp+4Ch] [ebp-3Ch]
    int v12; // [esp+50h] [ebp-38h]
    int ObjectClass; // [esp+54h] [ebp-34h]
    char v14[16]; // [esp+58h] [ebp-30h] BYREF
    char v15[16]; // [esp+68h] [ebp-20h] BYREF
    unsigned int v16; // [esp+78h] [ebp-10h]

    v16 = __readgsdword(0x14u);
    ObjectClass = _JNIEnv::GetObjectClass(a1, a2);
    ring(v15, &r2, &us3_m3);
    v12 = sub_1BCB0(v15);
    MethodID = _JNIEnv::GetMethodID(a1, ObjectClass, v12, "(Ljava/lang/String;)V");
    StringUTFChars = _JNIEnv::GetStringUTFChars(a1, a3, 0);
    std::string::basic_string<decltype(nullptr)>(v14, StringUTFChars);
    _JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
    for ( i = 0; i <= sub_1AC20(v14) - 1; ++i )
    {
        v6 = *sub_1AC50(v14, i);
        v3 = sub_1AC20(&v2);
        v7 = *sub_1AC50(&v2, i % v3) ^ v6;
        *sub_1AC50(v14, i) = v7;
    }

    v4 = sub_1BCB0(v14);
    v8 = _JNIEnv::NewStringUTF(a1, v4);
    _JNIEnv::CallVoidMethod(a1, a2, MethodID, v8);
    _JNIEnv::DeleteLocalRef(a1, v8);
    std::string::~string(v14);
    std::string::~string(v15);
    return __readgsdword(0x14u);
}

Here use RC4 with %v2 corresponds to m2

k4 = "xP78V`m?3XeL"


r = b""
m2 = "May_1??"
for i in range(len(k4)):
    ra = ord(m2[i % len(m2)]) ^ ord(k4[i])
    r += ra.to_bytes(2, byteorder="little")

print(r.replace(b"\x00",b""))
# b'51Ngg_RrR!:}'

I receive flag{Riz....c4_R3v3r51Ngg_RrR!:} and stuck with ring function in Java_com_backdoor_sl4ydroid_MainActivity_nim. It's a pity because this challenge is't hard hix :((. We can use log in Android Studio to check flag easily xDDD (From Vietzett)

image