Projects STRLCPY eeva Commits 4d35eeaa
🤬
Showing first 66 files as there are too many
  • ■ ■ ■ ■ ■ ■
    README.md
     1 +# E<sup>2</sup>VA, the *Damn Vulnerable App*
     2 + 
     3 +E<sup>2</sup>VA is an app allowing for binary exploitation on Android OS. It allows an external user to select and communicate with modules that contain severe vulnerabilities. Therefore, E<sup>2</sup>VA enables research on the applicability of *standard* binary exploitation techniques to Android apps, which call native functions.
     4 + 
     5 +E<sup>2</sup>VA stands for *Exploitation Experience (with) Vulnerable App*. It is the foundation of a series of [blog posts](LINK TO BLOCK POSTS) that describe exploitation of some already existing vulnerable modules. The app runs on a Pixel 3 emulator (without Google Play for root access), running Android 12 (API level 31), and an x86 - 64 architecture. Other setups have not yet been tested!
     6 + 
     7 +## Installation
     8 + 
     9 +**Warning**: This app requests a [runtime - permission](https://developer.android.com/reference/android/provider/Settings.html#ACTION_MANAGE_OVERLAY_PERMISSION) that it needs to stay active for e.g. long debugging sessions. As this app contains **actual vulnerabilities**, there is always a risk of some third party attacking the app, getting code execution and rampaging through your system. Therefore, launch E<sup>2</sup>VA in a controlled environment, which does not contain any important and/or personal information that must not be lost and/or leaked. (or accept the risk)
     10 + 
     11 +Currently, there are two ways of installing E<sup>2</sup>VA.
     12 + 
     13 +### APK
     14 + 
     15 +Use the `.apk` file, which is (hopefully kept) up to date with the current version of E<sup>2</sup>VA. This is the *intended* route, i.e. taking perspective of an attacker, it is more likely to have access to an `.apk` - file than the app's original source code.
     16 + 
     17 +### Build via *Android Studio*
     18 + 
     19 +As this is just the pushed [*Android Studio*](https://developer.android.com/studio) project of E<sup>2</sup>VA, one can just build the app, create own modules, optimize communication between external client and E<sup>2</sup>VA etc.
  • base.apk
    Binary file.
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/.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 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/.idea/.gitignore
     1 +# Default ignored files
     2 +/shelf/
     3 +/workspace.xml
     4 + 
  • ■ ■ ■ ■ ■
    damnvulnerableapp/.idea/.name
     1 +DamnVulnerableApp
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/.idea/compiler.xml
     1 +<?xml version="1.0" encoding="UTF-8"?>
     2 +<project version="4">
     3 + <component name="CompilerConfiguration">
     4 + <bytecodeTargetLevel target="11" />
     5 + </component>
     6 +</project>
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/.idea/gradle.xml
     1 +<?xml version="1.0" encoding="UTF-8"?>
     2 +<project version="4">
     3 + <component name="GradleMigrationSettings" migrationVersion="1" />
     4 + <component name="GradleSettings">
     5 + <option name="linkedExternalProjectsSettings">
     6 + <GradleProjectSettings>
     7 + <option name="testRunner" value="GRADLE" />
     8 + <option name="distributionType" value="DEFAULT_WRAPPED" />
     9 + <option name="externalProjectPath" value="$PROJECT_DIR$" />
     10 + <option name="modules">
     11 + <set>
     12 + <option value="$PROJECT_DIR$" />
     13 + <option value="$PROJECT_DIR$/app" />
     14 + </set>
     15 + </option>
     16 + </GradleProjectSettings>
     17 + </option>
     18 + </component>
     19 +</project>
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/.idea/misc.xml
     1 +<?xml version="1.0" encoding="UTF-8"?>
     2 +<project version="4">
     3 + <component name="DesignSurface">
     4 + <option name="filePathToZoomLevelMap">
     5 + <map>
     6 + <entry key="app/src/main/res/xml/internal_server_configuration.xml" value="0.3663949275362319" />
     7 + <entry key="app/src/main/res/xml/test_configuration.xml" value="0.3703125" />
     8 + </map>
     9 + </option>
     10 + </component>
     11 + <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
     12 + <output url="file://$PROJECT_DIR$/build/classes" />
     13 + </component>
     14 + <component name="ProjectType">
     15 + <option name="id" value="Android" />
     16 + </component>
     17 +</project>
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/.idea/vcs.xml
     1 +<?xml version="1.0" encoding="UTF-8"?>
     2 +<project version="4">
     3 + <component name="VcsDirectoryMappings">
     4 + <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
     5 + </component>
     6 +</project>
  • ■ ■ ■ ■ ■
    damnvulnerableapp/app/.gitignore
     1 +/build
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/build.gradle
     1 +plugins {
     2 + id 'com.android.application'
     3 +}
     4 + 
     5 +android {
     6 + compileSdk 32
     7 + 
     8 + defaultConfig {
     9 + applicationId "com.damnvulnerableapp"
     10 + minSdk 26
     11 + targetSdk 32
     12 + versionCode 1
     13 + versionName "1.0"
     14 + 
     15 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     16 + externalNativeBuild {
     17 + cmake {
     18 + cppFlags ''
     19 + }
     20 + }
     21 + }
     22 + 
     23 + buildTypes {
     24 + release {
     25 + minifyEnabled false
     26 + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
     27 + }
     28 + }
     29 + compileOptions {
     30 + sourceCompatibility JavaVersion.VERSION_1_8
     31 + targetCompatibility JavaVersion.VERSION_1_8
     32 + }
     33 + externalNativeBuild {
     34 + cmake {
     35 + path file('src/main/cpp/CMakeLists.txt')
     36 + version '3.18.1'
     37 + }
     38 + }
     39 + buildFeatures {
     40 + viewBinding true
     41 + }
     42 +}
     43 + 
     44 +dependencies {
     45 + 
     46 + implementation 'androidx.appcompat:appcompat:1.4.2'
     47 + implementation 'com.google.android.material:material:1.6.1'
     48 + implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
     49 + testImplementation 'junit:junit:4.13.2'
     50 + androidTestImplementation 'androidx.test.ext:junit:1.1.3'
     51 + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
     52 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/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
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/androidTest/java/com/damnvulnerableapp/TestManagerActivityOneLife.java
     1 +package com.damnvulnerableapp;
     2 + 
     3 +import static org.junit.Assert.assertArrayEquals;
     4 +import static org.junit.Assert.assertEquals;
     5 +import static org.junit.Assert.assertFalse;
     6 +import static org.junit.Assert.assertNull;
     7 +import static org.junit.Assert.assertTrue;
     8 +import static org.junit.Assert.fail;
     9 + 
     10 +import android.util.Log;
     11 + 
     12 +import androidx.test.ext.junit.rules.ActivityScenarioRule;
     13 +import androidx.test.ext.junit.runners.AndroidJUnit4;
     14 + 
     15 +import com.damnvulnerableapp.helpers.ExternalClient;
     16 +import com.damnvulnerableapp.managerservice.ManagerActivity;
     17 +import com.damnvulnerableapp.networking.exceptions.TimeoutException;
     18 +import com.damnvulnerableapp.networking.messages.Operation;
     19 +import com.damnvulnerableapp.networking.messages.Parameter;
     20 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     21 + 
     22 +import org.junit.After;
     23 +import org.junit.Before;
     24 +import org.junit.Rule;
     25 +import org.junit.Test;
     26 +import org.junit.runner.RunWith;
     27 + 
     28 +/**
     29 + * App is started once. Client connection/disconnect per test case. Expected: no app crashes! ONE LIFE
     30 + * */
     31 +@RunWith(AndroidJUnit4.class)
     32 +public class TestManagerActivityOneLife {
     33 + 
     34 + private static final int SETUP_DELAY = 2000;
     35 + 
     36 + private static final String HOST = "127.0.0.1";
     37 + private static final int PORT = 8080;
     38 + private static final int TIMEOUT = 3000;
     39 + 
     40 + private static final String MODULE_NAME = "StackBufferOverflowModule";
     41 + private static final String DEFAULT_STRING = "test123321TEST";
     42 + 
     43 + private static final int AMOUNT_REOPENS = 10;
     44 + 
     45 + private ExternalClient client;
     46 + 
     47 + @Rule
     48 + public ActivityScenarioRule<ManagerActivity> activityScenarioRule = new ActivityScenarioRule<>(ManagerActivity.class);
     49 + 
     50 + @Before
     51 + public void setup() throws Exception {
     52 + // All tests share THE SAME ACTIVITY!!! I.e. one client may be able to fully use the app,
     53 + // but switching clients in between takes some time!
     54 + Thread.sleep(SETUP_DELAY);
     55 + 
     56 + this.client = new ExternalClient(HOST, PORT, TIMEOUT);
     57 + }
     58 + 
     59 + @After
     60 + public void close() {
     61 + 
     62 + if (this.client != null)
     63 + this.client.disconnect();
     64 + }
     65 + 
     66 + @Test
     67 + public void When_RunningModule_Expect_Success() throws Exception {
     68 + 
     69 + assertTrue(this.client.isConnected());
     70 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     71 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     72 + assertEquals(Operation.SUCCESS, this.client.forward(DEFAULT_STRING).getOperation());
     73 + 
     74 + final PlainMessage modified = this.client.fetch();
     75 + assertEquals(Operation.SUCCESS, modified.getOperation());
     76 + assertArrayEquals(DEFAULT_STRING.toUpperCase().getBytes(), modified.getParameters().get(Parameter.CONTENT)); // <- what if module changes?
     77 + 
     78 + assertEquals(Operation.SUCCESS, this.client.exitReceive().getOperation());
     79 + }
     80 + 
     81 + @Test
     82 + public void When_SimpleConnect_Expect_Success() {
     83 + 
     84 + assertTrue(this.client.isConnected());
     85 + this.client.disconnect();
     86 + assertFalse(this.client.isConnected());
     87 + }
     88 + 
     89 + @Test
     90 + public void When_SimpleConnectSelect_Expect_Success() throws Exception {
     91 + 
     92 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     93 + }
     94 + 
     95 + @Test
     96 + public void When_SimpleConnectExit_Expect_Invalid() throws Exception {
     97 + 
     98 + assertEquals(Operation.INVALID, this.client.exitReceive().getOperation());
     99 + }
     100 + 
     101 + @Test
     102 + public void When_SimpleConnectForward_Expect_Invalid() throws Exception {
     103 + 
     104 + assertEquals(Operation.INVALID, this.client.forward(DEFAULT_STRING).getOperation());
     105 + }
     106 + 
     107 + @Test
     108 + public void When_SimpleConnectFetch_Expect_Invalid() throws Exception {
     109 + assertEquals(Operation.INVALID, this.client.fetch().getOperation());
     110 + }
     111 + 
     112 + @Test
     113 + public void When_DoubleSelect_Expect_Invalid() throws Exception {
     114 + 
     115 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     116 + assertEquals(Operation.INVALID, this.client.select(MODULE_NAME).getOperation());
     117 + }
     118 + 
     119 + // NOTE: Specific to vulnerable module!
     120 + @Test
     121 + public void When_SelectedDoubleFetch_Expect_TimeoutException() throws Exception {
     122 + 
     123 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     124 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     125 + 
     126 + try {
     127 + this.client.fetch();
     128 + } catch (TimeoutException e) {
     129 + return;
     130 + }
     131 + fail();
     132 + }
     133 + 
     134 + // NOTE: Specific to vulnerable module!
     135 + @Test
     136 + public void When_SelectedMultipleForwardFetch_Expect_Success() throws Exception {
     137 + 
     138 + //assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     139 + Log.i(this.getClass().getSimpleName(), this.client.select(MODULE_NAME).toString());
     140 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     141 + 
     142 + assertEquals(Operation.SUCCESS, this.client.forward(DEFAULT_STRING).getOperation());
     143 + assertEquals(Operation.SUCCESS, this.client.forward(DEFAULT_STRING).getOperation());
     144 + assertEquals(Operation.SUCCESS, this.client.forward(DEFAULT_STRING).getOperation());
     145 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     146 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     147 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     148 + }
     149 + 
     150 + @Test
     151 + public void When_MultipleSelectExit_Expect_Success() throws Exception {
     152 + 
     153 + for (int i = 0; i < AMOUNT_REOPENS; i++) {
     154 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     155 + assertEquals(Operation.SUCCESS, this.client.exitReceive().getOperation());
     156 + }
     157 + }
     158 + 
     159 + @Test
     160 + public void When_TwoClientsConnect_Expect_Success() throws Exception {
     161 + 
     162 + final ExternalClient second = new ExternalClient(HOST, PORT, TIMEOUT);
     163 + assertTrue(this.client.isConnected());
     164 + 
     165 + assertNull(second.select(MODULE_NAME));
     166 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     167 + 
     168 + second.disconnect();
     169 + }
     170 +}
     171 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/androidTest/java/com/damnvulnerableapp/TestManagerActivityOneLifeRaw.java
     1 +package com.damnvulnerableapp;
     2 + 
     3 +import static org.junit.Assert.assertTrue;
     4 + 
     5 +import androidx.test.ext.junit.rules.ActivityScenarioRule;
     6 +import androidx.test.ext.junit.runners.AndroidJUnit4;
     7 + 
     8 +import com.damnvulnerableapp.helpers.RawExternalClient;
     9 +import com.damnvulnerableapp.managerservice.ManagerActivity;
     10 +import com.damnvulnerableapp.networking.messages.Operation;
     11 + 
     12 +import org.junit.After;
     13 +import org.junit.Before;
     14 +import org.junit.Rule;
     15 +import org.junit.Test;
     16 +import org.junit.runner.RunWith;
     17 + 
     18 +@RunWith(AndroidJUnit4.class)
     19 +public class TestManagerActivityOneLifeRaw {
     20 + 
     21 + private static final int SETUP_DELAY = 1000;
     22 + 
     23 + private static final String HOST = "127.0.0.1";
     24 + private static final int PORT = 8080;
     25 + private static final int TIMEOUT = 3000;
     26 + 
     27 + private static final String MODULE_NAME = "StackBufferOverflowModule";
     28 + 
     29 + private RawExternalClient client;
     30 + 
     31 + @Rule
     32 + public ActivityScenarioRule<ManagerActivity> activityScenarioRule = new ActivityScenarioRule<>(ManagerActivity.class);
     33 + 
     34 + @Before
     35 + public void setup() throws Exception {
     36 + // All tests share THE SAME ACTIVITY!!!
     37 + Thread.sleep(SETUP_DELAY);
     38 + 
     39 + this.client = new RawExternalClient(HOST, PORT, TIMEOUT);
     40 + }
     41 + 
     42 + @After
     43 + public void close() throws Exception {
     44 + if (this.client != null)
     45 + this.client.disconnect();
     46 + }
     47 + 
     48 + @Test
     49 + public void When_UnknownOperation_Expect_Invalid() throws Exception {
     50 + 
     51 + client.send("CONTENT " + "SLCT" + " CONTENT " + MODULE_NAME);
     52 + assertTrue(client.receive().contains(Operation.INVALID.toString()));
     53 + }
     54 + 
     55 + @Test
     56 + public void When_SelectedUnknownOperation_Expect_Invalid() throws Exception {
     57 + 
     58 + client.send("CONTENT SELECT CONTENT " + MODULE_NAME);
     59 + assertTrue(client.receive().contains(Operation.SUCCESS.toString()));
     60 + client.send("CONTENT " + "SLCT" + " CONTENT " + MODULE_NAME);
     61 + assertTrue(client.receive().contains(Operation.INVALID.toString()));
     62 + }
     63 +}
     64 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/androidTest/java/com/damnvulnerableapp/TestVulnerableActivity.java
     1 +package com.damnvulnerableapp;
     2 + 
     3 +import static org.junit.Assert.assertEquals;
     4 +import static org.junit.Assert.assertFalse;
     5 +import static org.junit.Assert.assertTrue;
     6 +import static org.junit.Assert.fail;
     7 + 
     8 +import android.app.ActivityManager;
     9 +import android.content.Context;
     10 +import android.content.Intent;
     11 +import android.os.Process;
     12 +import android.util.Log;
     13 + 
     14 +import androidx.test.core.app.ActivityScenario;
     15 +import androidx.test.core.app.ApplicationProvider;
     16 + 
     17 +import com.damnvulnerableapp.helpers.ExternalClient;
     18 +import com.damnvulnerableapp.managerservice.ManagerGlobals;
     19 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     20 +import com.damnvulnerableapp.networking.exceptions.TimeoutException;
     21 +import com.damnvulnerableapp.networking.messages.Operation;
     22 +import com.damnvulnerableapp.networking.messages.Parameter;
     23 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     24 +import com.damnvulnerableapp.vulnerable.VulnerableActivity;
     25 +import com.damnvulnerableapp.vulnerable.VulnerableGlobals;
     26 + 
     27 +import org.junit.After;
     28 +import org.junit.Before;
     29 +import org.junit.Test;
     30 + 
     31 +import java.util.List;
     32 + 
     33 +public class TestVulnerableActivity {
     34 + 
     35 + private static final String PROCESS_NAME = "com.damnvulnerableapp:VulnerableActivity";
     36 + 
     37 + private static final int SETUP_DELAY = 2000;
     38 + private static final int KILL_DELAY = 2000;
     39 + 
     40 + private static final String HOST = VulnerableGlobals.HOST;
     41 + private static final int PORT = VulnerableGlobals.PORT;
     42 + private static final int TIMEOUT = 3000;
     43 + 
     44 + private static final String MODULE_NAME = "StackBufferOverflowModule";
     45 + private static final String DEFAULT_STRING = "test123321TEST";
     46 + 
     47 + private ExternalClient client;
     48 + 
     49 + private ActivityScenario<VulnerableActivity> scenario;
     50 + 
     51 + @Before
     52 + public void setup() throws Exception {
     53 + // All tests share THE SAME ACTIVITY!!! I.e. one client may be able to fully use the app,
     54 + // but switching clients in between takes some time!
     55 + final Intent intent = new Intent(ApplicationProvider.getApplicationContext(), VulnerableActivity.class);
     56 + intent.putExtra(ManagerGlobals.FACTORY_INTENT_KEY, NetworkFactory.class.getSimpleName());
     57 + intent.putExtra(ManagerGlobals.MODULE_INTENT_KEY, MODULE_NAME);
     58 + 
     59 + new Thread(() -> this.scenario = ActivityScenario.launch(intent)).start();
     60 + 
     61 + Thread.sleep(SETUP_DELAY);
     62 + 
     63 + this.client = new ExternalClient(HOST, PORT, TIMEOUT);
     64 + }
     65 + 
     66 + @After
     67 + public void close() {
     68 + 
     69 + // Kill remainders of app...this is REALLY bad style, but whelp...
     70 + int pid = this.getPidByProcessName(PROCESS_NAME);
     71 + if (pid != -1) {
     72 + Process.killProcess(pid);
     73 + while (this.isProcessAlive(pid));
     74 + }
     75 + 
     76 + if (this.client != null)
     77 + this.client.disconnect();
     78 + 
     79 + if (this.scenario != null)
     80 + this.scenario.close();
     81 + }
     82 + 
     83 + @Test
     84 + public void When_SimpleConnecting_Expect_Success() {
     85 + 
     86 + assertTrue(this.client.isConnected());
     87 + this.client.disconnect();
     88 + assertFalse(this.client.isConnected());
     89 + }
     90 + 
     91 + @Test
     92 + public void When_FinishingModuleExit_Expect_Success() throws Exception {
     93 + 
     94 + final PlainMessage initialMessage = this.client.select(MODULE_NAME);
     95 + int pid = Integer.parseInt(new String(initialMessage.getParameters().get(Parameter.CONTENT)));
     96 + assertEquals(Operation.SUCCESS, initialMessage.getOperation());
     97 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     98 + assertEquals(Operation.SUCCESS, this.client.forward("EXIT").getOperation());
     99 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     100 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     101 + assertTrue(this.client.isConnected());
     102 + this.client.exit();
     103 + 
     104 + Thread.sleep(KILL_DELAY);
     105 + assertFalse(this.isProcessAlive(pid));
     106 + }
     107 + 
     108 + @Test
     109 + public void When_FetchingEmpty_Expect_TimeoutException() throws Exception {
     110 + 
     111 + assertEquals(Operation.SUCCESS, this.client.select(MODULE_NAME).getOperation());
     112 + assertEquals(Operation.SUCCESS, this.client.fetch().getOperation());
     113 + 
     114 + try {
     115 + this.client.fetch();
     116 + } catch (TimeoutException e) {
     117 + return;
     118 + }
     119 + fail();
     120 + }
     121 + 
     122 + @Test
     123 + public void When_InstantExit_Expect_Success() throws Exception {
     124 + 
     125 + final PlainMessage initialMessage = this.client.select(MODULE_NAME);
     126 + int pid = Integer.parseInt(new String(initialMessage.getParameters().get(Parameter.CONTENT)));
     127 + assertEquals(Operation.SUCCESS, initialMessage.getOperation());
     128 + this.client.exit();
     129 + 
     130 + Thread.sleep(KILL_DELAY);
     131 + assertFalse(this.isProcessAlive(pid));
     132 + }
     133 + 
     134 + @Test
     135 + public void When_DoubleSelect_Expect_Invalid() throws Exception {
     136 + 
     137 + final PlainMessage message = this.client.select(MODULE_NAME);
     138 + int pid = Integer.parseInt(new String(message.getParameters().get(Parameter.CONTENT)));
     139 + assertEquals(Operation.SUCCESS, message.getOperation());
     140 + assertEquals(Operation.INVALID, this.client.select(MODULE_NAME).getOperation());
     141 + 
     142 + this.client.exit();
     143 + 
     144 + Thread.sleep(KILL_DELAY);
     145 + assertFalse(this.isProcessAlive(pid));
     146 + }
     147 + 
     148 + @Test
     149 + public void When_InstantForward_Expect_Invalid() throws Exception {
     150 + 
     151 + final PlainMessage message = this.client.forward(DEFAULT_STRING);
     152 + Log.e(this.getClass().getSimpleName(), message.toString());
     153 + assertEquals(Operation.INVALID, message.getOperation());
     154 + this.client.exit();
     155 + }
     156 + 
     157 + @Test
     158 + public void When_InstantFetch_Expect_Invalid() throws Exception {
     159 + 
     160 + assertEquals(Operation.INVALID, this.client.fetch().getOperation());
     161 + this.client.exit();
     162 + }
     163 + 
     164 + private boolean isProcessAlive(int pid) {
     165 + 
     166 + final ActivityManager manager = (ActivityManager) ApplicationProvider.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
     167 + final List<ActivityManager.RunningAppProcessInfo> procs = manager.getRunningAppProcesses();
     168 + if (procs != null) {
     169 + for (final ActivityManager.RunningAppProcessInfo info : procs) {
     170 + if (info.pid == pid)
     171 + return true;
     172 + }
     173 + }
     174 + 
     175 + return false;
     176 + }
     177 + 
     178 + private int getPidByProcessName(String processName) {
     179 + 
     180 + final ActivityManager manager = (ActivityManager) ApplicationProvider.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
     181 + final List<ActivityManager.RunningAppProcessInfo> procs = manager.getRunningAppProcesses();
     182 + if (procs != null) {
     183 + for (final ActivityManager.RunningAppProcessInfo info : procs) {
     184 + if (info.processName.equals(processName))
     185 + return info.pid;
     186 + }
     187 + }
     188 + return -1;
     189 + }
     190 +}
     191 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/androidTest/java/com/damnvulnerableapp/helpers/ExternalClient.java
     1 +package com.damnvulnerableapp.helpers;
     2 + 
     3 +import com.damnvulnerableapp.common.configuration.ClientExitConfiguration;
     4 +import com.damnvulnerableapp.networking.communication.CommunicationFactory;
     5 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     6 +import com.damnvulnerableapp.networking.communication.client.Client;
     7 +import com.damnvulnerableapp.networking.communication.client.NetworkConnectionInformation;
     8 +import com.damnvulnerableapp.networking.messages.Operation;
     9 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     10 +import com.damnvulnerableapp.networking.protocol.PlainProtocolFactory;
     11 + 
     12 +/**
     13 + * Used for testing CORRECT communication, i.e. no unexpected disconnects, double receives etc.
     14 + * */
     15 +public class ExternalClient {
     16 + 
     17 + private final Client client;
     18 + 
     19 + public ExternalClient(String host, int port, int timeout) throws Exception {
     20 + 
     21 + final CommunicationFactory factory = new NetworkFactory();
     22 + factory.setProtocolFactory(new PlainProtocolFactory());
     23 + this.client = factory.createClient();
     24 + this.client.connect(new NetworkConnectionInformation(host, port, timeout));
     25 + }
     26 + 
     27 + public void disconnect() {
     28 + this.client.setConfiguration(new ClientExitConfiguration());
     29 + this.client.disconnect();
     30 + }
     31 + 
     32 + public PlainMessage select(String moduleName) throws Exception {
     33 + 
     34 + this.client.send(new PlainMessage(
     35 + Operation.SELECT,
     36 + moduleName
     37 + ));
     38 + return (PlainMessage) this.client.receive();
     39 + }
     40 + 
     41 + public void exit() throws Exception {
     42 + 
     43 + this.client.send(new PlainMessage(
     44 + Operation.EXIT,
     45 + ""
     46 + ));
     47 + //return (PlainMessage) this.client.receive();
     48 + }
     49 + 
     50 + public PlainMessage exitReceive() throws Exception {
     51 + 
     52 + this.client.send(new PlainMessage(
     53 + Operation.EXIT,
     54 + ""
     55 + ));
     56 + return (PlainMessage) this.client.receive();
     57 + }
     58 + 
     59 + public PlainMessage shutdown() throws Exception {
     60 + 
     61 + this.client.send(new PlainMessage(
     62 + Operation.SHUTDOWN,
     63 + ""
     64 + ));
     65 + return (PlainMessage) this.client.receive();
     66 + }
     67 + 
     68 + public PlainMessage forward(String content) throws Exception {
     69 + 
     70 + this.client.send(new PlainMessage(
     71 + Operation.FORWARD,
     72 + content
     73 + ));
     74 + return (PlainMessage) this.client.receive();
     75 + }
     76 + 
     77 + public PlainMessage fetch() throws Exception {
     78 + 
     79 + this.client.send(new PlainMessage(
     80 + Operation.FETCH,
     81 + ""
     82 + ));
     83 + return (PlainMessage) this.client.receive();
     84 + }
     85 + 
     86 + public boolean isConnected() {
     87 + return this.client.isConnected();
     88 + }
     89 +}
     90 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/androidTest/java/com/damnvulnerableapp/helpers/RawExternalClient.java
     1 +package com.damnvulnerableapp.helpers;
     2 + 
     3 +import com.damnvulnerableapp.networking.communication.client.ClientType;
     4 +import com.damnvulnerableapp.networking.communication.client.NetworkEndPoint;
     5 +import com.damnvulnerableapp.networking.protocol.PlainClientProtocol;
     6 + 
     7 +import java.net.InetSocketAddress;
     8 +import java.net.Socket;
     9 + 
     10 +public final class RawExternalClient {
     11 + 
     12 + private final NetworkEndPoint endpoint;
     13 + private final PlainClientProtocol protocol;
     14 + 
     15 + public RawExternalClient(String host, int port, int timeout) throws Exception {
     16 + 
     17 + final Socket socket = new Socket();
     18 + socket.setSoTimeout(timeout);
     19 + socket.connect(new InetSocketAddress(host, port), timeout);
     20 + this.endpoint = new NetworkEndPoint(socket);
     21 + 
     22 + this.protocol = new PlainClientProtocol();
     23 + this.protocol.handshake(this.endpoint, ClientType.USER);
     24 + }
     25 + 
     26 + public boolean isConnected() {
     27 + return this.endpoint.isConnected();
     28 + }
     29 + 
     30 + public void disconnect() throws Exception {
     31 + 
     32 + this.protocol.shutdown(this.endpoint);
     33 + this.endpoint.disconnect();
     34 + }
     35 + 
     36 + public void send(String message) throws Exception {
     37 + this.endpoint.send(message.getBytes());
     38 + }
     39 + 
     40 + public String receive() throws Exception {
     41 + return new String(this.endpoint.receive());
     42 + }
     43 +}
     44 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/AndroidManifest.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     3 + package="com.damnvulnerableapp">
     4 + 
     5 + <uses-permission android:name="android.permission.INTERNET"/>
     6 + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     7 + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
     8 + 
     9 + 
     10 + <application
     11 + android:allowBackup="true"
     12 + android:icon="@mipmap/ic_launcher"
     13 + android:label="@string/app_name"
     14 + android:roundIcon="@mipmap/ic_launcher_round"
     15 + android:supportsRtl="true"
     16 + android:theme="@style/Theme.DamnVulnerableApp">
     17 + <activity
     18 + android:name=".managerservice.ManagerActivity"
     19 + android:exported="true">
     20 + <intent-filter>
     21 + <action android:name="android.intent.action.MAIN" />
     22 + 
     23 + <category android:name="android.intent.category.LAUNCHER" />
     24 + </intent-filter>
     25 + </activity>
     26 + 
     27 + <service android:name=".managerservice.ManagerService">
     28 + 
     29 + </service>
     30 + 
     31 + <activity android:name=".vulnerable.VulnerableActivity"
     32 + android:process=":VulnerableActivity"
     33 + android:exported="true">
     34 + </activity>
     35 + </application>
     36 + 
     37 +</manifest>
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/CMakeLists.txt
     1 +# For more information about using CMake with Android Studio, read the
     2 +# documentation: https://d.android.com/studio/projects/add-native-code.html
     3 + 
     4 +# Sets the minimum version of CMake required to build the native library.
     5 + 
     6 +cmake_minimum_required(VERSION 3.18.1)
     7 + 
     8 +# Declares and names the project.
     9 + 
     10 +project("damnvulnerableapp")
     11 + 
     12 +# Creates and names a library, sets it as either STATIC
     13 +# or SHARED, and provides the relative paths to its source code.
     14 +# You can define multiple libraries, and CMake builds them for you.
     15 +# Gradle automatically packages shared libraries with your APK.
     16 + 
     17 +add_library( # Sets the name of the library.
     18 + StackBufferOverflowModule
     19 + 
     20 + # Sets the library as a shared library.
     21 + SHARED
     22 + 
     23 + # Provides a relative path to your source file(s).
     24 + modules/StackBufferOverflowModule.c)
     25 + 
     26 +add_library(
     27 + EasyStackBufferOverflowModule
     28 + 
     29 + SHARED
     30 + 
     31 + modules/EasyStackBufferOverflowModule.c
     32 +)
     33 + 
     34 +add_library(
     35 + UseAfterFreeWriteModule
     36 + 
     37 + SHARED
     38 + 
     39 + modules/UseAfterFreeWriteModule.c
     40 +)
     41 + 
     42 +add_library(
     43 + UseAfterFreeExecModule
     44 + 
     45 + SHARED
     46 + 
     47 + modules/UseAfterFreeExecModule.c
     48 +)
     49 + 
     50 +add_library(
     51 + DoubleFreeModule
     52 + 
     53 + SHARED
     54 + 
     55 + modules/DoubleFreeModule.c
     56 +)
     57 + 
     58 +add_library(
     59 + HeapOverflowModule
     60 + 
     61 + SHARED
     62 + 
     63 + modules/HeapOverflowModule.c
     64 +)
     65 + 
     66 +add_library(
     67 + OffByOneModule
     68 + 
     69 + SHARED
     70 + 
     71 + modules/OffByOneModule.c
     72 +)
     73 + 
     74 +add_library(
     75 + SecondaryFakeModule
     76 + 
     77 + SHARED
     78 + 
     79 + modules/SecondaryFakeModule.c
     80 +)
     81 + 
     82 +add_library(
     83 + HeapSCAModule
     84 + 
     85 + SHARED
     86 + 
     87 + modules/HeapSCAModule.c
     88 +)
     89 + 
     90 +add_library(
     91 + PoCPrimaryPoisoning
     92 + 
     93 + SHARED
     94 + 
     95 + modules/PoCPrimaryPoisoning.c
     96 +)
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/DoubleFreeModule.c
     1 +#include <jni.h>
     2 +#include <stdlib.h>
     3 +#include <string.h>
     4 + 
     5 +//
     6 +// Created by kuehnemann on 30.08.22.
     7 +//
     8 + 
     9 +struct ref {
     10 + uint64_t *location;
     11 +};
     12 + 
     13 + 
     14 +JNIEXPORT jbyteArray
     15 +JNICALL
     16 +Java_com_damnvulnerableapp_vulnerable_modules_DoubleFreeModule_leak(
     17 + JNIEnv *env,
     18 + jclass clazz,
     19 + jint index) {
     20 + char *string_table[] = {
     21 + "amazing_key",
     22 + "secret_key",
     23 + "topsecret_key",
     24 + "a_very_very_long_key_with_fancy_features_:D"
     25 + };
     26 + 
     27 + jsize length = strlen(string_table[index]);
     28 + jbyteArray array = (*env)->NewByteArray(env, length);
     29 + 
     30 + // One '&' can ruin the day
     31 + (*env)->SetByteArrayRegion(env, array, 0, length, &string_table[index]);
     32 + 
     33 + return array;
     34 +}
     35 + 
     36 +JNIEXPORT void JNICALL
     37 +Java_com_damnvulnerableapp_vulnerable_modules_DoubleFreeModule_vulnerable(
     38 + JNIEnv *env,
     39 + jclass clazz,
     40 + jbyteArray input) {
     41 + 
     42 + jboolean iscopy;
     43 + char *raw_input = (*env)->GetByteArrayElements(env, input, &iscopy);
     44 + 
     45 + struct ref *first = (char*)calloc(1, sizeof(struct ref));
     46 + struct ref *second = (char*)calloc(1, sizeof(struct ref));
     47 + struct ref *third = (char*)calloc(1, sizeof(struct ref));
     48 + 
     49 + free(first);
     50 + free(second);
     51 + free(first);
     52 + 
     53 + first = (char*)calloc(1, sizeof(struct ref));
     54 + second = (char*)calloc(1, sizeof(struct ref));
     55 + third = (char*)calloc(1, sizeof(struct ref));
     56 + 
     57 + // write - what - where condition!
     58 + // First 8 bytes determine the location to write to
     59 + memcpy(&third->location, raw_input, sizeof(uint64_t*));
     60 + 
     61 + // Then the next 8 bytes determine what to write
     62 + *(first->location) = (uint64_t)*(raw_input + 8);
     63 + 
     64 + free(first);
     65 + free(second);
     66 + // free(third); --> boom
     67 + 
     68 + (*env)->ReleaseByteArrayElements(env, input, raw_input, JNI_ABORT);
     69 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/EasyStackBufferOverflowModule.c
     1 +//
     2 +// Created by kuehnemann on 10.08.22.
     3 +//
     4 + 
     5 +#include <jni.h>
     6 + 
     7 +#include <stdio.h>
     8 +#include <string.h>
     9 +#include <ctype.h>
     10 +#include <malloc.h>
     11 + 
     12 +uint32_t perfect_strlen(const char *str) {
     13 + uint32_t i = 0;
     14 + while (str[i] != 0) i++;
     15 + return i;
     16 +}
     17 + 
     18 +void perfect_memcpy(char *buffer, const char *source, uint32_t length) {
     19 + uint32_t i;
     20 + for (i = 0; i < length; i++)
     21 + buffer[i] = source[i];
     22 +}
     23 + 
     24 +JNIEXPORT jbyteArray
     25 +JNICALL
     26 +Java_com_damnvulnerableapp_vulnerable_modules_EasyStackBufferOverflowModule_vulnerableToUpper(
     27 + JNIEnv *env,
     28 + jobject thiz,
     29 + jbyteArray string,
     30 + jint unknown) {
     31 + 
     32 + char buffer[0x20] = { 0 };
     33 + jbyte *bytes = (*env)->GetByteArrayElements(env, string, NULL);
     34 + jint length = (*env)->GetArrayLength(env, string);
     35 + 
     36 + // Cannot use memcpy et al, because libc FORTIFY would get in our way ...
     37 + // Its actually hard to write insecure code...
     38 + perfect_memcpy(buffer, bytes, length);
     39 + 
     40 + // TO UPPER
     41 + uint32_t i;
     42 + for (i = 0; i < 0x20; i++)
     43 + buffer[i] = toupper(buffer[i]);
     44 + 
     45 + // Ofc. we need the string length of our buffer
     46 + if (unknown <= 0x100)
     47 + length = perfect_strlen(buffer) + unknown;
     48 + else
     49 + length = perfect_strlen(buffer);
     50 + 
     51 + jbyteArray upper = (*env)->NewByteArray(env, length);
     52 + 
     53 + (*env)->SetByteArrayRegion(env, upper, 0, length, buffer);
     54 + return upper;
     55 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/HeapOverflowModule.c
     1 +#include <jni.h>
     2 + 
     3 +#include <stdlib.h>
     4 +#include <stdint.h>
     5 +#include <string.h>
     6 + 
     7 + 
     8 +static uint8_t *alloc = NULL;
     9 + 
     10 +JNIEXPORT jbyteArray JNICALL
     11 +Java_com_damnvulnerableapp_vulnerable_modules_HeapOverflowModule_leak(JNIEnv *env, jobject unused) {
     12 + 
     13 + 
     14 + alloc = malloc(0x10);
     15 + if (alloc) {
     16 + jbyteArray output = (*env)->NewByteArray(env, 16);
     17 + 
     18 + jbyte *user_pointer = (jbyte*)&alloc;
     19 + jbyte *header = (jbyte*)(alloc - 0x10);
     20 + 
     21 + (*env)->SetByteArrayRegion(env, output, 0, 8, user_pointer);
     22 + (*env)->SetByteArrayRegion(env, output, 8, 8, header);
     23 + return output;
     24 + }
     25 + 
     26 + return NULL;
     27 +}
     28 + 
     29 +JNIEXPORT jbyteArray JNICALL
     30 +Java_com_damnvulnerableapp_vulnerable_modules_HeapOverflowModule_processMessage(JNIEnv *env, jobject unused, jbyteArray message) {
     31 + 
     32 + 
     33 + /*jbyte buffer[0x20] = { 0 };
     34 + jbyte *buf = buffer;
     35 + 
     36 + jbyte *array = (*env)->GetByteArrayElements(env, message, NULL);
     37 + jsize length = (*env)->GetArrayLength(env, message);
     38 + jsize i;
     39 + for (i = 0; i < length; i++)
     40 + buf[i] = array[i];
     41 + 
     42 + jbyteArray output;
     43 + if (alloc) {
     44 + free(buf + 0x10);
     45 + alloc = malloc(0x10);
     46 + 
     47 + length = strlen(alloc);
     48 + output = (*env)->NewByteArray(env, length);
     49 + (*env)->SetByteArrayRegion(env, output, 0, length, alloc);
     50 + } else {
     51 + alloc = malloc(0x10);
     52 + 
     53 + output = (*env)->NewByteArray(env, 8);
     54 + (*env)->SetByteArrayRegion(env, output, 0, 8, &buf);
     55 + }
     56 + 
     57 + return output;*/
     58 + 
     59 + jbyteArray ar = (*env)->NewByteArray(env, 0x10);
     60 + uint8_t *tmp = (uint8_t*)malloc(0x10);
     61 + if (tmp) {
     62 + 
     63 + memset(tmp, 0x42, 0x10);
     64 + free(tmp);
     65 + tmp = (uint8_t*)malloc(0x10);
     66 + 
     67 + (*env)->SetByteArrayRegion(env, ar, 0, 0x10, (jbyte*)tmp);
     68 + free(tmp);
     69 + }
     70 + 
     71 + return ar;
     72 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/HeapSCAModule.c
     1 +#include <jni.h>
     2 + 
     3 +#include <stdint.h>
     4 +#include <malloc.h>
     5 +#include <time.h>
     6 + 
     7 +#define NS_PER_SECOND 1000000000
     8 + 
     9 + 
     10 +JNIEXPORT jbyteArray JNICALL
     11 +Java_com_damnvulnerableapp_vulnerable_modules_HeapSCAModule_handleMessage(JNIEnv *env,
     12 + jclass class,
     13 + jbyteArray message) {
     14 + 
     15 + uint32_t length = (*env)->GetArrayLength(env, message);
     16 + if (length == 0)
     17 + return NULL;
     18 + 
     19 + jbyte *raw = (*env)->GetByteArrayElements(env, message, NULL);
     20 + if (raw) {
     21 + 
     22 + struct timespec before = { 0 };
     23 + struct timespec after = { 0 };
     24 + uint64_t elapsed;
     25 + 
     26 + jbyteArray result = (*env)->NewByteArray(env, 16);
     27 + switch (raw[0]) {
     28 + case 0: {
     29 + // Malloc
     30 + uint64_t size = *((uint64_t*)&raw[1]);
     31 + uint8_t *ptr;
     32 + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &before);
     33 + ptr = malloc(size);
     34 + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &after);
     35 + elapsed = (after.tv_sec * NS_PER_SECOND + after.tv_nsec) - (before.tv_sec * NS_PER_SECOND + before.tv_nsec);
     36 + (*env)->SetByteArrayRegion(env, result, 0, 8, (jbyte*)&ptr);
     37 + (*env)->SetByteArrayRegion(env, result, 8, 8, (jbyte*)&elapsed);
     38 + break;
     39 + }
     40 + case 1: {
     41 + // Free
     42 + uint8_t *ptr = (uint8_t*)(*(uint64_t*)&raw[1]);
     43 + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &before);
     44 + free(ptr);
     45 + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &after);
     46 + elapsed = (after.tv_sec * NS_PER_SECOND + after.tv_nsec) - (before.tv_sec * NS_PER_SECOND + before.tv_nsec);
     47 + (*env)->SetByteArrayRegion(env, result, 0, 8, (jbyte*)&ptr);
     48 + (*env)->SetByteArrayRegion(env, result, 8, 8, (jbyte*)&elapsed);
     49 + break;
     50 + }
     51 + }
     52 + return result;
     53 + }
     54 + 
     55 + return NULL;
     56 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/OffByOneModule.c
     1 +#include <jni.h>
     2 +#include <stdlib.h>
     3 +#include <string.h>
     4 +#include <ctype.h>
     5 + 
     6 +#include <time.h>
     7 + 
     8 +//
     9 +// Created by kuehnemann on 30.08.22.
     10 +//
     11 + 
     12 +#define BUFFER_SIZE 0x100
     13 + 
     14 +struct logged_data {
     15 + char message[BUFFER_SIZE];
     16 + char* (*filter)(char *message, uint64_t length);
     17 +};
     18 +static struct logged_data *g_logs;
     19 +static uint64_t g_logs_size;
     20 + 
     21 +static char *new_filter(char *message, uint64_t length);
     22 +static char *default_filter(char *message, uint64_t length);
     23 + 
     24 +static void *filters[] = {
     25 + default_filter,
     26 + new_filter
     27 +};
     28 +#define DEFAULT_FILTER 0
     29 +#define NEW_FILTER 1
     30 + 
     31 +JNIEXPORT jbyteArray JNICALL
     32 +Java_com_damnvulnerableapp_vulnerable_modules_OffByOneModule_logMessage(
     33 + JNIEnv *env,
     34 + jclass clazz,
     35 + jbyteArray message) {
     36 + 
     37 + jboolean iscopy;
     38 + jbyte *raw_message = (*env)->GetByteArrayElements(env, message, &iscopy);
     39 + 
     40 + g_logs = (struct logged_data*) realloc(g_logs, ++g_logs_size * sizeof(struct logged_data));
     41 + if (!g_logs)
     42 + return NULL;
     43 + struct logged_data *new = &g_logs[g_logs_size - 1];
     44 + 
     45 + memset(new, 0, sizeof(struct logged_data));
     46 + memcpy(&new->filter, &filters[DEFAULT_FILTER], sizeof(filters[DEFAULT_FILTER]));
     47 + 
     48 + jsize length = (*env)->GetArrayLength(env, message);
     49 + if (length - 1 > BUFFER_SIZE) // off - by - one bug
     50 + length = BUFFER_SIZE - 1;
     51 + memcpy(new->message, raw_message, length);
     52 + 
     53 + // Totally not control flow obfuscation to make filters align on page :)
     54 + struct timespec time;
     55 + if (clock_gettime(CLOCK_REALTIME, &time) != -1) {
     56 + time.tv_nsec += 10;
     57 + }
     58 + 
     59 + jbyte *filtered_message = (jbyte*)new->filter(new->message, length);
     60 + length = (jint)strlen((char*)filtered_message);
     61 + 
     62 + jbyteArray logged_message = (*env)->NewByteArray(env, length);
     63 + (*env)->SetByteArrayRegion(env, logged_message, 0, length, filtered_message);
     64 + (*env)->ReleaseByteArrayElements(env, message, raw_message, JNI_ABORT);
     65 + return logged_message;
     66 +}
     67 + 
     68 +char *new_filter(char *message, uint64_t length) {
     69 + // do weird stuff here
     70 + const char *strings[] = {
     71 + "test",
     72 + "test12321"
     73 + };
     74 + uint8_t index = *((uint8_t*)message);
     75 + return (char*)(&strings[index]);
     76 +}
     77 + 
     78 +char *default_filter(char *message, uint64_t length) {
     79 + // do secure stuff here
     80 + 
     81 + // maybe one buffer overflow, but NOTHING else --> basically useless, unless ....
     82 + char buffer[BUFFER_SIZE >> 1];
     83 + 
     84 + uint64_t i;
     85 + for (i = 0; i < length; i++)
     86 + buffer[i] = message[i];
     87 + return message;
     88 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/PoCPrimaryPoisoning.c
     1 +#include <jni.h>
     2 + 
     3 +#include <stdint.h>
     4 +#include <string.h>
     5 +#include <malloc.h>
     6 + 
     7 +#define BUFFER_SIZE 0x20
     8 + 
     9 +static uint8_t called = 0;
     10 +static uint8_t *buffer = NULL;
     11 + 
     12 +JNIEXPORT jbyteArray JNICALL Java_com_damnvulnerableapp_vulnerable_modules_PoCPrimaryPoisoning_free(
     13 + JNIEnv *env,
     14 + jobject class,
     15 + jbyteArray chunk) {
     16 + 
     17 + if (!called) {
     18 + called++;
     19 + buffer = malloc(BUFFER_SIZE);
     20 + jbyteArray ar = (*env)->NewByteArray(env, 8);
     21 + jbyte *leak = (jbyte*)&buffer;
     22 + (*env)->SetByteArrayRegion(env, ar, 0, 8, leak);
     23 + return ar;
     24 + }
     25 + 
     26 + uint8_t *raw = (uint8_t*)(*env)->GetByteArrayElements(env, chunk, NULL);
     27 + uint32_t length = (*env)->GetArrayLength(env, chunk);
     28 + if (raw) {
     29 + memcpy(buffer, raw, (length <= BUFFER_SIZE) ? length : BUFFER_SIZE);
     30 + 
     31 + // Brings attacker - controlled chunk into primary
     32 + free(buffer + 0x10); // combined header
     33 + 
     34 + uint8_t *new = malloc(0x10);
     35 + jbyteArray output = (*env)->NewByteArray(env, 0x10);
     36 + (*env)->SetByteArrayRegion(env, output, 0, 0x10, new);
     37 + return output;
     38 + }
     39 + return NULL;
     40 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/SecondaryFakeModule.c
     1 +#include <jni.h>
     2 + 
     3 +#include <stdint.h>
     4 +#include <stdlib.h>
     5 +#include <string.h>
     6 + 
     7 + 
     8 +#define BUFFER_SIZE 0x100
     9 + 
     10 +static uint8_t called = 0;
     11 + 
     12 +JNIEXPORT jbyteArray JNICALL
     13 +Java_com_damnvulnerableapp_vulnerable_modules_SecondaryFakeModule_free(JNIEnv *env,
     14 + jclass clazz,
     15 + jbyteArray chunk) {
     16 + uint8_t buffer[BUFFER_SIZE] = { 0 };
     17 + if (!called) {
     18 + called++;
     19 + jbyteArray ar = (*env)->NewByteArray(env, 8);
     20 + jbyte *leak = (jbyte*)&buffer;
     21 + (*env)->SetByteArrayRegion(env, ar, 0, 8, &leak);
     22 + return ar;
     23 + }
     24 + 
     25 + uint8_t *raw = (uint8_t*)(*env)->GetByteArrayElements(env, chunk, NULL);
     26 + uint32_t length = (*env)->GetArrayLength(env, chunk);
     27 + if (raw) {
     28 + memcpy(buffer, raw, (length <= BUFFER_SIZE) ? length : BUFFER_SIZE);
     29 + 
     30 + // Brings attacker - controlled chunk into secondary cache
     31 + free(buffer + 0x30 + 0x10); // large header + combined header
     32 + 
     33 + // Triggers potential write - what - where condition. This could also be triggered by another
     34 + // thread, although it might be problematic what that thread will write and how much...
     35 + uint8_t *write_trigger = malloc(length - 0x40);
     36 + memcpy(write_trigger, raw + 0x40, length - 0x40);
     37 + free(write_trigger);
     38 + }
     39 + return NULL;
     40 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/StackBufferOverflowModule.c
     1 +//
     2 +// Created by kuehnemann on 08.08.22.
     3 +//
     4 + 
     5 +#include <jni.h>
     6 + 
     7 +#include <string.h>
     8 +#include <ctype.h>
     9 + 
     10 +uint32_t perfect_strlen(const char *str) {
     11 + uint32_t i = 0;
     12 + while (str[i] != 0) i++;
     13 + return i;
     14 +}
     15 + 
     16 +void perfect_memcpy(char *buffer, const char *source, uint32_t length) {
     17 + uint32_t i;
     18 + for (i = 0; i < length; i++)
     19 + buffer[i] = source[i];
     20 +}
     21 + 
     22 + 
     23 +JNIEXPORT jbyteArray
     24 +JNICALL
     25 +Java_com_damnvulnerableapp_vulnerable_modules_StackBufferOverflowModule_vulnerableToUpper(
     26 + JNIEnv *env,
     27 + jobject thiz,
     28 + jbyteArray string) {
     29 + 
     30 + char buffer[0x20] = { 0 };
     31 + jbyte *bytes = (*env)->GetByteArrayElements(env, string, NULL);
     32 + jint length = (*env)->GetArrayLength(env, string);
     33 + 
     34 + // Cannot use memcpy et al, because libc FORTIFY would get in our way ...
     35 + // Its actually hard to write insecure code...
     36 + perfect_memcpy(buffer, bytes, length);
     37 + 
     38 + // TO UPPER
     39 + uint32_t i;
     40 + for (i = 0; i < 0x20; i++)
     41 + buffer[i] = toupper(buffer[i]);
     42 + 
     43 + // Ofc. we need the string length of our buffer
     44 + length = perfect_strlen(buffer);
     45 + 
     46 + jbyteArray upper = (*env)->NewByteArray(env, length);
     47 + (*env)->SetByteArrayRegion(env, upper, 0, length, buffer);
     48 + return upper;
     49 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/UseAfterFreeExecModule.c
     1 +//
     2 +// Created by kuehnemann on 23.08.22.
     3 +//
     4 + 
     5 +#include <stdint.h>
     6 +#include <jni.h>
     7 +#include <malloc.h>
     8 +#include <string.h>
     9 + 
     10 +struct object {
     11 + char key[256];
     12 + uint64_t value;
     13 +};
     14 + 
     15 +struct manager {
     16 + char *values[32];
     17 + char* (*make_printable)(const char *key, const char *debug);
     18 +};
     19 + 
     20 +static char *make_printable(const char *key, const char *debug) {
     21 + return "TODO: Implement!";
     22 +}
     23 + 
     24 +JNIEXPORT jbyteArray JNICALL
     25 +Java_com_damnvulnerableapp_vulnerable_modules_UseAfterFreeExecModule_lookupExamples(
     26 + JNIEnv * env,
     27 + jobject thiz,
     28 + jint index) {
     29 + 
     30 + char *string_table[] = {
     31 + "amazing_key",
     32 + "secret_key",
     33 + "topsecret_key",
     34 + "a_very_very_long_key_with_fancy_features_:D"
     35 + };
     36 + 
     37 + jsize length = strlen(string_table[index]);
     38 + jbyteArray array = (*env)->NewByteArray(env, length);
     39 + 
     40 + // One '&' can ruin the day
     41 + (*env)->SetByteArrayRegion(env, array, 0, length, &string_table[index]);
     42 + 
     43 + return array;
     44 +}
     45 + 
     46 +JNIEXPORT jbyteArray JNICALL
     47 +Java_com_damnvulnerableapp_vulnerable_modules_UseAfterFreeExecModule_storePair(
     48 + JNIEnv *env,
     49 + jobject thiz,
     50 + jbyteArray key,
     51 + jlong value) {
     52 + 
     53 + struct manager *m = (struct manager*)malloc(sizeof(struct manager));
     54 + m->make_printable = make_printable;
     55 + free(m);
     56 + 
     57 + // Allocate memory for key - value pair
     58 + struct object *obj = (struct object*)calloc(1, sizeof(struct object));
     59 + uint32_t length = (*env)->GetArrayLength(env, key);
     60 + if (length > 256)
     61 + length = 256;
     62 + 
     63 + // Store key - value pair
     64 + jboolean iscopy = JNI_FALSE;
     65 + jbyte *key_raw = (*env)->GetByteArrayElements(env, key, &iscopy);
     66 + memcpy(obj->key, key_raw, length);
     67 + obj->value = value;
     68 + 
     69 + // Finally show stored key - value pairs
     70 + uint64_t result = m->make_printable(obj->key, NULL);
     71 + uint8_t *output = &result;
     72 + jsize output_length = strlen(output);
     73 + jbyteArray array = (*env)->NewByteArray(env, output_length);
     74 + (*env)->SetByteArrayRegion(env, array, 0, output_length, output);
     75 + 
     76 + (*env)->ReleaseByteArrayElements(env, key, key_raw, JNI_ABORT);
     77 + free(obj);
     78 + 
     79 + return array;
     80 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/cpp/modules/UseAfterFreeWriteModule.c
     1 +//
     2 +// Created by kuehnemann on 17.08.22.
     3 +//
     4 + 
     5 +#include <stdint.h>
     6 +#include <jni.h>
     7 +#include <malloc.h>
     8 +#include <string.h>
     9 + 
     10 +struct object {
     11 + char key[256];
     12 + uint64_t value;
     13 +};
     14 + 
     15 +struct manager {
     16 + uint64_t *values[32];
     17 + uint64_t id;
     18 +};
     19 + 
     20 +JNIEXPORT jbyteArray JNICALL
     21 +Java_com_damnvulnerableapp_vulnerable_modules_UseAfterFreeWriteModule_lookupExamples(
     22 + JNIEnv * env,
     23 + jobject thiz,
     24 + jint index) {
     25 + 
     26 + char *string_table[] = {
     27 + "amazing_key",
     28 + "secret_key",
     29 + "topsecret_key",
     30 + "a_very_very_long_key_with_fancy_features_:D"
     31 + };
     32 + 
     33 + jsize length = strlen(string_table[index]);
     34 + jbyteArray array = (*env)->NewByteArray(env, length);
     35 + 
     36 + // One '&' can ruin the day
     37 + (*env)->SetByteArrayRegion(env, array, 0, length, &string_table[index]);
     38 + 
     39 + return array;
     40 +}
     41 + 
     42 +JNIEXPORT void JNICALL
     43 +Java_com_damnvulnerableapp_vulnerable_modules_UseAfterFreeWriteModule_storePair(
     44 + JNIEnv *env,
     45 + jobject thiz,
     46 + jbyteArray key,
     47 + jlong value) {
     48 + 
     49 + struct manager *m = (struct manager*)malloc(sizeof(struct manager));
     50 + free(m);
     51 + 
     52 + struct object *obj = (struct object*)malloc(sizeof(struct object));
     53 + uint32_t length = (*env)->GetArrayLength(env, key);
     54 + if (length > 256)
     55 + length = 256;
     56 + jboolean iscopy = JNI_FALSE;
     57 + jbyte *key_raw = (*env)->GetByteArrayElements(env, key, &iscopy);
     58 + memcpy(obj->key, key_raw, length);
     59 + 
     60 + // Write condition
     61 + *(m->values[0]) = value; // <-- this is so bad I can hardly imagine someone implementing this
     62 + 
     63 + (*env)->ReleaseByteArrayElements(env, key, key_raw, JNI_ABORT);
     64 + free(obj);
     65 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/DynamicClassLoader.java
     1 +package com.damnvulnerableapp.common;
     2 + 
     3 +/**
     4 + * Loader used to dynamically load classes by name. It also provides sanity checks for class names.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public final class DynamicClassLoader {
     10 + 
     11 + /**
     12 + * Sole instance of this class.
     13 + * */
     14 + private static DynamicClassLoader instance;
     15 + 
     16 + /**
     17 + * Construct sole instance.
     18 + * */
     19 + private DynamicClassLoader() {
     20 + 
     21 + }
     22 + 
     23 + /**
     24 + * Returns sole instance of this class.
     25 + *
     26 + * @return Sole instance.
     27 + * */
     28 + public static DynamicClassLoader getInstance() {
     29 + if (DynamicClassLoader.instance == null)
     30 + DynamicClassLoader.instance = new DynamicClassLoader();
     31 + return DynamicClassLoader.instance;
     32 + }
     33 + 
     34 + /**
     35 + * Checks whether a class with a given class name exists in the specified package and inherits
     36 + * from the specified parent class.
     37 + *
     38 + * @param classPackage Package that contains the class to check for.
     39 + * @param className Name of the class to check for.
     40 + * @param parent Superclass of class to check for.
     41 + * @return <code>true</code>, if specified class exists; <code>false</code> otherwise.
     42 + * */
     43 + public final boolean checkClass(Package classPackage, String className, Class<?> parent) {
     44 + 
     45 + if (classPackage == null || className == null || parent == null)
     46 + return false;
     47 + 
     48 + try {
     49 + final Class<?> target = Class.forName(classPackage.getName() + "." + className);
     50 + if (!parent.isAssignableFrom(target))
     51 + return false;
     52 + } catch (ClassNotFoundException e) {
     53 + return false;
     54 + }
     55 + 
     56 + return true;
     57 + }
     58 + 
     59 + /**
     60 + * Loads class based on its name. To that end, the class has to be located in a specified package
     61 + * and have a specified superclass.
     62 + *
     63 + * @param classPackage Package that contains the class to load.
     64 + * @param className Name of the class to load.
     65 + * @param parent Superclass of class to load.
     66 + * @return New instance of the class, if class exists; <code>null</code> otherwise.
     67 + * */
     68 + public final Object loadClass(Package classPackage, String className, Class<?> parent) {
     69 + 
     70 + if (classPackage == null || className == null || parent == null)
     71 + return null;
     72 + 
     73 + if (this.checkClass(classPackage, className, parent)) {
     74 + 
     75 + try {
     76 + 
     77 + final Class<?> target = Class.forName(classPackage.getName() + "." + className);
     78 + return target.newInstance();
     79 + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException ignored) {}
     80 + }
     81 + 
     82 + return null;
     83 + }
     84 +}
     85 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/configuration/ClientConfiguration.java
     1 +package com.damnvulnerableapp.common.configuration;
     2 + 
     3 +/**
     4 + * Configurations for a {@link com.damnvulnerableapp.networking.communication.client.Client}.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public class ClientConfiguration extends Configuration {
     10 + 
     11 + /**
     12 + * Timeout for underlying {@link com.damnvulnerableapp.networking.communication.client.EndPoint}
     13 + * in milliseconds. 0 indicates infinite timeout.
     14 + * */
     15 + protected int endpointTimeout = 5000;
     16 + 
     17 + protected int handshakeTimeout = 2000;
     18 + 
     19 + /**
     20 + * Returns timeout for underlying {@link com.damnvulnerableapp.networking.communication.client.EndPoint}
     21 + * in milliseconds.
     22 + *
     23 + * @return Timeout for {@link com.damnvulnerableapp.networking.communication.client.EndPoint}.
     24 + * @see com.damnvulnerableapp.networking.communication.client.EndPoint
     25 + * */
     26 + public final int getEndpointTimeout() {
     27 + return this.endpointTimeout;
     28 + }
     29 + 
     30 + public final int getHandshakeTimeout() {
     31 + return this.handshakeTimeout;
     32 + }
     33 +}
     34 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/configuration/ClientExitConfiguration.java
     1 +package com.damnvulnerableapp.common.configuration;
     2 + 
     3 + 
     4 +import com.damnvulnerableapp.networking.communication.client.Client;
     5 + 
     6 +/**
     7 + * Configuration used when trying to exit a selected module or to shut down the app. It prevents
     8 + * {@link Client#disconnect()} from blocking indefinitely.
     9 + *
     10 + * @author Pascal Kühnemann
     11 + * @version 1.0
     12 + * */
     13 +public final class ClientExitConfiguration extends ClientConfiguration {
     14 + public ClientExitConfiguration() {
     15 + this.endpointTimeout = 1000;
     16 + }
     17 +}
     18 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/configuration/Configurable.java
     1 +package com.damnvulnerableapp.common.configuration;
     2 + 
     3 +import androidx.annotation.NonNull;
     4 + 
     5 +/**
     6 + * Defines how configuration - related operations are to be performed.
     7 + *
     8 + * @author Pascal Kühnemann
     9 + * @version 1.0
     10 + * */
     11 +public interface Configurable {
     12 + 
     13 + /**
     14 + * Sets a new configuration by overwriting the old one. Implementing this function allows for
     15 + * dynamically adjusting properties of objects that depend on the configurations. E.g. the
     16 + * {@link com.damnvulnerableapp.networking.communication.client.NetworkEndPoint} can set its
     17 + * reading - timeout whenever a new configuration is to be used.
     18 + *
     19 + * @param configuration New configuration to use.
     20 + * @see Configuration
     21 + * */
     22 + void setConfiguration(Configuration configuration);
     23 + 
     24 + /**
     25 + * Gets currently selected configuration, or a modified version of it, depending on the use - case.
     26 + *
     27 + * @return Currently selected configuration.
     28 + * @see Configuration
     29 + * */
     30 + @NonNull
     31 + Configuration getConfiguration();
     32 +}
     33 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/configuration/Configuration.java
     1 +package com.damnvulnerableapp.common.configuration;
     2 + 
     3 +/**
     4 + * Superclass of all configuration classes. It does not specify any common configurations and only
     5 + * serves as a "common root" in the hierarchy of configuration classes.
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * */
     10 +public abstract class Configuration {}
     11 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/configuration/ServerClientConfiguration.java
     1 +package com.damnvulnerableapp.common.configuration;
     2 + 
     3 +public class ServerClientConfiguration extends ClientConfiguration {
     4 + 
     5 + public ServerClientConfiguration() {
     6 + this.endpointTimeout = 0;
     7 + }
     8 +}
     9 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/configuration/ServerConfiguration.java
     1 +package com.damnvulnerableapp.common.configuration;
     2 + 
     3 +import com.damnvulnerableapp.networking.communication.server.Server;
     4 + 
     5 +/**
     6 + * Configuration for a {@link com.damnvulnerableapp.networking.communication.server.Server}.
     7 + *
     8 + * @author Pascal Kühnemann
     9 + * @version 1.0
     10 + * */
     11 +public class ServerConfiguration extends Configuration {
     12 + 
     13 + /**
     14 + * Timeout for e.g. {@link Server#accept()}. In case of {@link com.damnvulnerableapp.networking.communication.server.NetworkServer}
     15 + * this value will be used in {@link java.net.ServerSocket#setSoTimeout(int)}. A timeout
     16 + * of 0 indicates no timeout. Defaults to 0ms.
     17 + * */
     18 + protected int timeout = 0;
     19 + 
     20 + /**
     21 + * Total number of clients to accept. Setting this to 0 indicates that infinite clients may
     22 + * connect.
     23 + * */
     24 + protected int numClients = 0;
     25 + 
     26 + /**
     27 + * Delay for accept loop in case {@link Server#startAsync()} is used. Defaults to 50ms.
     28 + * */
     29 + protected int acceptLoopDelay = 50;
     30 + 
     31 + /**
     32 + * Returns amount of milliseconds to wait for a client connection.
     33 + *
     34 + * @return Timeout.
     35 + * */
     36 + public final int getTimeout() {
     37 + return this.timeout;
     38 + }
     39 + 
     40 + /**
     41 + * Returns amount of client connections to accept (sequentially, not in parallel).
     42 + *
     43 + * @return Number of allowed client connections.
     44 + * */
     45 + public final int getNumClients() {
     46 + return this.numClients;
     47 + }
     48 + 
     49 + /**
     50 + * Returns amount of milliseconds to wait between each accept - loop iteration in case
     51 + * {@link Server#startAsync()} is used.
     52 + *
     53 + * @return Accept - loop delay.
     54 + * */
     55 + public final int getAcceptLoopDelay() {
     56 + return this.acceptLoopDelay;
     57 + }
     58 +}
     59 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/AppStateException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if an error related to the internal app(lication) state occurs.
     5 + * E.g. trying to move to a state, but there is no supported transition, may cause this error.
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * */
     10 +public class AppStateException extends Exception {
     11 + 
     12 + public AppStateException(String message) {
     13 + super("Error with state of app: " + message);
     14 + }
     15 + 
     16 + public AppStateException(String message, Throwable e) {
     17 + super("Error with state of app: " + message, e);
     18 + }
     19 +}
     20 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/InitializationException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if initializing a resource failed and cannot be recovered. E.g.
     5 + * failing to create a {@link com.damnvulnerableapp.networking.communication.client.Client} for
     6 + * communication with a vulnerable module would be devastating, therefore causing this error.
     7 + *
     8 + * @author Pascal Kühnemann
     9 + * @version 1.0
     10 + * */
     11 +public class InitializationException extends MVCException {
     12 + 
     13 + public InitializationException(String message) {
     14 + super("Initialization failed: " + message);
     15 + }
     16 + 
     17 + public InitializationException(String message, Throwable e) {
     18 + super("Initialization failed: " + message, e);
     19 + }
     20 +}
     21 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/InvalidTransitionException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if someone tries to move from one state to another without a
     5 + * supported transition. E.g. being in {@link com.damnvulnerableapp.managerservice.controllers.states.VulnerableSelectState},
     6 + * one cannot EXIT and thus re-enter the SELECT state, because there is no running module to exit.
     7 + *
     8 + * @author Pascal Kühnemann
     9 + * @version 1.0
     10 + * */
     11 +public class InvalidTransitionException extends AppStateException {
     12 + 
     13 + public InvalidTransitionException(String message) {
     14 + super("Failed to transition to new state: " + message);
     15 + }
     16 + 
     17 + public InvalidTransitionException(String message, Throwable e) {
     18 + super("Failed to transition to new state: " + message, e);
     19 + }
     20 +}
     21 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/MVCException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if any error related to the MVC architecture occurs. E.g. failing
     5 + * to create important resources can produce this error.
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * @see InitializationException
     10 + * */
     11 +public class MVCException extends Exception {
     12 + 
     13 + public MVCException(String message) {
     14 + super("MVC error: " + message);
     15 + }
     16 + 
     17 + public MVCException(String message, Throwable e) {
     18 + super("MVC error: " + message, e);
     19 + }
     20 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/VulnerableModuleCommunicationException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if communicating with the selected, vulnerable module fails. This
     5 + * is often a wrapper for {@link com.damnvulnerableapp.networking.exceptions.ConnectionException}s.
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * */
     10 +public class VulnerableModuleCommunicationException extends VulnerableModuleException {
     11 + 
     12 + public VulnerableModuleCommunicationException(String message) {
     13 + super("Failed to communicate with vulnerable module: " + message);
     14 + }
     15 + 
     16 + public VulnerableModuleCommunicationException(String message, Throwable e) {
     17 + super("Failed to communicate with vulnerable module: " + message, e);
     18 + }
     19 +}
     20 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/VulnerableModuleCreationException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +public class VulnerableModuleCreationException extends VulnerableModuleException {
     4 + 
     5 + public VulnerableModuleCreationException(String message) {
     6 + super("Failed to create vulnerable module activity: " + message);
     7 + }
     8 + 
     9 + public VulnerableModuleCreationException(String message, Throwable e) {
     10 + super("Failed to create vulnerable module activity: " + message, e);
     11 + }
     12 +}
     13 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/VulnerableModuleException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if any error related to the vulnerable module occurs.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * @see VulnerableModuleCommunicationException
     9 + * */
     10 +public class VulnerableModuleException extends Exception {
     11 + 
     12 + public VulnerableModuleException(String message) {
     13 + super("Error in vulnerable module: " + message);
     14 + }
     15 + 
     16 + public VulnerableModuleException(String message, Throwable e) {
     17 + super("Error in vulnerable module: " + message, e);
     18 + }
     19 +}
     20 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/VulnerableModuleExitException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if the vulnerable module exited by itself.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public class VulnerableModuleExitException extends VulnerableModuleException {
     10 + 
     11 + public VulnerableModuleExitException(String message) {
     12 + super("Vulnerable module quit: " + message);
     13 + }
     14 + 
     15 + public VulnerableModuleExitException(String message, Throwable e) {
     16 + super("Vulnerable module quit: " + message, e);
     17 + }
     18 +}
     19 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/common/exceptions/VulnerableModuleOperationException.java
     1 +package com.damnvulnerableapp.common.exceptions;
     2 + 
     3 +/**
     4 + * Exception that will be thrown, if e.g. fetching data from the vulnerable module or forwarding
     5 + * data to the vulnerable module fails
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * */
     10 +public class VulnerableModuleOperationException extends VulnerableModuleException {
     11 + 
     12 + public VulnerableModuleOperationException(String message) {
     13 + super("Operation not successful: " + message);
     14 + }
     15 + 
     16 + public VulnerableModuleOperationException(String message, Throwable e) {
     17 + super("Operation not successful: " + message, e);
     18 + }
     19 +}
     20 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/ManagerActivity.java
     1 +package com.damnvulnerableapp.managerservice;
     2 + 
     3 +import androidx.appcompat.app.AppCompatActivity;
     4 + 
     5 +import android.content.Intent;
     6 +import android.os.Bundle;
     7 +import android.provider.Settings;
     8 + 
     9 +import com.damnvulnerableapp.managerservice.ManagerService;
     10 + 
     11 +/**
     12 + * Initial activity that kicks off the whole application. It basically just starts a background
     13 + * service that does all the heavy lifting.
     14 + *
     15 + * @author Pascal Kühnemann
     16 + * @version 1.0
     17 + * */
     18 +public final class ManagerActivity extends AppCompatActivity {
     19 + 
     20 + /**
     21 + * Requests permission required in order to avoid the service timing out after a while (10s).
     22 + * Afterwards, it starts the service. If the permission is not granted, then the app will w.h.p.
     23 + * NOT work properly.
     24 + * */
     25 + @Override
     26 + protected void onCreate(Bundle savedInstanceState) {
     27 + super.onCreate(savedInstanceState);
     28 + 
     29 + if (!Settings.canDrawOverlays(this)) {
     30 + final Intent overlayPermissions = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
     31 + this.startActivity(overlayPermissions);
     32 + }
     33 + 
     34 + final Intent service = new Intent(this, ManagerService.class);
     35 + this.startService(service);
     36 + this.finish();
     37 + }
     38 +}
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/ManagerGlobals.java
     1 +package com.damnvulnerableapp.managerservice;
     2 + 
     3 +/**
     4 + * Data class that contains all globals that need to be accessible from different modules.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public final class ManagerGlobals {
     10 + 
     11 + /**
     12 + * Key of the factory name that is passed to vulnerable activity via intents.
     13 + * */
     14 + public static final String FACTORY_INTENT_KEY = "FACTORY";
     15 + 
     16 + /**
     17 + * Key of the module name to load. This is passed to the vulnerable activity via intents.
     18 + * */
     19 + public static final String MODULE_INTENT_KEY = "MODULE";
     20 +}
     21 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/ManagerService.java
     1 +package com.damnvulnerableapp.managerservice;
     2 + 
     3 +import android.app.Service;
     4 +import android.content.Intent;
     5 +import android.os.IBinder;
     6 + 
     7 +import androidx.annotation.Nullable;
     8 + 
     9 +import com.damnvulnerableapp.common.exceptions.InitializationException;
     10 +import com.damnvulnerableapp.managerservice.views.ExternalView;
     11 +import com.damnvulnerableapp.networking.exceptions.BindException;
     12 +import com.damnvulnerableapp.networking.exceptions.CreationException;
     13 +import com.damnvulnerableapp.networking.exceptions.InternalSocketException;
     14 + 
     15 +/**
     16 + * Background service that will listen for incoming connections. It will handle all management -
     17 + * related work like spawning vulnerable activities, handling I/O etc.
     18 + *
     19 + * @author Pascal Kühnemann
     20 + * @version 1.0
     21 + * */
     22 +public final class ManagerService extends Service {
     23 + 
     24 + /**
     25 + * Sole instance of this class.
     26 + * */
     27 + private static ManagerService instance;
     28 + 
     29 + /**
     30 + * Returns sole instance of this class.
     31 + *
     32 + * @return Sole instance.
     33 + * */
     34 + public static ManagerService getInstance() {
     35 + return ManagerService.instance;
     36 + }
     37 + 
     38 + /**
     39 + * Creates and sets up the {@link com.damnvulnerableapp.networking.communication.server.Server}
     40 + * used for communication with external clients. It also implicitly creates {@link com.damnvulnerableapp.managerservice.controllers.ExternalController}
     41 + * that manages the app's logic.
     42 + * */
     43 + @Override
     44 + public final int onStartCommand(Intent intent, int flags, int startID) {
     45 + ManagerService.instance = this;
     46 + 
     47 + // Create external view
     48 + final ExternalView externalView = new ExternalView();
     49 + try {
     50 + externalView.init();
     51 + } catch (BindException | InternalSocketException | CreationException | InitializationException e) {
     52 + // Exit if view cannot be created.
     53 + this.stopSelf();
     54 + }
     55 + 
     56 + return START_STICKY;
     57 + }
     58 + 
     59 + @Override
     60 + public final void onDestroy() {}
     61 + 
     62 + @Nullable
     63 + @Override
     64 + public final IBinder onBind(Intent intent) {
     65 + return null;
     66 + }
     67 +}
     68 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/controllers/ExternalController.java
     1 +package com.damnvulnerableapp.managerservice.controllers;
     2 + 
     3 +import com.damnvulnerableapp.common.configuration.ClientExitConfiguration;
     4 +import com.damnvulnerableapp.common.exceptions.InitializationException;
     5 +import com.damnvulnerableapp.managerservice.controllers.states.VulnerableAppState;
     6 +import com.damnvulnerableapp.managerservice.controllers.states.VulnerableSelectState;
     7 +import com.damnvulnerableapp.common.exceptions.AppStateException;
     8 +import com.damnvulnerableapp.common.exceptions.InvalidTransitionException;
     9 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleException;
     10 +import com.damnvulnerableapp.managerservice.views.ExternalView;
     11 +import com.damnvulnerableapp.networking.communication.client.Client;
     12 +import com.damnvulnerableapp.networking.communication.client.CommunicationListener;
     13 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     14 +import com.damnvulnerableapp.networking.messages.Message;
     15 +import com.damnvulnerableapp.networking.messages.Operation;
     16 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     17 + 
     18 +/**
     19 + * Controller that manages the app's main logic. It handles communication with external clients
     20 + * and keeps track of the app's state.
     21 + *
     22 + * To support communicating with a client, this controller implements {@link CommunicationListener},
     23 + * which allows constructing a request - response model by just reacting to incoming messages and
     24 + * treating them as requests. In any scenario, this controller will send a response.
     25 + *
     26 + * Tracking the app's state is done by simulating a DFA.
     27 + *
     28 + * @author Pascal Kühnemann
     29 + * @version 1.0
     30 + * */
     31 +public final class ExternalController implements CommunicationListener {
     32 + 
     33 + /**
     34 + * Amount of failures allowed when trying to send a response to the external client. If this
     35 + * is exceeded, the application will terminate, because communication is assumed to be impossible
     36 + * from there on.
     37 + * */
     38 + private static final int AMOUNT_RETRIES = 2;
     39 + 
     40 + /**
     41 + * Reference to the UI that a user is confronted with, i.e. basically a {@link com.damnvulnerableapp.networking.communication.server.NetworkServer}.
     42 + * */
     43 + private final ExternalView externalView;
     44 + 
     45 + /**
     46 + * Current state of app. It encapsulates behaviour that depends on the state.
     47 + * */
     48 + private VulnerableAppState state;
     49 + 
     50 + /**
     51 + * Process identifier of vulnerable activity.
     52 + * */
     53 + private int vulnerableModulePid;
     54 + 
     55 + private volatile Client mainClient;
     56 + 
     57 + /**
     58 + * Constructs this controller by giving it a back - reference to the UI as well as setting
     59 + * the initial state of the app ({@link VulnerableSelectState}).
     60 + *
     61 + * @param externalView Reference to UI.
     62 + * @throws InitializationException If initializing internal client fails.
     63 + * @see ExternalView
     64 + * @see VulnerableAppState
     65 + * */
     66 + public ExternalController(ExternalView externalView) throws InitializationException {
     67 + 
     68 + // Set reference to external view
     69 + this.externalView = externalView;
     70 + 
     71 + // Initialize state
     72 + this.state = new VulnerableSelectState(this);
     73 + }
     74 + 
     75 + /**
     76 + * Closes resources that this controller is using. Among other things, it will shutdown the view.
     77 + * To avoid confusion, {@link ExternalView#close()} is called first. This ensures that no
     78 + * further messages are received, which could potentially cause confusion by triggering state
     79 + * changes.
     80 + *
     81 + * @see ExternalView#close()
     82 + * */
     83 + public final void close() {
     84 + 
     85 + this.externalView.close();
     86 + 
     87 + // Perform other cleanup specific to this class...
     88 + }
     89 + 
     90 + /**
     91 + * Changes the state of the app. Every state implements allowed transitions and actions bound
     92 + * to those transitions.
     93 + *
     94 + * @param state New state of the app.
     95 + * @see VulnerableAppState
     96 + * */
     97 + public final void changeState(VulnerableAppState state) {
     98 + this.state = state;
     99 + }
     100 + 
     101 + /**
     102 + * Sets the process identifier of currently selected vulnerable module. This is used to force-
     103 + * shutdown the module, if requested by the user.
     104 + *
     105 + * @param pid Process identifier of vulnerable module.
     106 + * */
     107 + public final void setVulnerableModulePid(int pid) {
     108 + this.vulnerableModulePid = pid;
     109 + }
     110 + 
     111 + /**
     112 + * Gets the process identifier of currently selected vulnerable module. This is used to force-
     113 + * shutdown the module, if requested by the user.
     114 + *
     115 + * @return Process identifier of vulnerable module.
     116 + * */
     117 + public final int getVulnerableModulePid() {
     118 + return this.vulnerableModulePid;
     119 + }
     120 + 
     121 + /**
     122 + * Will be called, if a user connects to {@link ExternalView}. It is used to ensure that there
     123 + * is only one client at once by storing the {@link Client} object of the first client that
     124 + * connected. Any additional client will be immediately disconnected. Only after this first
     125 + * client disconnected there may be a new client.
     126 + *
     127 + * @param client External client that tries to connect.
     128 + * @see CommunicationListener
     129 + * */
     130 + @Override
     131 + public final void onConnect(Client client) {
     132 + 
     133 + synchronized (this) {
     134 + 
     135 + if (this.mainClient != null) {
     136 + 
     137 + // Allow timeout
     138 + client.setConfiguration(new ClientExitConfiguration());
     139 + client.disconnect();
     140 + } else {
     141 + this.mainClient = client;
     142 + }
     143 + }
     144 + }
     145 + 
     146 + /**
     147 + * Will be called, if a connected, external client disconnects. May be called multiple times. It
     148 + * is used to ensure that only one client is connected at a time. To that end, the client object
     149 + * of the external client that connected first is stored and compared with any other client.
     150 + * Only if the first client disconnects, this app will return to the SELECT state.
     151 + *
     152 + * @param client External client that tries to disconnect.
     153 + * @see CommunicationListener
     154 + * */
     155 + @Override
     156 + public final void onDisconnect(Client client) {
     157 + 
     158 + // If a second client is disconnected
     159 + synchronized (this) {
     160 + 
     161 + // The second condition should never be true...
     162 + if (this.mainClient != client || this.mainClient == null)
     163 + return;
     164 + 
     165 + // Exit running module, if any
     166 + try {
     167 + this.state.exit(null);
     168 + } catch (InitializationException e) {
     169 + 
     170 + this.sendRetry(
     171 + client,
     172 + new PlainMessage(
     173 + Operation.INVALID,
     174 + "Failed to create internal client. Shutting down..."
     175 + )
     176 + );
     177 + 
     178 + this.state.shutdown();
     179 + } catch (AppStateException ignored) {}
     180 + 
     181 + this.mainClient = null;
     182 + }
     183 + }
     184 + 
     185 + /**
     186 + * Will be called, if the server - side client that is connected to the external client, tries
     187 + * to send a message. Beware of recursion, i.e. calling {@link Client#send(Message)} is not a
     188 + * good idea!
     189 + *
     190 + * @param client Server - side client used to communicate with external client.
     191 + * @param message Message to be sent to external client.
     192 + * @see CommunicationListener
     193 + * */
     194 + @Override
     195 + public final void onSend(Client client, Message message) {}
     196 + 
     197 + /**
     198 + * Will be called, if the external client sends a {@link PlainMessage}. Based on the {@link Operation}
     199 + * of this message, different control paths (w.r.t. current state) will be taken. If there is
     200 + * an invalid {@link Operation} w.r.t. current state, then an error message will be the response.
     201 + *
     202 + * In general, this controller tries to always send a response. If an error occurs, then the
     203 + * response will contain information on what went wrong. If it is an internal error, e.g. a
     204 + * {@link com.damnvulnerableapp.networking.exceptions.ConnectionException} was thrown, then
     205 + * it is assumed that there is no way to communicate with the external client. Therefore, the
     206 + * app will disconnect the client. There are also errors that are non - recoverable, i.e. the
     207 + * app cannot do anything else but to terminate.
     208 + *
     209 + * If the operation is {@link Operation#FETCH}, then the response will contain a byte - array
     210 + * containing data that the internal, vulnerable module wants to output.
     211 + *
     212 + * @param client External client that sent a message.
     213 + * @param message Message sent by external client.
     214 + * @see CommunicationListener
     215 + * */
     216 + @Override
     217 + public final void onReceive(Client client, Message message) {
     218 + 
     219 + final PlainMessage plain = (PlainMessage)message;
     220 + 
     221 + try {
     222 + switch (plain.getOperation()) {
     223 + 
     224 + // Select a vulnerable module
     225 + case SELECT:
     226 + this.state.select(plain);
     227 + 
     228 + this.sendRetry(
     229 + client,
     230 + new PlainMessage(
     231 + Operation.SUCCESS,
     232 + "Successfully selected module."
     233 + )
     234 + );
     235 + break;
     236 + 
     237 + // Exit currently selected vulnerable module.
     238 + case EXIT:
     239 + this.state.exit(plain);
     240 + 
     241 + this.sendRetry(
     242 + client,
     243 + new PlainMessage(
     244 + Operation.SUCCESS,
     245 + "Successfully closed module."
     246 + )
     247 + );
     248 + break;
     249 + 
     250 + // Shut down this app.
     251 + case SHUTDOWN:
     252 + this.state.shutdown();
     253 + break;
     254 + 
     255 + // Forward data to vulnerable module.
     256 + case FORWARD:
     257 + this.state.forward(plain);
     258 + this.sendRetry(
     259 + client,
     260 + new PlainMessage(
     261 + Operation.SUCCESS,
     262 + "Successfully forwarded message."
     263 + )
     264 + );
     265 + break;
     266 + 
     267 + // Send data output by vulnerable module to client.
     268 + case FETCH:
     269 + this.sendRetry(
     270 + client,
     271 + this.state.fetch(plain)
     272 + );
     273 + break;
     274 + 
     275 + default:
     276 + this.sendRetry(
     277 + client,
     278 + new PlainMessage(
     279 + Operation.INVALID,
     280 + "Invalid operation: " + plain.getOperation()
     281 + )
     282 + );
     283 + }
     284 + } catch (InvalidTransitionException | VulnerableModuleException e) {
     285 + // These exceptions are recoverable
     286 + this.sendRetry(
     287 + client,
     288 + new PlainMessage(
     289 + Operation.INVALID,
     290 + e.getMessage()
     291 + )
     292 + );
     293 + } catch (AppStateException | InitializationException e) {
     294 + // There exceptions are deadly to app
     295 + this.sendRetry(
     296 + client,
     297 + new PlainMessage(
     298 + Operation.INVALID,
     299 + e.getMessage()
     300 + )
     301 + );
     302 + this.state.shutdown();
     303 + }
     304 + }
     305 + 
     306 + /**
     307 + * Will be called, if external client sends an invalid message, i.e. if the sent message is
     308 + * declared as invalid by {@link com.damnvulnerableapp.networking.messages.PlainMessageParser}.
     309 + *
     310 + * The external client will be informed that the message was invalid. If informing the external
     311 + * client fails, then the app will shut down.
     312 + *
     313 + * @param client External client that sent an invalid message.
     314 + * @param message Invalid message.
     315 + * @see CommunicationListener
     316 + * @see com.damnvulnerableapp.networking.messages.PlainMessageParser
     317 + * */
     318 + @Override
     319 + public final void onInvalidMessage(Client client, byte[] message) {
     320 + this.sendRetry(client, new PlainMessage(Operation.INVALID, "Invalid message: " + new String(message)));
     321 + }
     322 + 
     323 + /**
     324 + * Tries to send a message via a client. If sending the message fails, it will be retried as
     325 + * often as possible (i.e. until a threshold is hit). If the threshold is hit, then the app will
     326 + * disconnect the client, because it is assumed for communication to be impossible from there on.
     327 + *
     328 + * @param client Client to send message to.
     329 + * @param message Message to send.
     330 + * */
     331 + private void sendRetry(Client client, PlainMessage message) {
     332 + 
     333 + // Try to send message AMOUNT_RETRIES times
     334 + int amountRetries = 0;
     335 + while (amountRetries < ExternalController.AMOUNT_RETRIES) {
     336 + try {
     337 + client.send(message);
     338 + return;
     339 + } catch (CommunicationException e) {
     340 + amountRetries++;
     341 + }
     342 + }
     343 + 
     344 + // If sending the message fails: client will be disconnected
     345 + client.disconnect();
     346 + }
     347 +}
     348 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/controllers/states/VulnerableAppState.java
     1 +package com.damnvulnerableapp.managerservice.controllers.states;
     2 + 
     3 +import android.os.Process;
     4 + 
     5 +import com.damnvulnerableapp.common.configuration.ClientConfiguration;
     6 +import com.damnvulnerableapp.common.configuration.ClientExitConfiguration;
     7 +import com.damnvulnerableapp.common.exceptions.InitializationException;
     8 +import com.damnvulnerableapp.managerservice.ManagerService;
     9 +import com.damnvulnerableapp.managerservice.controllers.ExternalController;
     10 +import com.damnvulnerableapp.common.exceptions.AppStateException;
     11 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleException;
     12 +import com.damnvulnerableapp.networking.communication.CommunicationFactory;
     13 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     14 +import com.damnvulnerableapp.networking.communication.client.Client;
     15 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     16 +import com.damnvulnerableapp.networking.messages.Message;
     17 +import com.damnvulnerableapp.networking.messages.Operation;
     18 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     19 +import com.damnvulnerableapp.networking.protocol.PlainProtocolFactory;
     20 + 
     21 +/**
     22 + * App state, which declares functionality used by {@link ExternalController}. It describes what
     23 + * a specific app state needs to implement in order for the app to have state - dependent behaviour.
     24 + * Also it implements functionality that is common among all states.
     25 + *
     26 + * Additionally, this class constructs the {@link Client} used to communicate with vulnerable
     27 + * modules.
     28 + *
     29 + * @author Pascal Kühnemann
     30 + * @version 1.0
     31 + * */
     32 +public abstract class VulnerableAppState {
     33 + 
     34 + /**
     35 + * Factory used to create the communication endpoints. By default, a {@link NetworkFactory} is
     36 + * used for communicating with vulnerable modules.
     37 + * @see CommunicationFactory
     38 + * */
     39 + private final CommunicationFactory factory;
     40 + 
     41 + /**
     42 + * Client used for communication with vulnerable module.
     43 + * */
     44 + private final Client internalClient;
     45 + 
     46 + /**
     47 + * Reference to the context, i.e. the portion of the app that manages the states. This can be
     48 + * used to trigger transitions inside of states.
     49 + * */
     50 + private final ExternalController context;
     51 + 
     52 + /**
     53 + * Configuration used to EXIT a selected module or to shutdown the app. This ensures that
     54 + * disconnecting the internal client does not result in a deadlock due to infinite timeout.
     55 + * */
     56 + private final ClientExitConfiguration exitConfiguration;
     57 + 
     58 + /**
     59 + * Constructs the app state by creating the client used to communicate with a vulnerable module
     60 + * and storing the context.
     61 + *
     62 + * The client will be created using a {@link NetworkFactory} in conjunction with a
     63 + * {@link PlainProtocolFactory}. Changing the {@link CommunicationFactory} can be achieved by
     64 + * changing this constructor. Changing the message type is (almost) impossible, as it will be
     65 + * used inside the vulnerable module and there is currently no way to generate messages using
     66 + * {@link com.damnvulnerableapp.networking.protocol.ProtocolFactory}. If creation of the internal
     67 + * client fails, this app will be shut down.
     68 + *
     69 + * @param context App context that manages the app states.
     70 + * @param internalClient {@link Client} to be used as internal client. This is used in case of
     71 + * a transition from {@link VulnerableSelectState} to {@link VulnerableCommunicateState}.
     72 + * It may be <code>null</code>.
     73 + * @throws InitializationException If initializing internal client fails.
     74 + * @see NetworkFactory
     75 + * @see PlainProtocolFactory
     76 + * */
     77 + public VulnerableAppState(ExternalController context, Client internalClient) throws InitializationException {
     78 + 
     79 + // Replace with other factory, if necessary
     80 + this.factory = new NetworkFactory();
     81 + this.factory.setProtocolFactory(new PlainProtocolFactory());
     82 + 
     83 + if (internalClient == null)
     84 + this.internalClient = this.factory.createClient();
     85 + else
     86 + this.internalClient = internalClient;
     87 + 
     88 + if (this.internalClient == null)
     89 + throw new InitializationException("Failed to initialize internal client.");
     90 + 
     91 + this.context = context;
     92 + this.exitConfiguration = new ClientExitConfiguration();
     93 + }
     94 + 
     95 + /**
     96 + * Shuts down the app. As this is a common transition that every state can perform, it is
     97 + * defined in this class.
     98 + *
     99 + * @see Client#disconnect()
     100 + * @see ExternalController#close()
     101 + * */
     102 + public final void shutdown() {
     103 + 
     104 + // Set configuration of internal client s.t. there cannot be any infinite waits.
     105 + this.getInternalClient().setConfiguration(this.exitConfiguration);
     106 + 
     107 + try {
     108 + // Tell module to quit. If this fails, the module most likely crashed so it does not matter
     109 + this.getInternalClient().send(new PlainMessage(Operation.EXIT, "rip"));
     110 + 
     111 + // Give module a chance to terminate itself
     112 + this.getInternalClient().receive();
     113 + } catch (CommunicationException ignored) {}
     114 + 
     115 + // Disconnect internal client gracefully
     116 + this.getInternalClient().disconnect();
     117 + 
     118 + // Kill vulnerable process
     119 + if (this.getContext().getVulnerableModulePid() != 0)
     120 + Process.killProcess(this.getContext().getVulnerableModulePid());
     121 + 
     122 + this.getContext().close();
     123 + ManagerService.getInstance().stopSelf();
     124 + Process.killProcess(Process.myPid());
     125 + }
     126 + 
     127 + /**
     128 + * Selects a vulnerable module, if there is no selected module yet.
     129 + *
     130 + * @param message Request that triggered the selection process. It may contain additional
     131 + * information on how to run the vulnerable module.
     132 + * @throws AppStateException If a DFA - related error occurs. E.g. if trying to make a transition
     133 + * which is not allowed in a certain state. This will be used to manage
     134 + * messaging in {@link ExternalController#onReceive(Client, Message)}.
     135 + * @throws InitializationException If initializing internal client fails.
     136 + * @throws VulnerableModuleException If an error occurs while selecting the module. E.g. if the
     137 + * given module name does not match a single class.
     138 + * @see PlainMessage
     139 + * */
     140 + public abstract void select(PlainMessage message) throws AppStateException, InitializationException, VulnerableModuleException;
     141 + 
     142 + /**
     143 + * Exits the currently selected, vulnerable module, if any. This might be called, if a module
     144 + * crashed or hung up.
     145 + *
     146 + * @param message Request that triggered this routine. It may contain additional information on
     147 + * how to exit the current module. May be <code>null</code>.
     148 + * @throws AppStateException If a DFA - related error occurs. E.g. if trying to make a transition
     149 + * which is not allowed in a certain state. This will be used to manage
     150 + * messaging in {@link ExternalController#onReceive(Client, Message)}.
     151 + * @throws InitializationException If initializing internal client fails.
     152 + * @see PlainMessage
     153 + * */
     154 + public abstract void exit(PlainMessage message) throws AppStateException, InitializationException;
     155 + 
     156 + /**
     157 + * Tries to forward data to the vulnerable module, if any. This represents the input - portion
     158 + * of the I/O of the vulnerable module.
     159 + *
     160 + * @param message Request that triggered this routine. It may contain additional information on
     161 + * how to forward the data.
     162 + * @throws AppStateException If a DFA - related error occurs. E.g. if trying to make a transition
     163 + * which is not allowed in a certain state. This will be used to manage
     164 + * messaging in {@link ExternalController#onReceive(Client, Message)}.
     165 + * @throws InitializationException If initializing internal client fails.
     166 + * @throws VulnerableModuleException If an error occurs inside the vulnerable module.
     167 + * @see PlainMessage
     168 + * */
     169 + public abstract void forward(PlainMessage message) throws AppStateException, InitializationException, VulnerableModuleException;
     170 + 
     171 + /**
     172 + * Tries to fetch data from the vulnerable module, if any. This represents the output - portion
     173 + * of the I/O of the vulnerable module.
     174 + *
     175 + * @param message Request that triggered this routine. It may contain additional information on
     176 + * how to fetch the data from current module.
     177 + * @return Data from vulnerable module.
     178 + * @throws AppStateException If a DFA - related error occurs. E.g. if trying to make a transition
     179 + * which is not allowed in a certain state. This will be used to manage
     180 + * messaging in {@link ExternalController#onReceive(Client, Message)}.
     181 + * @throws InitializationException If initializing internal client fails.
     182 + * @throws VulnerableModuleException If an error occurs inside the vulnerable module.
     183 + * @see PlainMessage
     184 + * */
     185 + public abstract PlainMessage fetch(PlainMessage message) throws AppStateException, InitializationException, VulnerableModuleException;
     186 + 
     187 + /**
     188 + * Gets the factory used to create the internal {@link Client}. This will be passed to the
     189 + * vulnerable module, which allows it to construct a {@link com.damnvulnerableapp.networking.communication.server.Server}
     190 + * that matches this internal client.
     191 + *
     192 + * @return Factory for constructing compatible {@link Client}s and {@link com.damnvulnerableapp.networking.communication.server.Server}s.
     193 + * */
     194 + protected final CommunicationFactory getFactory() {
     195 + return this.factory;
     196 + }
     197 + 
     198 + /**
     199 + * Gets this class's internal client that can communicate with the vulnerable module, if any.
     200 + *
     201 + * @return Internal client.
     202 + * */
     203 + protected final Client getInternalClient() {
     204 + return this.internalClient;
     205 + }
     206 + 
     207 + /**
     208 + * Gets the reference to the state manager. It is mainly used to trigger state transitions.
     209 + *
     210 + * @return State manager, i.e. context.
     211 + * */
     212 + protected final ExternalController getContext() {
     213 + return this.context;
     214 + }
     215 + 
     216 + /**
     217 + * Gets the exit configuration used to exit a selected module or to shut down the app. This
     218 + * prevents {@link Client#disconnect()} from blocking indefinitely.
     219 + *
     220 + * @return Exit configuration.
     221 + * */
     222 + protected final ClientConfiguration getExitConfiguration() {
     223 + return this.exitConfiguration;
     224 + }
     225 +}
     226 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/controllers/states/VulnerableCommunicateState.java
     1 +package com.damnvulnerableapp.managerservice.controllers.states;
     2 + 
     3 +import android.app.ActivityManager;
     4 +import android.content.Context;
     5 +import android.os.Process;
     6 + 
     7 +import com.damnvulnerableapp.common.exceptions.InitializationException;
     8 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleExitException;
     9 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleOperationException;
     10 +import com.damnvulnerableapp.managerservice.ManagerService;
     11 +import com.damnvulnerableapp.managerservice.controllers.ExternalController;
     12 +import com.damnvulnerableapp.common.exceptions.InvalidTransitionException;
     13 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleCommunicationException;
     14 +import com.damnvulnerableapp.networking.communication.client.Client;
     15 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     16 +import com.damnvulnerableapp.networking.messages.Operation;
     17 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     18 + 
     19 +import java.util.List;
     20 +import java.util.TimerTask;
     21 +import java.util.concurrent.Executors;
     22 +import java.util.concurrent.ScheduledExecutorService;
     23 +import java.util.concurrent.TimeUnit;
     24 + 
     25 +/**
     26 + * COMMUNICATE - state that will be in use, if a vulnerable module is selected. This is the main
     27 + * state. Therefore, the only way to leave this state is to either close the vulnerable module or
     28 + * to shut down the app.
     29 + *
     30 + * @author Pascal Kühnemann
     31 + * @version 1.0
     32 + * */
     33 +public final class VulnerableCommunicateState extends VulnerableAppState {
     34 + 
     35 + /**
     36 + * Interval, at which to check for existence of vulnerable module process.
     37 + * */
     38 + private static final int WAIT_INTERVAL = 200;
     39 + 
     40 + /**
     41 + * State of vulnerable module, i.e. either its process is up and running or not. This flag is
     42 + * used to wait until the process of the vulnerable module dies before returning from
     43 + * {@link VulnerableCommunicateState#exit(PlainMessage)}.
     44 + * */
     45 + private volatile boolean moduleAlive;
     46 + 
     47 + /**
     48 + * Constructs this state by storing the reference to the state manager. Coming into this state
     49 + * implies that the vulnerable module is up and running.
     50 + *
     51 + * @param context State manager, i.e. the context.
     52 + * @param internalClient Initialized internal client. As this state can only be reached through
     53 + * {@link VulnerableSelectState}, this client will be coming from {@link VulnerableSelectState}.
     54 + * @throws InitializationException If initializing internal client fails.
     55 + * */
     56 + public VulnerableCommunicateState(ExternalController context, Client internalClient) throws InitializationException {
     57 + super(context, internalClient);
     58 + 
     59 + // Module is assumed to be alive!
     60 + this.moduleAlive = true;
     61 + }
     62 + 
     63 + /**
     64 + * Invalid operation in this state. It must not possible to select a module, if there is already
     65 + * a selected module. In order to select a different module/restart the current module, try to
     66 + * EXIT first and (re - )SELECT the module of choice.
     67 + *
     68 + * @param ignored Request that triggered this routine. Will be ignored.
     69 + * @throws InvalidTransitionException Always thrown, because this operation is not permitted in
     70 + * this state, i.e. in the SELECT - state.
     71 + * */
     72 + @Override
     73 + public final void select(PlainMessage ignored) throws InvalidTransitionException {
     74 + throw new InvalidTransitionException(
     75 + "Cannot SELECT in COMMUNICATE - state. There is already a selected module."
     76 + );
     77 + }
     78 + 
     79 + /**
     80 + * Exits selected, vulnerable module. To that end, this method first sets the default timeout
     81 + * to a value that allows the client to timeout quickly (i.e. e.g. no 20s waiting). Then it gives
     82 + * the module a chance to terminate itself. Finally the internal client will be disconnected
     83 + * and the module killed via {@link android.os.Process#killProcess(int)}. Eventually, the state
     84 + * will be changed back to SELECT.
     85 + *
     86 + * After this call, the process identifier of the vulnerable module will be reset to 0.
     87 + *
     88 + * @param message Request that triggered this routine; or <code>null</code>.
     89 + * @throws InitializationException If initializing internal client fails.
     90 + * */
     91 + @Override
     92 + public final void exit(PlainMessage message) throws InitializationException {
     93 + 
     94 + // In order for this not to hang/block, set the timeout to something small > 0.
     95 + this.getInternalClient().setConfiguration(this.getExitConfiguration());
     96 + 
     97 + try {
     98 + // Tell module to quit. If this fails, the module most likely crashed so it does not matter
     99 + if (message == null)
     100 + message = new PlainMessage(Operation.EXIT, "rip");
     101 + this.getInternalClient().send(message);
     102 + 
     103 + // Give module a chance to terminate itself. This will trigger a ConnectionException
     104 + // in almost all cases.
     105 + this.getInternalClient().receive();
     106 + } catch (CommunicationException ignored) {}
     107 + 
     108 + // Disconnect internal client
     109 + this.getInternalClient().disconnect();
     110 + 
     111 + // Kill vulnerable process
     112 + if (this.getContext().getVulnerableModulePid() != 0)
     113 + Process.killProcess(this.getContext().getVulnerableModulePid());
     114 + 
     115 + this.spinOnVulnerableModuleAlive();
     116 + 
     117 + // Reset pid
     118 + this.getContext().setVulnerableModulePid(0);
     119 + 
     120 + // Transition back to select state
     121 + this.getContext().changeState(new VulnerableSelectState(this.getContext()));
     122 + }
     123 + 
     124 + /**
     125 + * Tries to forward data to the selected, vulnerable module.
     126 + *
     127 + * If the vulnerable module hung up or crashed, this method will run {@link VulnerableCommunicateState#exit(PlainMessage)}
     128 + * to ensure that every resource of the module is freed and the state of the app changes back
     129 + * to SELECT. Afterwards, an error is thrown to inform the user.
     130 + *
     131 + * Timeouts of 0 may introduce deadlocks, because this method waits for a response, i.e. a
     132 + * guarantee that the data has been forwarded successfully. If the vulnerable module hangs
     133 + * without shutting down the connection, this method might block indefinitely long.
     134 + *
     135 + * @param message Request that triggered this routine. It will be forwarded to the selected,
     136 + * vulnerable module.
     137 + * @throws InitializationException If initializing internal client fails.
     138 + * @throws VulnerableModuleCommunicationException If communication with the vulnerable module fails.
     139 + * This is thrown in response to any {@link CommunicationException}.
     140 + * @throws VulnerableModuleExitException If vulnerable module terminated itself (gracefully).
     141 + * @throws VulnerableModuleOperationException If {@link PlainMessage#getOperation()} of response
     142 + * returns something else than {@link Operation#SUCCESS}.
     143 + * */
     144 + @Override
     145 + public final void forward(PlainMessage message) throws InitializationException, VulnerableModuleCommunicationException, VulnerableModuleExitException, VulnerableModuleOperationException {
     146 + 
     147 + try {
     148 + // Forward data to internal server. Operation is guaranteed to be FORWARD.
     149 + this.getInternalClient().send(message);
     150 + final PlainMessage response = (PlainMessage) this.getInternalClient().receive();
     151 + if (response.getOperation() == Operation.EXIT) {
     152 + 
     153 + // Handle exit from vulnerable app
     154 + this.exit(null);
     155 + throw new VulnerableModuleExitException("Vulnerable module terminated itself: " + response);
     156 + } else if (response.getOperation() != Operation.SUCCESS) {
     157 + throw new VulnerableModuleOperationException("Operation was not successful: " + response);
     158 + }
     159 + 
     160 + } catch (CommunicationException e) {
     161 + 
     162 + this.exit(null);
     163 + throw new VulnerableModuleCommunicationException("Failed to forward data.", e);
     164 + }
     165 + }
     166 + 
     167 + /**
     168 + * Tries to fetch data from selected, vulnerable module.
     169 + *
     170 + * In case the vulnerable module is not responsive, i.e. either crashed or hung up, this method
     171 + * will run {@link VulnerableCommunicateState#exit(PlainMessage)} to ensure that every resource
     172 + * of the module is freed and the state of the app changes back to SELECT. Afterwards, an error
     173 + * is thrown in order to inform the user.
     174 + *
     175 + * Instead of forwarding the message received via the internal client, this method will
     176 + * extract the contents. This allows for wrapping the contents in a {@link PlainMessage} with
     177 + * {@link com.damnvulnerableapp.networking.messages.Operation#SUCCESS}.
     178 + *
     179 + * Timeouts of 0 may introduce deadlocks, because this method waits for data, i.e. a
     180 + * If the vulnerable module hangs without shutting down the connection, this method might
     181 + * block indefinitely long.
     182 + *
     183 + * @param message Request that triggered this routine. It will be forwarded as a request to the
     184 + * internal server.
     185 + * @return Data fetched from selected, vulnerable module.
     186 + * @throws InitializationException If initializing internal client fails.
     187 + * @throws VulnerableModuleCommunicationException If communication with the vulnerable module fails.
     188 + * This is thrown in response to any {@link CommunicationException}.
     189 + * @throws VulnerableModuleExitException If vulnerable module terminated itself (gracefully).
     190 + * @throws VulnerableModuleOperationException If {@link PlainMessage#getOperation()} of response
     191 + * returns something else than {@link Operation#SUCCESS}.
     192 + * */
     193 + @Override
     194 + public final PlainMessage fetch(PlainMessage message) throws InitializationException, VulnerableModuleCommunicationException, VulnerableModuleExitException, VulnerableModuleOperationException {
     195 + 
     196 + try {
     197 + 
     198 + // Request data from internal server. Operation is guaranteed to be FETCH
     199 + this.getInternalClient().send(message);
     200 + 
     201 + // Wait for response
     202 + final PlainMessage response = (PlainMessage) this.getInternalClient().receive();
     203 + if (response.getOperation() == Operation.EXIT) {
     204 + 
     205 + // Handle exit from vulnerable app
     206 + this.exit(null);
     207 + throw new VulnerableModuleExitException("Vulnerable module terminated itself: " + response);
     208 + } else if (response.getOperation() != Operation.SUCCESS) {
     209 + throw new VulnerableModuleOperationException("Operation was not successful: " + response);
     210 + }
     211 + 
     212 + return response;
     213 + } catch (CommunicationException e) {
     214 + 
     215 + // Any error, under the assumption that module - specific configs are good, is deadly
     216 + this.exit(null);
     217 + throw new VulnerableModuleCommunicationException("Failed to fetch data.", e);
     218 + }
     219 + }
     220 + 
     221 + /**
     222 + * Blocks until the process identifier of the vulnerable module is no longer listed as part of
     223 + * the list of running processes. This is somewhat a busy - wait, but a bit nicer than just
     224 + * calling {@link Thread#sleep(long)}.
     225 + * */
     226 + private void spinOnVulnerableModuleAlive() {
     227 + 
     228 + final Object signal = new Object();
     229 + 
     230 + final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
     231 + executor.scheduleAtFixedRate(new TimerTask() {
     232 + @Override
     233 + public void run() {
     234 + 
     235 + if (!isVulnerableModuleAlive()) {
     236 + synchronized (signal) {
     237 + moduleAlive = false;
     238 + signal.notify();
     239 + }
     240 + this.cancel();
     241 + }
     242 + }
     243 + }, 0, VulnerableCommunicateState.WAIT_INTERVAL, TimeUnit.MILLISECONDS);
     244 + 
     245 + synchronized (signal) {
     246 + while (this.moduleAlive) {
     247 + try {
     248 + signal.wait();
     249 + } catch (InterruptedException ignored) {}
     250 + }
     251 + }
     252 + }
     253 + 
     254 + /**
     255 + * Checks whether a process with a process identifier that matches the pid of the vulnerable
     256 + * module exists. {@link VulnerableCommunicateState#exit(PlainMessage)} can spin on this
     257 + * until the module is dead.
     258 + *
     259 + * @return <code>true</code>, if vulnerable module is alive; <code>false</code> otherwise.
     260 + * */
     261 + private boolean isVulnerableModuleAlive() {
     262 + 
     263 + final int modulePid = this.getContext().getVulnerableModulePid();
     264 + if (modulePid == 0)
     265 + return false;
     266 + 
     267 + final ActivityManager manager = (ActivityManager) ManagerService.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
     268 + final List<ActivityManager.RunningAppProcessInfo> procs = manager.getRunningAppProcesses();
     269 + if (procs != null) {
     270 + for (final ActivityManager.RunningAppProcessInfo info : procs) {
     271 + if (info.pid == modulePid)
     272 + return true;
     273 + }
     274 + }
     275 + 
     276 + return false;
     277 + }
     278 +}
     279 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/controllers/states/VulnerableSelectState.java
     1 +package com.damnvulnerableapp.managerservice.controllers.states;
     2 + 
     3 +import android.content.Intent;
     4 + 
     5 +import com.damnvulnerableapp.common.DynamicClassLoader;
     6 +import com.damnvulnerableapp.common.configuration.ClientConfiguration;
     7 +import com.damnvulnerableapp.common.exceptions.InitializationException;
     8 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleCreationException;
     9 +import com.damnvulnerableapp.common.exceptions.VulnerableModuleOperationException;
     10 +import com.damnvulnerableapp.managerservice.ManagerGlobals;
     11 +import com.damnvulnerableapp.managerservice.ManagerService;
     12 +import com.damnvulnerableapp.managerservice.controllers.ExternalController;
     13 +import com.damnvulnerableapp.common.exceptions.InvalidTransitionException;
     14 +import com.damnvulnerableapp.networking.communication.client.NetworkConnectionInformation;
     15 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     16 +import com.damnvulnerableapp.networking.messages.Operation;
     17 +import com.damnvulnerableapp.networking.messages.Parameter;
     18 +import com.damnvulnerableapp.networking.messages.PlainMessage;
     19 +import com.damnvulnerableapp.vulnerable.VulnerableActivity;
     20 +import com.damnvulnerableapp.vulnerable.VulnerableGlobals;
     21 +import com.damnvulnerableapp.vulnerable.modules.VulnerableModule;
     22 + 
     23 +/**
     24 + * SELECT - state that is the initial state and is always in use, when a client closed a vulnerable
     25 + * module.
     26 + *
     27 + * @author Pascal Kühnemann
     28 + * @version 1.0
     29 + * */
     30 +public final class VulnerableSelectState extends VulnerableAppState {
     31 + 
     32 + /**
     33 + * Amount of connection retries to perform when selecting a module. If this is exceeded, then
     34 + * the app will shut down.
     35 + * */
     36 + private static final int AMOUNT_RETRIES = 5;
     37 + 
     38 + /**
     39 + * Constructs this state by storing the reference to the state manager.
     40 + *
     41 + * @param context State manager, i.e. the context.
     42 + * @throws InitializationException If initializing internal client fails.
     43 + * */
     44 + public VulnerableSelectState(ExternalController context) throws InitializationException {
     45 + super(context, null);
     46 + }
     47 + 
     48 + /**
     49 + * Selects a vulnerable module based on a message sent by an external client. The module name
     50 + * has to be specified in the {@link Parameter#CONTENT} - field of a {@link PlainMessage}. It is
     51 + * supposed to be in a one - to - one correspondence to a class that represents the vulnerable
     52 + * module to load.
     53 + *
     54 + * For each module, there are configurations that specify e.g. timeouts for the internal client.
     55 + * If those configurations cannot be loaded, or the class name is incorrect, then an error will
     56 + * be thrown.
     57 + *
     58 + * After the vulnerable app has been started, this internal client will try to connect to the
     59 + * {@link com.damnvulnerableapp.vulnerable.view.InternalView} and retry on failure until the
     60 + * amount of attempted retries exceed a certain threshold. If the threshold is exceeded, it is
     61 + * assumed that communication with the internal vulnerable module is impossible and therefore
     62 + * this app is shut down. Otherwise, a high - level handshake is performed, in which the
     63 + * vulnerable module communicates its process identifier. This is used to allow the manager to
     64 + * kill the vulnerable module process, if the user requests it (e.g. by sending {@link Operation#EXIT}
     65 + * or disconnecting).
     66 + *
     67 + * Finally, this method changes the state to communication.
     68 + *
     69 + * @throws InitializationException If initializing internal client fails.
     70 + * @throws VulnerableModuleOperationException If dynamically loading module configurations fails.
     71 + * */
     72 + @Override
     73 + public final void select(PlainMessage message) throws InitializationException, VulnerableModuleOperationException, VulnerableModuleCreationException {
     74 + 
     75 + // Check content, which specifies what vulnerable module to load
     76 + final String moduleName = new String(message.getParameters().get(Parameter.CONTENT));
     77 + 
     78 + // Load module - specific configurations
     79 + if (DynamicClassLoader.getInstance().checkClass(
     80 + VulnerableModule.class.getPackage(),
     81 + moduleName,
     82 + VulnerableModule.class)) {
     83 + 
     84 + final ClientConfiguration configuration = (ClientConfiguration) DynamicClassLoader.getInstance().loadClass(
     85 + VulnerableModule.class.getPackage(),
     86 + moduleName + "Configuration",
     87 + ClientConfiguration.class
     88 + );
     89 + if (configuration == null)
     90 + throw new VulnerableModuleOperationException("Failed to load module configurations.");
     91 + 
     92 + this.getInternalClient().setConfiguration(configuration);
     93 + } else {
     94 + throw new VulnerableModuleOperationException("Failed to find vulnerable module.");
     95 + }
     96 + 
     97 + // Load vulnerable activity
     98 + final Intent intent = new Intent(ManagerService.getInstance(), VulnerableActivity.class);
     99 + intent.putExtra(ManagerGlobals.FACTORY_INTENT_KEY, this.getFactory().getClass().getSimpleName());
     100 + intent.putExtra(ManagerGlobals.MODULE_INTENT_KEY, moduleName);
     101 + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     102 + ManagerService.getInstance().startActivity(intent);
     103 + 
     104 + // Connect internal client to vulnerable module -> could throw exception, if initialization failed
     105 + // Might also throw exception if this method is faster than VulnerableActivity
     106 + // -> try fixed amount of times with timeouts after each try
     107 + int i = 0;
     108 + while (i < VulnerableSelectState.AMOUNT_RETRIES) {
     109 + 
     110 + try {
     111 + 
     112 + this.getInternalClient().connect(
     113 + new NetworkConnectionInformation(
     114 + VulnerableGlobals.HOST,
     115 + VulnerableGlobals.PORT,
     116 + VulnerableGlobals.CONNECT_TIMEOUT
     117 + )
     118 + );
     119 + break;
     120 + } catch (CommunicationException e) {
     121 + i++;
     122 + }
     123 + 
     124 + try {
     125 + Thread.sleep(1000);
     126 + } catch (InterruptedException ignored) {}
     127 + }
     128 + 
     129 + if (i >= VulnerableSelectState.AMOUNT_RETRIES) {
     130 + //this.shutdown();
     131 + throw new VulnerableModuleCreationException("Failed to connect to vulnerable module.");
     132 + }
     133 + 
     134 + // Send select request -> high - level handshake
     135 + try {
     136 + 
     137 + this.getInternalClient().send(
     138 + new PlainMessage(
     139 + Operation.SELECT,
     140 + "gogogo"
     141 + )
     142 + );
     143 + } catch (CommunicationException e) {
     144 + //this.shutdown();
     145 + throw new VulnerableModuleCreationException("Failed to send message.", e);
     146 + }
     147 + 
     148 + // Await response
     149 + try {
     150 + 
     151 + final PlainMessage response = (PlainMessage) this.getInternalClient().receive();
     152 + if (response.getOperation() != Operation.SUCCESS) {
     153 + //this.shutdown();
     154 + throw new VulnerableModuleCreationException("Failed selecting module.");
     155 + }
     156 + 
     157 + // Content field contains pid of process
     158 + final byte[] content = response.getParameters().get(Parameter.CONTENT);
     159 + if (content == null) {
     160 + //this.shutdown();
     161 + throw new VulnerableModuleCreationException("Failed retrieving pid of vulnerable module.");
     162 + }
     163 + this.getContext().setVulnerableModulePid(Integer.parseInt(new String(content)));
     164 + 
     165 + } catch (CommunicationException e) {
     166 + //this.shutdown();
     167 + throw new VulnerableModuleCreationException("Failed to receive response.", e);
     168 + }
     169 + 
     170 + // Transition to VulnerableCommunicateState, if previous steps are successful
     171 + this.getContext().changeState(new VulnerableCommunicateState(this.getContext(), this.getInternalClient()));
     172 + }
     173 + 
     174 + /**
     175 + * Invalid operation in this state. It must not possible to exit a module, if there is no module
     176 + * selected.
     177 + *
     178 + * @param ignored Request that triggered this routine. Will be ignored.
     179 + * @throws InvalidTransitionException Always thrown, because this operation is not permitted in
     180 + * this state, i.e. in the SELECT - state.
     181 + * */
     182 + @Override
     183 + public final void exit(PlainMessage ignored) throws InvalidTransitionException {
     184 + throw new InvalidTransitionException(
     185 + "Cannot EXIT from SELECT - state. There is not a selected module."
     186 + );
     187 + }
     188 + 
     189 + /**
     190 + * Invalid operation in this state. It must not be possible to send messages to a module, if
     191 + * there is no module selected.
     192 + *
     193 + * @param ignored Request that triggered this routine. Will be ignored.
     194 + * @throws InvalidTransitionException Always thrown, because this operation is not permitted in
     195 + * this state, i.e. in the SELECT - state.
     196 + * */
     197 + @Override
     198 + public final void forward(PlainMessage ignored) throws InvalidTransitionException {
     199 + throw new InvalidTransitionException(
     200 + "Cannot FORWARD message in SELECT - state. There is not a selected module."
     201 + );
     202 + }
     203 + 
     204 + /**
     205 + * Invalid operation in this state. It must not be possible to receive messages from a module, if
     206 + * there is no module selected.
     207 + *
     208 + * @param ignored Request that triggered this routine. Will be ignored.
     209 + * @return Nothing. Will always trigger {@link InvalidTransitionException}.
     210 + * @throws InvalidTransitionException Always thrown, because this operation is not permitted in
     211 + * this state, i.e. in the SELECT - state.
     212 + * */
     213 + @Override
     214 + public final PlainMessage fetch(PlainMessage ignored) throws InvalidTransitionException{
     215 + throw new InvalidTransitionException(
     216 + "Cannot FETCH message in SELECT - state. There is not a selected module."
     217 + );
     218 + }
     219 +}
     220 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/managerservice/views/ExternalView.java
     1 +package com.damnvulnerableapp.managerservice.views;
     2 + 
     3 +import com.damnvulnerableapp.common.configuration.ClientExitConfiguration;
     4 +import com.damnvulnerableapp.common.exceptions.InitializationException;
     5 +import com.damnvulnerableapp.managerservice.controllers.ExternalController;
     6 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     7 +import com.damnvulnerableapp.networking.communication.server.NetworkBindInformation;
     8 +import com.damnvulnerableapp.networking.communication.server.NetworkServer;
     9 +import com.damnvulnerableapp.networking.exceptions.BindException;
     10 +import com.damnvulnerableapp.networking.exceptions.CreationException;
     11 +import com.damnvulnerableapp.networking.exceptions.InternalSocketException;
     12 +import com.damnvulnerableapp.networking.protocol.PlainProtocolFactory;
     13 + 
     14 +/**
     15 + * User - interface in form of a {@link NetworkServer}(TCP). This is what an external user will interact
     16 + * with when trying to use this application.
     17 + *
     18 + * @author Pascal Kühnemann
     19 + * @version 1.0
     20 + * */
     21 +public final class ExternalView {
     22 + 
     23 + /**
     24 + * Hostname and port, on which to bind the {@link NetworkServer}.
     25 + * */
     26 + private static final String HOST = "127.0.0.1";
     27 + private static final int PORT = 8080;
     28 + 
     29 + /**
     30 + * Server that handles client connections and represents the "UI".
     31 + * @see NetworkServer
     32 + * */
     33 + private final NetworkServer server;
     34 + 
     35 + /**
     36 + * Constructs the {@link NetworkServer} w.r.t. a {@link PlainProtocolFactory}. This ensures
     37 + * that all messages are of type {@link com.damnvulnerableapp.networking.messages.PlainMessage},
     38 + * which is a message format that supports sending binary data.
     39 + *
     40 + * @see NetworkFactory
     41 + * @see NetworkServer
     42 + * @see com.damnvulnerableapp.networking.messages.PlainMessage
     43 + * @see PlainProtocolFactory
     44 + * */
     45 + public ExternalView() {
     46 + 
     47 + final NetworkFactory factory = new NetworkFactory();
     48 + factory.setProtocolFactory(new PlainProtocolFactory()); // -> always PlainMessage's
     49 + this.server = (NetworkServer) factory.createServer();
     50 + }
     51 + 
     52 + /**
     53 + * Initializes this view by assigning an instance of {@link ExternalController} to the server.
     54 + * This is done by registering {@link ExternalController} as a {@link com.damnvulnerableapp.networking.communication.client.CommunicationListener}.
     55 + *
     56 + * Also, the {@link NetworkServer} is bound and started.
     57 + *
     58 + * @throws BindException If binding the {@link NetworkServer} fails.
     59 + * @throws CreationException If creating a network resource fails.
     60 + * @throws InitializationException If initializing internal client fails.
     61 + * @throws InternalSocketException If an internal socket error occurs. E.g. {@link java.net.Socket#setSoTimeout(int)}
     62 + * could fail due to an internal protocol(TCP) error.
     63 + * @see com.damnvulnerableapp.networking.communication.client.CommunicationListener
     64 + * @see ExternalController
     65 + * */
     66 + public final void init() throws BindException, CreationException, InitializationException, InternalSocketException {
     67 + 
     68 + // Create controller and initialize it
     69 + final ExternalController controller = new ExternalController(this);
     70 + 
     71 + // Add controller as listener to server
     72 + this.server.addCommunicationListener(controller);
     73 + 
     74 + // Bind server
     75 + this.server.bind(new NetworkBindInformation(ExternalView.HOST, ExternalView.PORT));
     76 + 
     77 + // Start server asynchronously
     78 + this.server.startAsync();
     79 + }
     80 + 
     81 + /**
     82 + * Closes all resources that this view manages. Primarily, the {@link NetworkServer} will be
     83 + * closed.
     84 + * */
     85 + public final void close() {
     86 + 
     87 + // Shutdown server
     88 + this.server.setClientConfiguration(new ClientExitConfiguration());
     89 + this.server.close();
     90 + }
     91 +}
     92 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/CommunicationFactory.java
     1 +package com.damnvulnerableapp.networking.communication;
     2 + 
     3 +import com.damnvulnerableapp.networking.communication.client.Client;
     4 +import com.damnvulnerableapp.networking.communication.server.Server;
     5 +import com.damnvulnerableapp.networking.protocol.ProtocolFactory;
     6 + 
     7 +/**
     8 + * A class that constructs clients and servers that share a common interface and technical
     9 + * implementation. The protocol used by client and server is determined by calling
     10 + *
     11 + * @author Pascal Kühnemann
     12 + * @version 1.0
     13 + * */
     14 +public abstract class CommunicationFactory {
     15 + 
     16 + /**
     17 + * Factory for constructing a common protocol that is used by client and server.
     18 + * @see ProtocolFactory
     19 + * */
     20 + private ProtocolFactory protocolFactory;
     21 + 
     22 + /**
     23 + * Fixes a protocol creator to use when creating client and server objects. Every {@link Client}
     24 + * and {@link Server} instance will be given an instance of the {@link com.damnvulnerableapp.networking.protocol.Protocol}
     25 + * that is returned by <code>protocolFactory</code>.
     26 + *
     27 + * Notice that there are no guarantees regarding compatibility among two different protocols.
     28 + * @param protocolFactory Protocol creator that will be used to dynamically create instances of
     29 + * {@link com.damnvulnerableapp.networking.protocol.Protocol}.
     30 + * @see ProtocolFactory
     31 + * */
     32 + public void setProtocolFactory(ProtocolFactory protocolFactory) {
     33 + this.protocolFactory = protocolFactory;
     34 + }
     35 + 
     36 + /**
     37 + * Returns currently selected {@link ProtocolFactory} that is used to create instances of
     38 + * {@link com.damnvulnerableapp.networking.protocol.Protocol}.
     39 + * @return Instance of selected {@link ProtocolFactory}; or null, if this is called before
     40 + * {@link CommunicationFactory#setProtocolFactory(ProtocolFactory)}.
     41 + * @see ProtocolFactory
     42 + * */
     43 + public ProtocolFactory getProtocolFactory() {
     44 + return this.protocolFactory;
     45 + }
     46 + 
     47 + /**
     48 + * Instantiates an implementation of {@link Client}. This can be used to send requests to a
     49 + * corresponding {@link Server}. Notice that by calling {@link CommunicationFactory#setProtocolFactory(ProtocolFactory)}
     50 + * in between {@link CommunicationFactory#createClient()} and {@link CommunicationFactory#createServer()}
     51 + * can result in incompatible clients and servers.
     52 + * @return Instance of an implementation of {@link Client}.
     53 + * @see Client
     54 + * */
     55 + public abstract Client createClient();
     56 + 
     57 + /**
     58 + * Instantiates an implementation of {@link Server}. This instance can be used to handle client
     59 + * requests. Notice that by calling {@link CommunicationFactory#setProtocolFactory(ProtocolFactory)}
     60 + * in between {@link CommunicationFactory#createClient()} and {@link CommunicationFactory#createServer()}
     61 + * can result in incompatible clients and servers.
     62 + * @return Instance of an implementation of {@link Server}.
     63 + * @see Server
     64 + * */
     65 + public abstract Server createServer();
     66 +}
     67 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/NetworkFactory.java
     1 +package com.damnvulnerableapp.networking.communication;
     2 + 
     3 +import com.damnvulnerableapp.networking.communication.client.Client;
     4 +import com.damnvulnerableapp.networking.communication.client.NetworkClient;
     5 +import com.damnvulnerableapp.networking.communication.server.NetworkServer;
     6 +import com.damnvulnerableapp.networking.communication.server.Server;
     7 +import com.damnvulnerableapp.networking.protocol.ProtocolFactory;
     8 + 
     9 +/**
     10 + * Factory producing socket - based clients and servers that speak a common protocol. For the
     11 + * latter it is assumed that there is no call to {@link CommunicationFactory#setProtocolFactory(ProtocolFactory)}
     12 + * in between a call to {@link NetworkFactory#createClient()} and {@link NetworkFactory#createServer()}.
     13 + *
     14 + * @author Pascal Kühnemann
     15 + * @version 1.0
     16 + * @see CommunicationFactory
     17 + * */
     18 +public class NetworkFactory extends CommunicationFactory{
     19 + 
     20 + /**
     21 + * Creates an instance of a socket - based client. Also, using this {@link ProtocolFactory} set
     22 + * via {@link CommunicationFactory#setProtocolFactory(ProtocolFactory)}, the client will be
     23 + * given a {@link com.damnvulnerableapp.networking.protocol.Protocol} such that it can
     24 + * communicate with a server that speaks the same protocol.
     25 + * @return Instance of a socket - based client.
     26 + * @see com.damnvulnerableapp.networking.protocol.Protocol
     27 + * @see ProtocolFactory
     28 + * @see Client
     29 + * @see Server
     30 + */
     31 + @Override
     32 + public Client createClient() {
     33 + 
     34 + NetworkClient client = new NetworkClient();
     35 + client.setProtocol(this.getProtocolFactory().createClientProtocol());
     36 + return client;
     37 + }
     38 + 
     39 + /**
     40 + * Creates an instance of a socket - based server, i.e. it internally uses {@link java.net.ServerSocket}.
     41 + * Also, using this {@link ProtocolFactory} set via
     42 + * {@link CommunicationFactory#setProtocolFactory(ProtocolFactory)}, the server will be
     43 + * given a {@link com.damnvulnerableapp.networking.protocol.Protocol} such that it can
     44 + * communicate with connected clients that speaks the same protocol.
     45 + * @return Instance of a socket - based server.
     46 + * @see com.damnvulnerableapp.networking.protocol.Protocol
     47 + * @see ProtocolFactory
     48 + * @see Server
     49 + * @see Client
     50 + * */
     51 + @Override
     52 + public Server createServer() {
     53 + 
     54 + NetworkServer server = new NetworkServer();
     55 + server.setProtocol(this.getProtocolFactory().createServerProtocol());
     56 + return server;
     57 + }
     58 +}
     59 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/Client.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +import androidx.annotation.NonNull;
     4 + 
     5 +import com.damnvulnerableapp.common.configuration.ClientConfiguration;
     6 +import com.damnvulnerableapp.common.configuration.Configurable;
     7 +import com.damnvulnerableapp.common.configuration.Configuration;
     8 +import com.damnvulnerableapp.networking.communication.CommunicationFactory;
     9 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     10 +import com.damnvulnerableapp.networking.exceptions.ConnectionException;
     11 +import com.damnvulnerableapp.networking.exceptions.InvalidClientException;
     12 +import com.damnvulnerableapp.networking.exceptions.MessageParserException;
     13 +import com.damnvulnerableapp.networking.exceptions.TimeoutException;
     14 +import com.damnvulnerableapp.networking.messages.Message;
     15 +import com.damnvulnerableapp.networking.protocol.Protocol;
     16 + 
     17 +import java.util.LinkedList;
     18 +import java.util.List;
     19 + 
     20 +/**
     21 + * A client that can be used to send requests to a {@link com.damnvulnerableapp.networking.communication.server.Server}.
     22 + * It is by default independent of any communication - specific implementations, i.e. one may
     23 + * implement communication via file mappings, socket etc. This client is a wrapper for those
     24 + * implementations and provides an easy way to handle messages.
     25 + *
     26 + * @author Pascal Kühnemann
     27 + * @see com.damnvulnerableapp.networking.communication.server.Server
     28 + * @see Configurable
     29 + * */
     30 +public abstract class Client implements Configurable {
     31 + 
     32 + /**
     33 + * Type or identifier of the client. This is useful when connecting to a server that cares
     34 + * about the functionality of a client. E.g. a client could be a "normal" user, an automated
     35 + * command handler etc. Defaults to {@link ClientType#USER}.
     36 + * @see ClientType
     37 + * */
     38 + private ClientType type;
     39 + 
     40 + /**
     41 + * Wrapping communication layer that lies between the low - level implementation of
     42 + * communication and this client. It determines how messages are sent, how to handle initial
     43 + * connects and how to shutdown a connection.
     44 + * @see Protocol
     45 + * */
     46 + private Protocol protocol;
     47 + 
     48 + /**
     49 + * Low - level implementation of communication. This may e.g. be a socket - based or file
     50 + * mapping - based implementation. Depending on the setting, on implementation may be more
     51 + * stable than the other.
     52 + * @see EndPoint
     53 + * */
     54 + private EndPoint endpoint;
     55 + 
     56 + /**
     57 + * A list of {@link CommunicationListener} objects that will be called on certain events. Any
     58 + * method that triggers an event will handle calling the correct callbacks. It may be possible
     59 + * that an event is fired multiple times, because or parallelism.
     60 + * @see CommunicationListener
     61 + * */
     62 + private final List<CommunicationListener> listeners;
     63 + 
     64 + /**
     65 + * {@link com.damnvulnerableapp.common.configuration.Configuration} class holding information on
     66 + * low - level - specific details.
     67 + * @see com.damnvulnerableapp.common.configuration.Configuration
     68 + * */
     69 + private ClientConfiguration configuration;
     70 + 
     71 + /**
     72 + * Construct list of {@link CommunicationListener} and this {@link com.damnvulnerableapp.common.configuration.Configuration}.
     73 + *
     74 + * This construct will be called, if a new client is created that actually wants to perform
     75 + * connect operations.
     76 + *
     77 + * Do NOT call any constructor of this class by instantiating a subclass. Rather use
     78 + * {@link CommunicationFactory#createClient()} for creating clients.
     79 + * */
     80 + public Client() {
     81 + 
     82 + this.listeners = new LinkedList<>();
     83 + 
     84 + this.setConfiguration(new ClientConfiguration());
     85 + 
     86 + this.type = ClientType.USER;
     87 + }
     88 + 
     89 + /**
     90 + * Construct this client as in {@link Client#Client()}. Additionally, this fixes a low - level
     91 + * implementation that is already created, i.e. this constructor will be used by a
     92 + * {@link com.damnvulnerableapp.networking.communication.server.Server} when accepting incoming
     93 + * client connections.
     94 + *
     95 + * Do NOT call any constructor of this class by instantiating a subclass. Rather use
     96 + * {@link CommunicationFactory#createClient()} for creating clients.
     97 + *
     98 + * @param endpoint Low - level implementation constructed by {@link com.damnvulnerableapp.networking.communication.server.Server}
     99 + * when accepting incoming connection.
     100 + * */
     101 + public Client(EndPoint endpoint) {
     102 + this();
     103 + this.endpoint = endpoint;
     104 + }
     105 + 
     106 + /**
     107 + * Connects to a given target, described by <code>info</code>. If the low - level implementation
     108 + * is already up and running, this method will omit creating a new instance of {@link EndPoint}
     109 + * and immediately start with a {@link Protocol#handshake(EndPoint, ClientType)}. After the
     110 + * {@link Protocol#handshake(EndPoint, ClientType)}, all {@link CommunicationListener#onConnect(Client)}
     111 + * will be called.
     112 + *
     113 + * A {@link com.damnvulnerableapp.networking.communication.server.Server}, which is accepting
     114 + * incoming client connections, also calls this method, but with an already connected endpoint.
     115 + * Therefore, for server - side clients, only the handshake and listeners are invoked.
     116 + *
     117 + * @param info Information on where and how to connect. This depends on the low - level
     118 + * implementation of {@link EndPoint}.
     119 + * @throws ConnectionException If communication partner disconnects unexpectedly, the low - level
     120 + * implementation unexpectedly fails creating a connection or the
     121 + * {@link Protocol#handshake(EndPoint, ClientType)} fails.
     122 + * @throws MessageParserException If parsing a message from {@link Protocol#handshake(EndPoint, ClientType)}
     123 + * fails.
     124 + * @throws TimeoutException If connecting to a server or reading exceeds a configured time limit.
     125 + * @see Protocol
     126 + * @see EndPoint
     127 + * */
     128 + public void connect(ConnectionInformation info) throws CommunicationException {
     129 + 
     130 + // If endpoint is not in connected state: connect it
     131 + if (!this.isConnected()) {
     132 + 
     133 + this.endpoint = this.createEndPoint();
     134 + this.setConfiguration(this.configuration);
     135 + 
     136 + final int oldTimeout = this.endpoint.getTimeout();
     137 + this.endpoint.setTimeout(info.getTimeout());
     138 + this.endpoint.connect(info);
     139 + this.endpoint.setTimeout(oldTimeout);
     140 + }
     141 + 
     142 + try {
     143 + 
     144 + final int oldTimeout = this.endpoint.getTimeout();
     145 + this.endpoint.setTimeout(this.configuration.getHandshakeTimeout());
     146 + this.protocol.handshake(this.endpoint, this.type);
     147 + this.endpoint.setTimeout(oldTimeout);
     148 + } catch (InvalidClientException ignored) {}
     149 + 
     150 + for (CommunicationListener listener : this.listeners)
     151 + listener.onConnect(this);
     152 + }
     153 + 
     154 + /**
     155 + * Disconnect this client from established connection. Before shutting down the connection
     156 + * and closing this {@link EndPoint}, all {@link CommunicationListener#onDisconnect(Client)}
     157 + * will be called.
     158 + *
     159 + * Before closing resources used in the internal {@link EndPoint} instance, {@link Protocol#shutdown(EndPoint)}
     160 + * is used to aim for a graceful shutdown. As this method may be called on closed clients
     161 + * as well in order to insure that all internal resources are closed, all exceptions of
     162 + * {@link Protocol#shutdown(EndPoint)} will be discarded.
     163 + *
     164 + * This method may also be called, if the connection is already closed, i.e. to free resources
     165 + * used by internal objects.
     166 + *
     167 + * @see Protocol
     168 + * @see EndPoint
     169 + * */
     170 + public void disconnect() {
     171 + 
     172 + for (CommunicationListener listener : this.listeners)
     173 + listener.onDisconnect(this);
     174 + 
     175 + if (this.isConnected()) {
     176 + try {
     177 + // Hope that gracefully shutting down connections works...
     178 + this.protocol.shutdown(this.endpoint);
     179 + } catch (CommunicationException ignored) {
     180 + }
     181 + }
     182 + if (this.endpoint != null)
     183 + this.endpoint.disconnect();
     184 + }
     185 + 
     186 + /**
     187 + * Checks whether the underlying {@link EndPoint} is still connected to a server.
     188 + *
     189 + * @return <code>true</code>, if {@link EndPoint} is connected; <code>false</code> otherwise.
     190 + * @see EndPoint#isConnected()
     191 + * */
     192 + public boolean isConnected() {
     193 + if (this.endpoint != null)
     194 + return this.endpoint.isConnected();
     195 + return false;
     196 + }
     197 + 
     198 + /**
     199 + * Sends a {@link Message} to a communication partner using a fixed {@link Protocol}. Before
     200 + * sending the message, if this client is still connected, all {@link CommunicationListener#onSend(Client, Message)}
     201 + * will be called.
     202 + *
     203 + * If there is a connection issue, i.e. internally some method throws {@link ConnectionException},
     204 + * then this client will automatically be disconnected.
     205 + *
     206 + * @param message Message to send to the communication partner using a specified protocol.
     207 + * Setting this to <code>null</code> will result in protocol - dependent handling
     208 + * of the message.
     209 + * @throws ConnectionException If communication partner disconnects unexpectedly, the low - level
     210 + * implementation unexpectedly fails receiving a message or unexpectedly
     211 + * fails sending a message. Also the low - level implementation may
     212 + * be not connected anymore due to unknown reasons. This client is
     213 + * automatically disconnected. Also thrown if client was not connected
     214 + * via {@link Client#connect(ConnectionInformation)}.
     215 + * @throws MessageParserException If <code>message</code> is of an invalid format w.r.t. the
     216 + * underlying protocol.
     217 + * @throws TimeoutException If sending a message exceeds a preconfigured time limit. This may be
     218 + * linked to waiting for ACK messages after a sent message.
     219 + * @throws CommunicationException If any communication - related error occurs. It will enforce
     220 + * a disconnect.
     221 + * @see Protocol
     222 + * @see Message
     223 + * @see com.damnvulnerableapp.networking.messages.MessageParser
     224 + * */
     225 + public void send(Message message) throws CommunicationException {
     226 + 
     227 + if (!this.isConnected())
     228 + throw new ConnectionException("Client is not connected.");
     229 + 
     230 + for (CommunicationListener listener : this.listeners)
     231 + listener.onSend(this, message);
     232 + 
     233 + // This will throw an exception on disconnected endpoint.
     234 + try {
     235 + this.protocol.send(this.endpoint, message);
     236 + } catch (CommunicationException e) {
     237 + 
     238 + // Handle disconnect
     239 + this.disconnect();
     240 + throw e;
     241 + }
     242 + }
     243 + 
     244 + /**
     245 + * Receives a message by blocking until there is a message available.
     246 + *
     247 + * If there is a connection issue, i.e. internally some method throws {@link ConnectionException},
     248 + * then this client will automatically be disconnected.
     249 + *
     250 + * Receiving a message will trigger all {@link CommunicationListener#onReceive(Client, Message)}
     251 + * and thus a client can post requests either by actively using {@link Client#send(Message)} or
     252 + * reactively by calling {@link Client#send(Message)} in {@link CommunicationListener#onReceive(Client, Message)}.
     253 + *
     254 + * E.g. in {@link com.damnvulnerableapp.networking.communication.server.NetworkServer}, there the
     255 + * clients will permanently listen for incoming requests, forward the requests to
     256 + * {@link CommunicationListener#onReceive(Client, Message)}, which in turn may reactively answer
     257 + * the requests with corresponding responses messages sent via {@link Client#send(Message)}.
     258 + *
     259 + * @return Message If a valid message is received, then this will be returned; null otherwise.
     260 + * @throws ConnectionException If communication partner disconnects unexpectedly, the low - level
     261 + * implementation unexpectedly fails receiving a message or unexpectedly
     262 + * fails sending a message. Also the low - level implementation may
     263 + * be not connected anymore due to unknown reasons. This client is
     264 + * automatically disconnected. Also thrown if client was not connected
     265 + * via {@link Client#connect(ConnectionInformation)}.
     266 + * @throws MessageParserException If a received message is of an invalid format.
     267 + * @throws TimeoutException If waiting for an incoming message exceeds a preconfigured time limit.
     268 + * @throws CommunicationException If any communication - related error occurs.
     269 + * @see Protocol
     270 + * @see Message
     271 + * @see com.damnvulnerableapp.networking.messages.MessageParser
     272 + * */
     273 + public Message receive() throws CommunicationException {
     274 + 
     275 + if (!this.isConnected())
     276 + throw new ConnectionException("Client is not connected.");
     277 + 
     278 + Message message;
     279 + try {
     280 + message = this.protocol.receive(this.endpoint);
     281 + } catch (ConnectionException e) {
     282 + 
     283 + // Handle disconnect
     284 + this.disconnect();
     285 + throw e;
     286 + } catch (MessageParserException e) {
     287 + 
     288 + // Handle invalid messages
     289 + for (CommunicationListener listener : this.listeners)
     290 + listener.onInvalidMessage(this, e.getInvalidMessage());
     291 + 
     292 + throw e;
     293 + }
     294 + if (message == null)
     295 + return null;
     296 + 
     297 + for (CommunicationListener listener : this.listeners)
     298 + listener.onReceive(this, message);
     299 + 
     300 + return message;
     301 + }
     302 + 
     303 + /**
     304 + * Adds a {@link CommunicationListener} to this list of communication listeners. Its callbacks
     305 + * will be invoked on specified event.
     306 + *
     307 + * @param listener Instance of {@link CommunicationListener} that should be invoked on events.
     308 + * @see CommunicationListener
     309 + * */
     310 + public void addCommunicationListener(CommunicationListener listener) {
     311 + this.listeners.add(listener);
     312 + }
     313 + 
     314 + /**
     315 + * Removes a {@link CommunicationListener} from this list of registered listeners. Therefore
     316 + * its callbacks will no longer be invoked on occurring events.
     317 + *
     318 + * @param listener Listener to remove.
     319 + * @see CommunicationListener
     320 + * */
     321 + public void removeCommunicationListener(CommunicationListener listener) {
     322 + this.listeners.remove(listener);
     323 + }
     324 + 
     325 + /**
     326 + * Specifies this {@link Protocol} to be used for communication with a server.
     327 + *
     328 + * Do NOT call this method. It is used in {@link CommunicationFactory} for linking a protocol
     329 + * to clients and servers. Calling this method may result in connection crashes etc.
     330 + *
     331 + * @param protocol {@link Protocol} to use for communication.
     332 + * @see Protocol
     333 + * */
     334 + public void setProtocol(Protocol protocol) {
     335 + this.protocol = protocol;
     336 + }
     337 + 
     338 + /**
     339 + * Specifies this {@link ClientType} that identifies a client's functionality.
     340 + *
     341 + * @param type Identifier for client's functionality.
     342 + * @see ClientType
     343 + * */
     344 + public void setType(ClientType type) {
     345 + this.type = type;
     346 + }
     347 + 
     348 + /**
     349 + * Provides a {@link ClientConfiguration} to be used for various configurable aspects of this
     350 + * client. Primarily, this will have an effect on this internal {@link EndPoint}. This will
     351 + * overwrite the default configuration.
     352 + *
     353 + * This method should be called before calling any other method of {@link Client}.
     354 + *
     355 + * @param configuration New configuration to use.
     356 + * @see ClientConfiguration
     357 + * @see Configurable
     358 + * */
     359 + @Override
     360 + public void setConfiguration(Configuration configuration) {
     361 + // Check if configuration is a subclass of EndPointConfiguration
     362 + if (ClientConfiguration.class.isAssignableFrom(configuration.getClass())) {
     363 + this.configuration = (ClientConfiguration) configuration;
     364 + if (this.endpoint != null) {
     365 + try {
     366 + this.endpoint.setTimeout(this.configuration.getEndpointTimeout());
     367 + } catch (CommunicationException ignored) {}
     368 + }
     369 + }
     370 + }
     371 + 
     372 + /**
     373 + * Returns this {@link ClientConfiguration} that is currently active. This method will be useful
     374 + * when implementing a new {@link com.damnvulnerableapp.networking.communication.server.Server}.
     375 + *
     376 + * @return This configuration that is currently active.
     377 + * @see ClientConfiguration
     378 + * @see Configurable
     379 + * */
     380 + @NonNull
     381 + @Override
     382 + public Configuration getConfiguration() {
     383 + return this.configuration;
     384 + }
     385 + 
     386 + // Factory method pattern. This outsources "raw" communication impl.
     387 + /**
     388 + * Constructs an instance of the low - level implementation for communication. E.g. this can be
     389 + * a socket - based or file - mapping - based approach. Any {@link Client} - implementation has
     390 + * to specify how communication will be done.
     391 + *
     392 + * @return Instance of a low - level implementation for communication.
     393 + * @throws CommunicationException If establishing a connection fails. This should only happen, if
     394 + * the {@link EndPoint} is used in conjunction with a
     395 + * {@link com.damnvulnerableapp.networking.communication.server.Server}.
     396 + * */
     397 + protected abstract EndPoint createEndPoint() throws CommunicationException;
     398 +}
     399 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/ClientType.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +/**
     4 + * Type/identifier of a client. This is useful in settings where a client has to tell a server
     5 + * about its functionality/task.
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * */
     10 +public enum ClientType {
     11 + /**
     12 + * Indicates that a client is a (real) user, i.e. an uncontrollable entity, from which to
     13 + * expect further instructions.
     14 + * */
     15 + USER("USER"),
     16 + 
     17 + /**
     18 + * Indicates that a client is a command handler, i.e. it is responsible to forward commands
     19 + * between internal components to e.g. ensure a graceful application shutdown or resource close.
     20 + * */
     21 + COMMAND("COMMAND"),
     22 + 
     23 + /**
     24 + * Indicates that a client is representing the I/O of a program, i.e. its input component will
     25 + * be forwarded to an internal program, whereas its output component will be fetched and
     26 + * forwarded to whoever created this client.
     27 + * */
     28 + IO("IO"),
     29 + 
     30 + /**
     31 + * Indicates that a client is constructed by a {@link com.damnvulnerableapp.networking.communication.server.Server}.
     32 + * Therefore it is just responsible for handling events via {@link CommunicationListener}. It
     33 + * is not allowed to connect two clients of this type.
     34 + * */
     35 + MANAGER("MANAGER");
     36 + 
     37 + /**
     38 + * Enum value.
     39 + * */
     40 + private final String type;
     41 + 
     42 + /**
     43 + * Set enum value.
     44 + *
     45 + * @param type Value of the enum indicating a client type.
     46 + * */
     47 + ClientType(String type) {this.type = type;}
     48 + 
     49 + /**
     50 + * Converts enum value to a string.
     51 + *
     52 + * @return String value of the enum.
     53 + * */
     54 + @Override
     55 + public String toString() {return this.type;}
     56 +}
     57 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/CommunicationListener.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +import com.damnvulnerableapp.networking.messages.Message;
     4 + 
     5 +/**
     6 + * Listener for communication events. These methods are invoked by the {@link Client}, with
     7 + * whom this listener is registered (via {@link Client#addCommunicationListener(CommunicationListener)}).
     8 + *
     9 + * @author Pascal Kühnemann
     10 + * @version 1.0
     11 + * */
     12 +public interface CommunicationListener {
     13 + 
     14 + /**
     15 + * Handles the event that a client connected successfully, i.e. also {@link com.damnvulnerableapp.networking.protocol.Protocol#handshake(EndPoint, ClientType)}
     16 + * was successful.
     17 + *
     18 + * @param client Client that triggered the event.
     19 + * @see Client
     20 + * */
     21 + void onConnect(Client client);
     22 + 
     23 + /**
     24 + * Handles the event that a client wants to disconnect. This callback is invoked right before
     25 + * any measures are taken to perform a disconnect. Therefore, finalizing messages can be
     26 + * exchanged before shutting down the connection.
     27 + *
     28 + * Circumstances, under which this event is triggered vary from a user calling {@link Client#disconnect()}
     29 + * to connection errors occurring in {@link Client#send(Message)} and {@link Client#receive()}.
     30 + *
     31 + * This callback is called at least once for a disconnect event. It may be called multiple times!
     32 + *
     33 + * @param client Client that triggered the event.
     34 + * */
     35 + void onDisconnect(Client client);
     36 + 
     37 + /**
     38 + * Handles the event that a client wants to send a message. This callback is called right before
     39 + * the actual sending is performed (technically allowing to transform this into a chain of
     40 + * responsibility pattern, if necessary). Do NOT call {@link Client#send(Message)} inside of this
     41 + * method. Otherwise this may result in infinite recursion.
     42 + *
     43 + * @param client Client that wants to send a message.
     44 + * @param message Message that will be sent.
     45 + * */
     46 + void onSend(Client client, Message message);
     47 + 
     48 + /**
     49 + * Handles the event that a client has received a message.
     50 + *
     51 + * This allows for constructing a request - response pattern, as a {@link com.damnvulnerableapp.networking.communication.server.Server}
     52 + * may outsource any client into a thread that permanently calls {@link Client#receive()}. If
     53 + * a message is received, this event is triggered, allowing a handler to send a response by
     54 + * invoking {@link Client#send(Message)} inside of this callback.
     55 + *
     56 + * @param client Client that received a message.
     57 + * @param message Message that has been received.
     58 + * */
     59 + void onReceive(Client client, Message message);
     60 + 
     61 + /**
     62 + * Handles the event that a client received an invalid message.
     63 + *
     64 + * It is possible that due to transmission errors etc. a message is not in a proper format, i.e.
     65 + * a {@link com.damnvulnerableapp.networking.messages.MessageParser} throws an exception,
     66 + * because the format of a message is unknown. Still the message might contain valuable
     67 + * information that should not be missed.
     68 + *
     69 + * Notice that these kind of methods allow for easy pen - testing of this app, which is good
     70 + * and bad at the same time. Therefore this callback should either be left empty when implementing
     71 + * a listener or be carefully analyzed, as it may obtain arbitrary inputs from potentially
     72 + * unknown sources.
     73 + *
     74 + * @param client Client that received an invalid message.
     75 + * @param message Invalid message received by the client.
     76 + * */
     77 + void onInvalidMessage(Client client, byte[] message);
     78 +}
     79 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/ConnectionInformation.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +/**
     4 + * Common superclass of all data classes wrapping connection information.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public abstract class ConnectionInformation {
     10 + 
     11 + private final int timeout;
     12 + 
     13 + public ConnectionInformation(int timeout) {
     14 + 
     15 + this.timeout = timeout;
     16 + }
     17 + 
     18 + public int getTimeout() {
     19 + return this.timeout;
     20 + }
     21 +}
     22 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/EndPoint.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +import com.damnvulnerableapp.common.configuration.Configurable;
     4 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     5 +import com.damnvulnerableapp.networking.exceptions.ConnectionException;
     6 +import com.damnvulnerableapp.networking.exceptions.TimeoutException;
     7 +import com.damnvulnerableapp.networking.messages.Message;
     8 + 
     9 +/**
     10 + * Communication endpoint, i.e. one peer of a peer - to - peer communication. Its methods form
     11 + * the basis of communication used in {@link Client} and {@link com.damnvulnerableapp.networking.protocol.Protocol}.
     12 + *
     13 + * @author Pascal Kühnemann
     14 + * @version 1.0
     15 + * @see Configurable
     16 + * */
     17 +public interface EndPoint {
     18 + 
     19 + /**
     20 + * Connects to specified location. This method has to ensure a stable connection after return.
     21 + * It does not perform any handshakes, it just sets up the connection to the server.
     22 + *
     23 + * @param info Information on the connection that e.g. describes where to connect to.
     24 + * @throws ConnectionException If an endpoint is already connected or some other connection
     25 + * related error occurs.
     26 + * @throws TimeoutException Connecting to a server may timeout, e.g. if the server is not
     27 + * available or does not answer.
     28 + * @throws CommunicationException If any communication - related error occurs.
     29 + * */
     30 + void connect(ConnectionInformation info) throws CommunicationException;
     31 + 
     32 + /**
     33 + * Disconnects this endpoint from an established connection. This method may be used to just
     34 + * close resource, for which closing implies shutting down the connection like in {@link java.net.Socket}.
     35 + * Notice that it should be possible to disconnect although there is no open connection.
     36 + *
     37 + * If an instance of a {@link Client} wants to disconnect, then it will internally call this
     38 + * method.
     39 + *
     40 + * @see Client
     41 + * */
     42 + void disconnect();
     43 + 
     44 + /**
     45 + * Determines whether this endpoint is connected or not. This method is useful for instances of
     46 + * {@link com.damnvulnerableapp.networking.protocol.Protocol} before sending or receiving
     47 + * messages.
     48 + *
     49 + * @return <code>true</code>, if endpoint is connected; <code>false</code> otherwise.
     50 + * @see com.damnvulnerableapp.networking.protocol.Protocol
     51 + * */
     52 + boolean isConnected();
     53 + 
     54 + /**
     55 + * Sends a raw byte array via a medium to the communication partner. It does not directly
     56 + * verify whether the connection is open or not, but rather will figure this out through
     57 + * exceptions.
     58 + *
     59 + * The procedure for sending a raw message can be as follows:
     60 + * 1. Try to send a 4 - byte integer indicating the <code>length</code> of the actual message to send.
     61 + * 2. Send the actual message.
     62 + *
     63 + * Notice that successfully sending a message does not imply that the communication partner
     64 + * received the message. If this endpoint was closed right after send, then the message might
     65 + * not be arriving at the receiver's end (Imagine a car(message) crossing a bridge(connection).
     66 + * destroying the bridge while the car is still on it might also destroy the car).
     67 + *
     68 + * This method is the basis for {@link com.damnvulnerableapp.networking.protocol.Protocol#send(EndPoint, Message)}.
     69 + *
     70 + * @param message Array of bytes, i.e. a raw message, to send to the communication partner.
     71 + * @throws ConnectionException If the connection is shut down during sending or has already
     72 + * been shut down before sending.
     73 + * @throws CommunicationException If any communication - related error occurs.
     74 + * */
     75 + void send(byte[] message) throws CommunicationException;
     76 + 
     77 + /**
     78 + * Receives a message from the communication partner. To do so, this method will block until
     79 + * a message is available or an exception occurs, i.e. timeout or connection error.
     80 + *
     81 + * The procedure for receiving a raw message can be as follows:
     82 + * 1. Try to read a 4 - byte integer indicating the <code>length</code> of the actual message to come.
     83 + * 2. If the <code>length</code> is greater than 0, then wait for <code>length</code> - many bytes
     84 + * and read them into a buffer that is returned by this method.
     85 + *
     86 + * @return Raw message.
     87 + * @throws ConnectionException If connection is shut down while or before waiting for a message.
     88 + * @throws TimeoutException As receiving a message blocks until there is a message, this may
     89 + * timeout. Therefore this is thrown on receive timeout.
     90 + * @throws CommunicationException If any communication - related error occurs.
     91 + * */
     92 + byte[] receive() throws CommunicationException;
     93 + 
     94 + /**
     95 + * Sets the timeout for blocking operations, e.g. for {@link EndPoint#receive()}. E.g. for
     96 + * {@link NetworkEndPoint}, this will call {@link java.net.Socket#setSoTimeout(int)}.
     97 + *
     98 + * @param timeout Timeout to use for blocking operations.
     99 + * */
     100 + void setTimeout(int timeout) throws CommunicationException;
     101 + 
     102 + /**
     103 + * Gets timeout used for blocking operations, e.g. for {@link EndPoint#receive()}.
     104 + *
     105 + * @return Timeout used for blocking operations.
     106 + * */
     107 + int getTimeout() throws CommunicationException;
     108 +}
     109 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/NetworkClient.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     4 +import com.damnvulnerableapp.networking.exceptions.ConnectionException;
     5 + 
     6 +/**
     7 + * Implementation of a client that is based on {@link java.net.Socket}.
     8 + *
     9 + * @author Pascal Kühnemann
     10 + * @version 1.0
     11 + * */
     12 +public class NetworkClient extends Client {
     13 + 
     14 + /**
     15 + * Construct socket - based client. This constructor is used by {@link NetworkFactory#createClient()}
     16 + * to create new instances of socket - based clients.
     17 + *
     18 + * Do NOT call this constructor directly. Rather call {@link NetworkFactory#createClient()}
     19 + * to obtain an instance of {@link NetworkClient}. Calling this constructor anyways may result
     20 + * in unstable connections, if any.
     21 + *
     22 + * @see Client#Client()
     23 + * */
     24 + public NetworkClient() {
     25 + super();
     26 + }
     27 + 
     28 + /**
     29 + * Construct socket - based client, where the internal implementation is already created. This
     30 + * will be useful only in conjunction with a {@link com.damnvulnerableapp.networking.communication.server.NetworkServer}.
     31 + *
     32 + * Do NOT call this constructor directly. Rather call {@link NetworkFactory#createClient()}
     33 + * to obtain an instance of {@link NetworkClient}. Calling this constructor anyways may result
     34 + * in unstable connections, if any.
     35 + *
     36 + * @param endpoint Socket - based implementation of communication.
     37 + * */
     38 + public NetworkClient(EndPoint endpoint) {
     39 + super(endpoint);
     40 + }
     41 + 
     42 + /**
     43 + * Creates a new instance of a socket - based implementation of communication. This will be
     44 + * called by {@link Client#connect(ConnectionInformation)}, if there is no connected endpoint
     45 + * yet.
     46 + *
     47 + * @return New instance of {@link NetworkEndPoint}.
     48 + * @throws ConnectionException If establishing a connection fails. This should only happen, if
     49 + * the {@link EndPoint} is used in conjunction with a
     50 + * {@link com.damnvulnerableapp.networking.communication.server.Server}.
     51 + * */
     52 + @Override
     53 + protected EndPoint createEndPoint() throws ConnectionException {
     54 + return new NetworkEndPoint();
     55 + }
     56 +}
     57 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/NetworkConnectionInformation.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +/**
     4 + * Wrapper for connection information that is used for connecting a {@link java.net.Socket} to
     5 + * a {@link java.net.ServerSocket}.
     6 + *
     7 + * @author Pascal Kühnemann
     8 + * @version 1.0
     9 + * */
     10 +public class NetworkConnectionInformation extends ConnectionInformation {
     11 + 
     12 + /**
     13 + * Host name to connect to. Most of the time, this is an IPv4 address.
     14 + * */
     15 + private final String host;
     16 + 
     17 + /**
     18 + * Port number to connect to. This implies usage of TCP.
     19 + * */
     20 + private final int port;
     21 + 
     22 + /**
     23 + * Constructs data object by assigning the connection destination.
     24 + *
     25 + * @param host Host to connect to. The user of this class has to ensure that the host name is
     26 + * valid.
     27 + * @param port Port to connect to. Only least - significant 2 bytes are considered.
     28 + * @param timeout Timeout used for connecting to server.
     29 + * */
     30 + public NetworkConnectionInformation(String host, int port, int timeout) {
     31 + super(timeout);
     32 + this.host = host;
     33 + this.port = (port & 0xffff);
     34 + }
     35 + 
     36 + /**
     37 + * Returns the host name, to which a socket is supposed to connect.
     38 + *
     39 + * @return Host name.
     40 + * */
     41 + public String getHost() {
     42 + return this.host;
     43 + }
     44 + 
     45 + /**
     46 + * Returns the port, to which a socket is supposed to connect. Only the least - significant
     47 + * 2 bytes are returned.
     48 + *
     49 + * @return Port.
     50 + * */
     51 + public int getPort() {
     52 + 
     53 + return this.port;
     54 + }
     55 +}
     56 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/client/NetworkEndPoint.java
     1 +package com.damnvulnerableapp.networking.communication.client;
     2 + 
     3 +import androidx.annotation.NonNull;
     4 + 
     5 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     6 +import com.damnvulnerableapp.networking.communication.server.Server;
     7 +import com.damnvulnerableapp.networking.exceptions.ConnectionException;
     8 +import com.damnvulnerableapp.networking.exceptions.InternalSocketException;
     9 +import com.damnvulnerableapp.networking.exceptions.TimeoutException;
     10 + 
     11 +import java.io.DataInputStream;
     12 +import java.io.DataOutputStream;
     13 +import java.io.IOException;
     14 +import java.net.InetSocketAddress;
     15 +import java.net.ServerSocket;
     16 +import java.net.Socket;
     17 +import java.net.SocketAddress;
     18 +import java.net.SocketException;
     19 +import java.net.SocketTimeoutException;
     20 + 
     21 +/**
     22 + * Socket - based communication endpoint.
     23 + *
     24 + * This is a concrete implementation of {@link EndPoint} and therefore represents one peer in
     25 + * peer - to - peer communication. Internally, {@link Socket} is used in conjunction with
     26 + * {@link DataInputStream} and {@link DataOutputStream}.
     27 + *
     28 + * @author Pascal Kühnemann
     29 + * @version 1.0
     30 + * */
     31 +public class NetworkEndPoint implements EndPoint { //extends EndPoint {
     32 + 
     33 + /**
     34 + * Socket that is used to connect and communication with a server, i.e. {@link java.net.ServerSocket}.
     35 + * */
     36 + private final Socket socket;
     37 + 
     38 + /**
     39 + * Input portion of I/O. It will be used to receive messages from a {@link com.damnvulnerableapp.networking.communication.server.Server}.
     40 + * */
     41 + private DataInputStream input;
     42 + 
     43 + /**
     44 + * Output portion of I/O. It will be used to send messages to a {@link com.damnvulnerableapp.networking.communication.server.Server}.
     45 + * */
     46 + private DataOutputStream output;
     47 + 
     48 + /**
     49 + * Construct new endpoint based on a completely new socket. This will be used in {@link NetworkFactory#createClient()}
     50 + * as its internal socket is NOT coming from a {@link Server#accept()}.
     51 + *
     52 + * @throws ConnectionException If, given a socket returned by {@link ServerSocket#accept()},
     53 + * constructing I/O streams fails. Should not affect this constructor.
     54 + */
     55 + public NetworkEndPoint() throws ConnectionException {
     56 + this(new Socket());
     57 + }
     58 + 
     59 + /**
     60 + * Construct new endpoint based on a socket returned by {@link ServerSocket#accept()}. If the
     61 + * socket is already connected, then I/O streams will be created as well.
     62 + *
     63 + * @param socket Socket to attach to this endpoint. This must not be null.
     64 + * @throws ConnectionException If, given a socket returned by {@link ServerSocket#accept()},
     65 + * constructing I/O streams fails.
     66 + * */
     67 + public NetworkEndPoint(Socket socket) throws ConnectionException {
     68 + 
     69 + this.socket = socket;
     70 + if (this.socket.isConnected())
     71 + this.initStreams();
     72 + }
     73 + 
     74 + /**
     75 + * Connects this endpoint to specified server. To that end, {@link Socket#connect(SocketAddress, int)}
     76 + * is used with a timeout specified in currently selected configuration. This method will also
     77 + * set a timeout for receiving messages via {@link Socket#setSoTimeout(int)}.
     78 + *
     79 + * It is not allowed to connect a socket - based endpoint multiple times. Just like with
     80 + * {@link Socket}s, a new endpoint has to be created.
     81 + *
     82 + * It is possible to connect to a {@link ServerSocket} successfully, before {@link ServerSocket#accept()}
     83 + * is called. {@link ServerSocket#accept()} just constructs a socket based on a recent
     84 + * connection located on the backlog.
     85 + *
     86 + * @param info Information on where to connect to.
     87 + * @throws ConnectionException If an endpoint is already connected or some other connection
     88 + * related error occurs.
     89 + * @throws TimeoutException Connecting to a server may timeout, e.g. if the server is not
     90 + * available or does not answer.
     91 + * */
     92 + @Override
     93 + public void connect(ConnectionInformation info) throws ConnectionException, TimeoutException {
     94 + 
     95 + NetworkConnectionInformation target = (NetworkConnectionInformation) info;
     96 + 
     97 + // If socket is already connected, we cannot just reconnect as this requires a new socket
     98 + // to be created.
     99 + if (this.socket.isConnected())
     100 + throw new ConnectionException("Socket is already connected.");
     101 + 
     102 + // Connect to target host
     103 + try {
     104 + this.socket.connect(
     105 + new InetSocketAddress(
     106 + target.getHost(),
     107 + target.getPort()
     108 + ),
     109 + info.getTimeout()
     110 + );
     111 + } catch (SocketTimeoutException e) {
     112 + throw new TimeoutException("Timed out while trying to connect to " +
     113 + target.getHost() + ":" + target.getPort() + ".", e);
     114 + } catch (IOException e) {
     115 + throw new ConnectionException("Connecting to " + target.getHost() + ":"
     116 + + target.getPort() + " failed.", e);
     117 + }
     118 + 
     119 + this.initStreams();
     120 + }
     121 + 
     122 + /**
     123 + * Disconnects this endpoint from an established connection by closing this {@link Socket}
     124 + * and corresponding I/O streams. Any exceptions that may occur during closing are ignored, as
     125 + * they do not yield useful information in this case.
     126 + */
     127 + @Override
     128 + public void disconnect() {
     129 + 
     130 + // Shutdown I/O and socket. Why split this step: If output.close threw an exception
     131 + // then we would not close the socket -> separate tries
     132 + try {
     133 + if (this.output != null)
     134 + this.output.close();
     135 + } catch (IOException ignored) {}
     136 + 
     137 + try {
     138 + if (this.input != null)
     139 + this.input.close();
     140 + } catch (IOException ignored) {}
     141 + 
     142 + try {
     143 + if (this.socket != null)
     144 + this.socket.close();
     145 + } catch (IOException ignored) {}
     146 + }
     147 + 
     148 + /**
     149 + * Returns whether this endpoint is connected to a {@link Server}.
     150 + *
     151 + * This method is based on {@link Socket#isConnected()}, which will be set to <code>true</code>,
     152 + * if this {@link Socket} connected successfully. Unfortunately, {@link Socket#isConnected()}
     153 + * will never be <code>false</code> again. Therefore, for a socket to be connected, it has to
     154 + * be in connected state and not closed.
     155 + *
     156 + * @return <code>true</code>, if socket is not closed and connected; <code>false</code> otherwise.
     157 + * */
     158 + @Override
     159 + public boolean isConnected() {
     160 + return (!this.socket.isClosed() && this.socket.isConnected());
     161 + }
     162 + 
     163 + /**
     164 + * Sends a raw message via a {@link DataOutputStream}. After the message has been sent according
     165 + * to the format described in {@link EndPoint#send(byte[])}, this {@link DataOutputStream} will
     166 + * be flushed to ensure that the message is sent.
     167 + *
     168 + * If a message was dropped, this might make this endpoint stumble, as it does not take any
     169 + * measures to recover from missing messages.
     170 + *
     171 + * @param message Array of bytes to send to other peer. If it is <code>null</code>, then it will
     172 + * be set to an empty byte array.
     173 + * @throws ConnectionException If the connection is shut down during sending or has already
     174 + * been shut down before sending.
     175 + * @see EndPoint#send(byte[])
     176 + * */
     177 + @Override
     178 + public void send(byte[] message) throws ConnectionException {
     179 + 
     180 + try {
     181 + 
     182 + // If message is null, set it to empty
     183 + if (message == null)
     184 + message = new byte[0];
     185 + 
     186 + // First send length of message to send
     187 + this.output.writeInt(message.length);
     188 + 
     189 + // Write whole message
     190 + this.output.write(message, 0, message.length);
     191 + 
     192 + // Enforce write
     193 + this.output.flush();
     194 + 
     195 + } catch (IOException e) {
     196 + // We do not care about SocketTimeoutException, because, according to docs, this can
     197 + // only occur "... on a socket read or accept".
     198 + throw new ConnectionException("Connection closed. Failed to send message.", e);
     199 + }
     200 + }
     201 + 
     202 + /**
     203 + * Receives a raw message using a {@link DataInputStream} in a blocking fashion. If the message
     204 + * received is of length 0, then an empty byte array is returned.
     205 + *
     206 + * @return Raw message.
     207 + * @throws ConnectionException If connection is shut down while or before waiting for a message.
     208 + * @throws TimeoutException As receiving a message blocks until there is a message, this may
     209 + * timeout. Therefore this is thrown on receive timeout.
     210 + * @see EndPoint#receive()
     211 + * */
     212 + @Override
     213 + @NonNull
     214 + public byte[] receive() throws TimeoutException, ConnectionException {
     215 + 
     216 + byte[] message;
     217 + 
     218 + try {
     219 + 
     220 + // Read message length
     221 + int length = this.input.readInt();
     222 + if (length > 0) {
     223 + message = new byte[length];
     224 + 
     225 + // Read whole message based on length.
     226 + this.input.readFully(message, 0, length);
     227 + } else {
     228 + message = new byte[0];
     229 + }
     230 + 
     231 + return message;
     232 + } catch (SocketTimeoutException e) {
     233 + throw new TimeoutException("Timed out while trying to receive a message.", e);
     234 + } catch (IOException e) {
     235 + throw new ConnectionException("Connection closed. Failed to receive message.", e);
     236 + }
     237 + }
     238 + 
     239 + @Override
     240 + public void setTimeout(int timeout) throws InternalSocketException {
     241 + try {
     242 + this.socket.setSoTimeout(timeout);
     243 + } catch (SocketException e) {
     244 + throw new InternalSocketException("Trying to set timeout on server socket failed.", e);
     245 + }
     246 + }
     247 + 
     248 + @Override
     249 + public int getTimeout() throws InternalSocketException {
     250 + try {
     251 + return this.socket.getSoTimeout();
     252 + } catch (SocketException e) {
     253 + throw new InternalSocketException("Trying to set timeout on server socket failed.", e);
     254 + }
     255 + }
     256 + 
     257 + /**
     258 + * Initializes input and output streams of this socket. Any error is vital, thus deadly
     259 + * exceptions are thrown on error.
     260 + *
     261 + * @throws ConnectionException If creating an I/O stream fails. This can be due to a closed
     262 + * connection, not connected socket, a closed stream etc.
     263 + * @see DataInputStream
     264 + * @see DataOutputStream
     265 + * */
     266 + private void initStreams() throws ConnectionException {
     267 + // Create input stream
     268 + try {
     269 + this.input = new DataInputStream(this.socket.getInputStream());
     270 + } catch (IOException e) {
     271 + throw new ConnectionException("Failed to create input stream. Either"
     272 + + " stream creation failed, the connection was closed, the socket was"
     273 + + " not connected or the respective stream was closed.", e);
     274 + }
     275 + 
     276 + // Create output stream
     277 + try {
     278 + this.output = new DataOutputStream(this.socket.getOutputStream());
     279 + } catch (IOException e) {
     280 + try {
     281 + // Try to be nice and clean up opened input stream to avoid resource leaks.
     282 + this.input.close();
     283 + } catch (IOException ignored) {}
     284 + throw new ConnectionException("Failed to create output stream. Either"
     285 + + " stream creation failed, the connection was closed, the socket was"
     286 + + " not connected or the respective stream was closed.", e);
     287 + }
     288 + }
     289 +}
     290 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/server/BindInformation.java
     1 +package com.damnvulnerableapp.networking.communication.server;
     2 + 
     3 +/**
     4 + * Superclass for data classes that contain information on how and where to bind a server.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public abstract class BindInformation {
     10 +}
     11 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/server/NetworkBindInformation.java
     1 +package com.damnvulnerableapp.networking.communication.server;
     2 + 
     3 +/**
     4 + * Information on where to bind a {@link java.net.ServerSocket}.
     5 + *
     6 + * @author Pascal Kühnemann
     7 + * @version 1.0
     8 + * */
     9 +public class NetworkBindInformation extends BindInformation {
     10 + 
     11 + /**
     12 + * Host name, on which to bind the {@link java.net.ServerSocket}.
     13 + * */
     14 + private final String host;
     15 + 
     16 + /**
     17 + * Port of the {@link java.net.ServerSocket}.
     18 + * */
     19 + private final int port;
     20 + 
     21 + /**
     22 + * Assigns binding information.
     23 + *
     24 + * @param host Host name, on which to bind.
     25 + * @param port Port, on which to bind. Only the least - significant 2 bytes are considered.
     26 + * */
     27 + public NetworkBindInformation(String host, int port) {
     28 + this.host = host;
     29 + this.port = (port & 0xffff);
     30 + }
     31 + 
     32 + /**
     33 + * Returns the host name, on which a {@link java.net.ServerSocket} should bind.
     34 + *
     35 + * @return Host name.
     36 + * */
     37 + public String getHost() {
     38 + return this.host;
     39 + }
     40 + 
     41 + /**
     42 + * Returns the port, on which a {@link java.net.ServerSocket} should bind. Only the least -
     43 + * significant 2 bytes are considered.
     44 + *
     45 + * @return Port.
     46 + * */
     47 + public int getPort() {
     48 + return this.port;
     49 + }
     50 +}
     51 + 
  • ■ ■ ■ ■ ■ ■
    damnvulnerableapp/app/src/main/java/com/damnvulnerableapp/networking/communication/server/NetworkServer.java
     1 +package com.damnvulnerableapp.networking.communication.server;
     2 + 
     3 +import androidx.annotation.NonNull;
     4 + 
     5 +import com.damnvulnerableapp.common.configuration.ClientConfiguration;
     6 +import com.damnvulnerableapp.common.configuration.Configuration;
     7 +import com.damnvulnerableapp.common.configuration.ServerConfiguration;
     8 +import com.damnvulnerableapp.networking.communication.NetworkFactory;
     9 +import com.damnvulnerableapp.networking.communication.client.ClientType;
     10 +import com.damnvulnerableapp.networking.communication.client.CommunicationListener;
     11 +import com.damnvulnerableapp.networking.communication.client.Client;
     12 +import com.damnvulnerableapp.networking.communication.client.ConnectionInformation;
     13 +import com.damnvulnerableapp.networking.communication.client.EndPoint;
     14 +import com.damnvulnerableapp.networking.communication.client.NetworkClient;
     15 +import com.damnvulnerableapp.networking.communication.client.NetworkEndPoint;
     16 +import com.damnvulnerableapp.networking.exceptions.AcceptException;
     17 +import com.damnvulnerableapp.networking.exceptions.BindException;
     18 +import com.damnvulnerableapp.networking.exceptions.CommunicationException;
     19 +import com.damnvulnerableapp.networking.exceptions.ConnectionException;
     20 +import com.damnvulnerableapp.networking.exceptions.CreationException;
     21 +import com.damnvulnerableapp.networking.exceptions.InternalSocketException;
     22 +import com.damnvulnerableapp.networking.exceptions.InvalidClientException;
     23 +import com.damnvulnerableapp.networking.exceptions.MessageParserException;
     24 +import com.damnvulnerableapp.networking.exceptions.TimeoutException;
     25 +import com.damnvulnerableapp.networking.messages.Message;
     26 + 
     27 +import java.io.IOException;
     28 +import java.net.InetSocketAddress;
     29 +import java.net.ServerSocket;
     30 +import java.net.Socket;
     31 +import java.net.SocketAddress;
     32 +import java.net.SocketException;
     33 +import java.net.SocketTimeoutException;
     34 +import java.util.LinkedList;
     35 +import java.util.List;
     36 + 
     37 +/**
     38 + * Socket - based implementation of {@link Server} that uses a {@link ServerSocket} to handle
     39 + * incoming connections. Accepted client are then wrapped by {@link NetworkClient}, which is
     40 + * compatible with this server.
     41 + *
     42 + * {@link NetworkClient} and {@link NetworkServer} should use the same protocol.
     43 + *
     44 + * This class should NEVER be instantiated by calling its constructor. Rather call
     45 + * {@link NetworkFactory#createServer()} to obtain an instance of this class.
     46 + *
     47 + * @author Pascal Kühnemann
     48 + * @version 1.0
     49 + * */
     50 +public class NetworkServer extends Server {
     51 + 
     52 + /**
     53 + * Socket used to handle incoming client connections.
     54 + * */
     55 + private ServerSocket socket;
     56 + 
     57 + /**
     58 + * List of message - loop threads that repeatedly call {@link Client#receive()}, which results
     59 + * in a request - response model, because a server - side client can only respond to a request
     60 + * through {@link CommunicationListener#onReceive(Client, Message)}.
     61 + * */
     62 + private final List<Thread> messageThreads;
     63 + 
     64 + /**
     65 + * Construct server.
     66 + * */
     67 + public NetworkServer() {
     68 + this.messageThreads = new LinkedList<>();
     69 + }
     70 + 
     71 + /**
     72 + * Binds socket - based server to specified host and port. It also specifies the receive - timeout
     73 + * according to the configuration of this server.
     74 + *
     75 + * @param info Instance of {@link NetworkBindInformation} that contains information on where
     76 + * to bind the server socket to.
     77 + * @throws BindException If calling {@link ServerSocket#bind(SocketAddress)} fails.
     78 + * @throws CreationException If creating the server socket fails.
     79 + * @throws InternalSocketException If setting a timeout triggers an internal protocol error (TCP).
     80 + * @see NetworkBindInformation
     81 + * @see ServerSocket#bind(SocketAddress)
     82 + * */
     83 + @Override
     84 + public void bind(BindInformation info) throws BindException, CreationException, InternalSocketException {
     85 + 
     86 + // If socket is bound already, i.e. it has been used before, then interpret this
     87 + // call as re-bind.
     88 + if (this.isBound()) {
     89 + this.close();
     90 + this.socket = null;
     91 + }
     92 + 
     93 + NetworkBindInformation target = (NetworkBindInformation) info;
     94 + try {
     95 + 
     96 + // If the server socket has not been created yet
     97 + if (this.socket == null)
     98 + this.socket = new ServerSocket();
     99 + 
     100 + } catch (IOException e) {
     101 + throw new CreationException("Failed to create server socket.", e);
     102 + }
     103 + 
     104 + try {
     105 + // Bind socket
     106 + this.socket.bind(new InetSocketAddress(target.getHost(), target.getPort()));
     107 + } catch (IOException e) {
     108 + throw new BindException("Failed to bind server socket to " + target.getHost() + ":" + target.getPort(), e);
     109 + }
     110 + 
     111 + try {
     112 + // Try to set timeout
     113 + this.socket.setSoTimeout(((ServerConfiguration)this.getConfiguration()).getTimeout());
     114 + } catch (SocketException e) {
     115 + // This is specific to the underlying protocol, i.e. in this case: TCP
     116 + // This might also be quite rare
     117 + throw new InternalSocketException("Trying to set timeout on server socket failed.", e);
     118 + }
     119 + }
     120 + 
     121 + /**
     122 + * Checks whether the server is bound, i.e. whether there has been a successful call to
     123 + * {@link Server#bind(BindInformation)}. Notice that if this {@link ServerSocket} is bound, then
     124 + * it cannot be closed. On the other hand, if this {@link ServerSocket} is closed, it cannot be
     125 + * bound. Combining this equivalence with {@link ServerSocket#isBound()} is assumed to yield a
     126 + * very good approximation to the actual state of the server socket.
     127 + *
     128 + * @return <code>true</code>, if this server is bound; <code>false</code> otherwise.
     129 + * @see Server#bind(BindInformation)
     130 + * */
     131 + @Override
     132 + public boolean isBound() {
     133 + if (this.socket != null)
     134 + return (this.socket.isBound() && !this.socket.isClosed());
     135 + return false;
     136 + }
     137 + 
     138 + /**
     139 + * Accepts an incoming client connection. If there is no incoming connection when calling this
     140 + * method, it will block until there is a connection or a specified timeout is hit.
     141 + *
     142 + * Notice that {@link ServerSocket#accept()} returns {@link Socket}, which is fed into
     143 + * {@link NetworkEndPoint#NetworkEndPoint(Socket)}. This will be wired up with this server's
     144 + * protocol and configurations to construct a {@link Client}.
     145 + *
     146 + * This method should be called in {@link Server#acceptLoopIteration()} to ensure that client
     147 + * connections are accepted repeatedly.
     148 + *
     149 + * In this implementation, each server - side client will be assigned a message - loop that
     150 + * repeatedly calls {@link Client#receive()}. This results in a request - response model, because
     151 + * each server - side client can only react to messages via {@link CommunicationListener#onReceive(Client, Message)}.
     152 + *
     153 + * @return Accepted client.
     154 + * @throws AcceptException If accepting a client via {@link ServerSocket#accept()} fails.
     155 + * @throws ConnectionException If, among others, creating an endpoint fails. Is also triggered
     156 + * when trying to connect the client with a handshake or receiving
     157 + * messages in the message - loop.
     158 + * @throws InvalidClientException If client - side client posted invalid functionality.
     159 + * @throws MessageParserException If parsing a message fails. Can be caused by {@link Client#connect(ConnectionInformation)}
     160 + * due to {@link com.damnvulnerableapp.networking.protocol.Protocol#handshake(EndPoint, ClientType)}
     161 + * or in the message - loop by {@link Client#receive()}.
     162 + * @throws TimeoutException If accepting clients via {@link ServerSocket#accept()} times out.
     163 + * @see ClientType
     164 + * @see Client
     165 + * @see CommunicationListener
     166 + * */
     167 + @Override
     168 + public Client accept() throws CommunicationException {
     169 + 
     170 + if (!this.isBound())
     171 + throw new AcceptException("Server needs to be bound in order to accept clients.");
     172 + 
     173 + Client client;
     174 + 
     175 + // Accept incoming connection.
     176 + Socket clientSocket;
     177 + try {
     178 + clientSocket = this.socket.accept();
     179 + } catch (SocketTimeoutException e) {
     180 + throw new TimeoutException("Server socket timed out while waiting for connection(s).", e);
     181 + } catch (IOException e) {
     182 + throw new AcceptException("Server socket failed accepting connection(s).", e);
     183 + }
     184 + 
     185 + // Construct endpoint
     186 + EndPoint ep;
     187 + try {
     188 + ep = new NetworkEndPoint(clientSocket);
     189 + } catch (ConnectionException e) {
     190 + 
     191 + // Deadly error(s)! We need to clean up socket returned by ServerSocket#accept
     192 + try {
     193 + clientSocket.close();
     194 + } catch (IOException ignored) {}
     195 + throw e;
     196 + }
     197 + 
     198 + try {
     199 + // Define server - side of connection
     200 + client = new NetworkClient(ep);
     201 + client.setProtocol(this.getProtocol());
     202 + client.setType(ClientType.MANAGER);
     203 + client.setConfiguration(this.getClientConfiguration());
     204 + 
     205 + // Take all listeners registered with the server and assign them to the server-side
     206 + // part of the connection with the client.
     207 + for (CommunicationListener listener : this.getListeners())
     208 + client.addCommunicationListener(listener);
     209 + 
     210 + // Start client, i.e. perform handshake and run message loop.
     211 + client.connect(null);
     212 + 
     213 + // Run client in message loop, i.e. request - response loop
     214 + final Thread t = new Thread(() -> {
     215 + boolean run = true;
     216 + 
     217 + while (run) {
     218 + 
     219 + try {
     220 + client.receive();
     221 + } catch (MessageParserException ignored) {
     222 + 
     223 + } catch (CommunicationException e) {
     224 + run = false;
     225 + }
     226 + }
     227 + 
     228 + client.disconnect();
     229 + getClients().remove(client);
     230 + });
     231 + this.messageThreads.add(t);
     232 + t.start();
     233 + 
     234 + } catch (CommunicationException e) {
     235 + 
     236 + // Deadly error(s)! We need to clean up socket returned by ServerSocket#accept and
     237 + // also shutdown endpoint -> EndPoint#disconnect handles both!
     238 + ep.disconnect();
     239 + throw e;
     240 + }
     241 + 
     242 + return client;
     243 + }
     244 + 
     245 + /**
     246 + * Closes this server by interrupting all message - loop threads, disconnecting server - side
     247 + * clients and closing this server socket.
     248 + *
     249 + * @see ServerSocket#close()
     250 + * */
     251 + @Override
     252 + public void close() {
     253 + 
     254 + for (Thread thread : this.messageThreads)
     255 + thread.interrupt();
     256 + 
     257 + for (Client client : this.getClients()) {
     258 + if (client != null)
     259 + client.disconnect();
     260 + this.getClients().remove(client);
     261 + }
     262 + 
     263 + try {
     264 + if (this.socket != null)
     265 + this.socket.close();
     266 + } catch (IOException ignored) {}
     267 + }
     268 + 
     269 + /**
     270 + * Sets the server configuration to use. This, among other things, allows tuning timeouts and
     271 + * the number of clients. Changing the configuration after starting the server with {@link Server#start()}
     272 + * or {@link Server#startAsync()} may cause crashes!
     273 + *
     274 + * @param configuration New configuration to use for this server.
     275 + * @see ServerConfiguration
     276 + * */
     277 + @Override
     278 + public void setConfiguration(Configuration configuration) {
     279 + // Check if configuration is a subclass of EndPointConfiguration
     280 + if (ServerConfiguration.class.isAssignableFrom(configuration.getClass())) {
     281 + this.configuration = (ServerConfiguration) configuration;
     282 + try {
     283 + this.socket.setSoTimeout(this.configuration.getTimeout());
     284 + } catch (SocketException ignored) {}
     285 + }
     286 + }
     287 + 
     288 + /**
     289 + * Returns this server's configuration, which contains information that is vital to handling
     290 + * client connections.
     291 + *
     292 + * @return Server configuration.
     293 + * @see ServerConfiguration
     294 + * */
     295 + @Override
     296 + @NonNull
     297 + public Configuration getConfiguration() {
     298 + return this.configuration;
     299 + }
     300 + 
     301 + /**
     302 + * Sets the default server - side client configuration. It will be assigned to any client that
     303 + * is constructed from an incoming client connection.
     304 + *
     305 + * CAUTION: It also affects server - side clients that are currently up and running.
     306 + *
     307 + * @param configuration New client configuration to use for any future server - side client.
     308 + * @see ClientConfiguration
     309 + * */
     310 + public void setClientConfiguration(ClientConfiguration configuration) {
     311 + this.clientConfiguration = configuration;
     312 + 
     313 + for (final Client client : this.getClients()) {
     314 + if (client != null)
     315 + client.setConfiguration(configuration);
     316 + }
     317 + }
     318 + 
     319 + /**
     320 + * Returns this server's default client configuration.
     321 + *
     322 + * @return Server - side client configuration.
     323 + * @see ClientConfiguration
     324 + * */
     325 + public ClientConfiguration getClientConfiguration() {
     326 + return this.clientConfiguration;
     327 + }
     328 + 
     329 + /**
     330 + * Performs one iteration of the accept - loop by just calling {@link NetworkServer#accept()}.
     331 + *
     332 + * It adds the newly accepted client to a list of clients that connected successfully.
     333 + *
     334 + * @throws AcceptException If accepting a client via {@link ServerSocket#accept()} fails.
     335 + * @throws ConnectionException If, among others, creating an endpoint fails. Is also triggered
     336 + * when trying to connect the client with a handshake or receiving
     337 + * messages in the message - loop.
     338 + * @throws InvalidClientException If client - side client posted invalid functionality.
     339 + * @throws MessageParserException If parsing a message fails. Can be caused by {@link Client#connect(ConnectionInformation)}
     340 + * due to {@link com.damnvulnerableapp.networking.protocol.Protocol#handshake(EndPoint, ClientType)}
     341 + * or in the message - loop by {@link Client#receive()}.
     342 + * @throws TimeoutException If accepting clients via {@link ServerSocket#accept()} times out.
     343 + * @see NetworkServer#accept()
     344 + * */
     345 + @Override
     346 + protected void acceptLoopIteration() throws CommunicationException {
     347 + 
     348 + // If the client is up and running, add it to the list
     349 + final Client client = this.accept();
     350 + if (client == null)
     351 + throw new AcceptException("Failed to accept client (client was null).");
     352 + this.getClients().add(client);
     353 + }
     354 +}
     355 + 
Please wait...
Page is in error, reload to recover