This is the second challenge on APK. We used the same command to extract the file.
apktool d baby_android-2.apk
cd baby_android-2 && tree -L 1
.
├── ActivityMainBinding
├── AndroidManifest.xml
├── apktool.yml
├── kotlin
├── lib
├── META-INF
├── original
├── res
├── smali
├── smali_classes2
├── smali_classes3
├── smali_classes4
└── unknown
Using the jadx decompiler, we decompiled some of the smali files.
We want to see what is happening in MainActivity. To do so, we use JADX decompiler to retrieve a Java code from SMALI files.
The MainActivity was located in `smali_classes4/byuctf/babyandroid/MainActivity$1.smali.
So we used the command :
jadx smali_classes4/byuctf/babyandroid/MainActivity\$1.smali
The command then creates a directory named MainActivity$1 with the outputs of decompilation.
package byuctf.babyandroid;
import android.view.View;
import android.widget.TextView;
/* loaded from: /tmp/jadx-15504538600247676588.dex */
class MainActivity$1 implements View.OnClickListener {
final /* synthetic */ MainActivity this$0;
MainActivity$1(MainActivity this$0) {
this.this$0 = this$0;
}
@Override // android.view.View.OnClickListener
public void onClick(View view) {
String flagAttempt = MainActivity.access$000(this.this$0).getText().toString();
TextView banner = (TextView) this.this$0.findViewById(R.id.banner);
if (FlagChecker.check(flagAttempt)) {
banner.setText("That's the right flag!!!");
} else {
banner.setText("Nope! Try again if you'd like");
}
}
}
We can see from the decompiled code that some kind of checks are done using the static function FlagChecker.check.
Decompiling the code that gives information on the FlagChecker class (located in smali_classes4/byuctf/babyandroid/FlagChecker.smali) gives us this :
package byuctf.babyandroid;
/* loaded from: /tmp/jadx-12705296425172830130.dex */
public class FlagChecker {
public static native boolean check(String str);
static {
System.loadLibrary("babyandroid");
}
}
We clearly see that a dynamic library is loaded within the Java code. This library is located in lib/x86_64/libbabyandroid.so
file lib/x86_64/libbabyandroid.so
lib/x86_64/libbabyandroid.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=90037cbe182cf81ad3d59cd28947da7543276d3d, stripped
We can use Ghidra to decompile this dynamic library. The check function can easily be retrieved :
undefined Java_byuctf_babyandroid_FlagChecker_check
(_jstring *param_1,undefined8 param_2,uchar *param_3)
{
long lVar1;
char *pcVar2;
long in_FS_OFFSET;
int iStack_58;
undefined uStack_29;
basic_string<> abStack_28 [24];
long lStack_10;
lStack_10 = *(long *)(in_FS_OFFSET + 0x28);
_JNIEnv::GetStringUTFChars(param_1,param_3);
std::__ndk1::basic_string<>::basic_string<>((char *)abStack_28);
lVar1 = FUN_001206f0(abStack_28);
if (lVar1 == 0x17) {
for (iStack_58 = 0; iStack_58 < 0x17; iStack_58 = iStack_58 + 1) {
pcVar2 = (char *)FUN_00120710(abStack_28,(long)iStack_58);
if (*pcVar2 !=
"bycnu)_aacGly~}tt+?=<_ML?f^i_vETkG+b{nDJrVp6=)="[(iStack_58 * iStack_58) % 0x2f]) {
uStack_29 = 0;
goto LAB_00120608;
}
}
uStack_29 = 1;
}
else {
uStack_29 = 0;
}
LAB_00120608:
std::__ndk1::basic_string<>::~basic_string(abStack_28);
if (*(long *)(in_FS_OFFSET + 0x28) == lStack_10) {
return uStack_29;
}
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
The code basically checks for the flag using a special iteration We iterate on each characters in reverse order using i * i, with ‘i’ ranging from 0 to 0x17 excluded. To decode the flag, we just have to iterate on the characters using this iteration from left to right.
def solve():
reference = "bycnu)_aacGly~}tt+?=<_ML?f^i_vETkG+b{nDJrVp6=)"
length = 0x17
result = []
for i in range(length):
idx = (i * i) % 0x2f
result.append(reference[idx])
flag = "".join(result)
print(flag)
if __name__ == "__main__":
solve()
python3 solve.py
byuctf{c++_in_an_apk??}