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??}