├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── migrations.xml ├── misc.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle.kts ├── challenge.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── hextree │ │ └── flagproject │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── hextree │ │ │ └── flagproject │ │ │ ├── ChallengeActivity.java │ │ │ ├── FlagActivity.java │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── layout │ │ ├── activity_challenge.xml │ │ ├── activity_flag.xml │ │ └── activity_main.xml │ │ ├── mipmap-anydpi │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── io │ └── hextree │ └── flagproject │ └── ExampleUnitTest.java ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | } 4 | 5 | apply(from="challenge.gradle") 6 | 7 | android { 8 | namespace = "io.hextree.flagproject" 9 | compileSdk = 34 10 | 11 | defaultConfig { 12 | applicationId = "io.hextree.flagproject" 13 | minSdk = 29 14 | targetSdk = 34 15 | versionCode = 1 16 | versionName = "1.0" 17 | 18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | 21 | buildTypes { 22 | release { 23 | isMinifyEnabled = false 24 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility = JavaVersion.VERSION_1_8 29 | targetCompatibility = JavaVersion.VERSION_1_8 30 | } 31 | } 32 | 33 | dependencies { 34 | 35 | implementation("androidx.appcompat:appcompat:1.6.1") 36 | implementation("com.google.android.material:material:1.11.0") 37 | implementation("androidx.constraintlayout:constraintlayout:2.1.4") 38 | testImplementation("junit:junit:4.13.2") 39 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 40 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 41 | } -------------------------------------------------------------------------------- /app/challenge.gradle: -------------------------------------------------------------------------------- 1 | import java.security.MessageDigest 2 | 3 | // Add this task in your app module's build.gradle file 4 | task generateChallengeHash { 5 | // Define file paths 6 | def manifestFile = file('src/main/AndroidManifest.xml') 7 | def stringsFile = file('src/main/res/values/strings.xml') 8 | 9 | // Generate SHA-256 hash of AndroidManifest.xml 10 | def messageDigest = MessageDigest.getInstance("SHA-256") 11 | def manifestStr = new String(manifestFile.bytes) 12 | def cleanedString = manifestStr.replaceAll("[^A-Za-z0-9]", "") 13 | def manifestHash = messageDigest.digest(cleanedString.bytes).collect { String.format("%02x", it) }.join() 14 | 15 | // Read strings.xml content 16 | def stringsContent = stringsFile.getText() 17 | 18 | println "Hello, Gradle! $manifestHash" 19 | def a = "sec" 20 | def b = "ret_k" 21 | def c = "l" 22 | // Modify strings.xml content 23 | def newStringEntry = "$manifestHash" 24 | def pattern = ".*?" 25 | def found = stringsContent =~ pattern 26 | if (found.size() > 0) { 27 | // Insert the new string entry before the last closing tag 28 | stringsContent = stringsContent.replaceFirst(pattern, newStringEntry) 29 | } else { 30 | // Insert the new string entry before the last closing tag 31 | stringsContent = stringsContent.replaceFirst("", "$newStringEntry\n") 32 | } 33 | // Write the modified content back to strings.xml 34 | stringsFile.write(stringsContent) 35 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/io/hextree/flagproject/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.hextree.flagproject; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("io.hextree.flagproject", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/io/hextree/flagproject/ChallengeActivity.java: -------------------------------------------------------------------------------- 1 | package io.hextree.flagproject; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | 11 | public class ChallengeActivity extends AppCompatActivity { 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_challenge); 16 | 17 | // which button should you press? 18 | View.OnClickListener failHandler = new View.OnClickListener() { 19 | @Override 20 | public void onClick(View v) { 21 | startActivity(new Intent(ChallengeActivity.this, MainActivity.class)); 22 | } 23 | }; 24 | 25 | findViewById(R.id.button1).setOnClickListener(failHandler); 26 | findViewById(R.id.button2).setOnClickListener(failHandler); 27 | findViewById(R.id.button3).setOnClickListener(failHandler); 28 | findViewById(R.id.button4).setOnClickListener(failHandler); 29 | findViewById(R.id.button5).setOnClickListener(failHandler); 30 | findViewById(R.id.button6).setOnClickListener(failHandler); 31 | findViewById(R.id.button7).setOnClickListener(failHandler); 32 | findViewById(R.id.button8).setOnClickListener(failHandler); 33 | findViewById(R.id.button9).setOnClickListener(new View.OnClickListener() { 34 | @Override 35 | public void onClick(View v) { 36 | startActivity(new Intent(ChallengeActivity.this, FlagActivity.class)); 37 | } 38 | }); 39 | findViewById(R.id.button10).setOnClickListener(failHandler); 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/io/hextree/flagproject/FlagActivity.java: -------------------------------------------------------------------------------- 1 | package io.hextree.flagproject; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.util.Base64; 7 | import android.util.Log; 8 | import android.widget.SeekBar; 9 | import android.widget.TextView; 10 | 11 | 12 | public class FlagActivity extends AppCompatActivity { 13 | 14 | int progressTracking = 0; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_flag); 20 | 21 | // how to reveal the flag now? 22 | TextView text = findViewById(R.id.flag_text); 23 | SeekBar bar = findViewById(R.id.seek_bar); 24 | bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 25 | @Override 26 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 27 | text.setText("Read the code: "+progress+"%"); 28 | progressTracking = progress; 29 | } 30 | 31 | @Override 32 | public void onStartTrackingTouch(SeekBar seekBar) { 33 | } 34 | 35 | @Override 36 | public void onStopTrackingTouch(SeekBar seekBar) { 37 | // Success!!! Show the flag now! 38 | if(progressTracking==42) { 39 | text.setText(decryptFlag()); 40 | } 41 | } 42 | }); 43 | } 44 | 45 | // You could reverse engineer this function to find the flag, but it's probably faster if 46 | // you just read the activity code. 47 | // How do you get to this FlagActivity? What actions do you have to perform? 48 | // Run this project in the emulator and perform those actions to reveal the flag. 49 | String decryptFlag() { 50 | 51 | String z1 = a(getResources().getString(b(c.d.e))); 52 | int z2 = z1.length(), z3 = 0x10; 53 | byte[] z4 = new byte[z2 / 2]; 54 | for (int z5 = 0; z5 < z2; z5 += 2) { 55 | z4[z5 / 2] = (byte) ((Character.digit(z1.charAt(z5), z3) << 4) 56 | + Character.digit(z1.charAt(z5 + 1), z3)); 57 | } 58 | 59 | String z6 = a(getResources().getString(b(c.f.g))); 60 | byte[] z7 = Base64.decode(z6, Base64.DEFAULT); 61 | //byte[] z7 = z6.getBytes(); 62 | byte[] z8 = new byte[z7.length]; 63 | for (int z9 = 0; z9 < z7.length; z9++) { 64 | z8[z9] = (byte) (z7[z9] ^ z4[z9 % z4.length]); 65 | } 66 | 67 | Log.i("FLAG", new String(z8)); 68 | Log.i("base64.FLAG", new String(Base64.encode(z8, Base64.DEFAULT))); 69 | 70 | return new String(z8); 71 | } 72 | String a(String s) { return s; } 73 | int b(int i) { return i; } 74 | interface c { 75 | interface d { int e = R.string.challenge_secret_key; } 76 | interface f { int g = R.string.secret; } 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/java/io/hextree/flagproject/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.hextree.flagproject; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.util.Base64; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | int counter = 0; 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | // this is the main activity... how to get to the challenge? 19 | TextView text = findViewById(R.id.main_text); 20 | text.setOnClickListener(new View.OnClickListener() { 21 | @Override 22 | public void onClick(View v) { 23 | counter++; 24 | text.setText("Counter: "+counter); 25 | if(counter>9999) { 26 | startActivity(new Intent(MainActivity.this, ChallengeActivity.class)); 27 | } 28 | } 29 | }); 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_challenge.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 |