ProfileManager.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. package de.tudarmstadt.informatik.hostage.persistence;
  2. import org.json.JSONArray;
  3. import org.json.JSONException;
  4. import org.json.JSONObject;
  5. import android.content.Context;
  6. import android.content.SharedPreferences;
  7. import java.io.FileInputStream;
  8. import java.io.FileNotFoundException;
  9. import java.io.FileOutputStream;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.io.StreamCorruptedException;
  13. import java.util.ArrayList;
  14. import java.util.Arrays;
  15. import java.util.Collection;
  16. import java.util.HashMap;
  17. import java.util.HashSet;
  18. import java.util.LinkedList;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Random;
  22. import java.util.Set;
  23. import de.tudarmstadt.informatik.hostage.Hostage;
  24. import de.tudarmstadt.informatik.hostage.Listener;
  25. import de.tudarmstadt.informatik.hostage.R;
  26. import de.tudarmstadt.informatik.hostage.model.Profile;
  27. import de.tudarmstadt.informatik.hostage.ui2.activity.MainActivity;
  28. import de.tudarmstadt.informatik.hostage.ui2.adapter.ProfileManagerListAdapter;
  29. /**
  30. * The profile manager is responsible for persisting and deleting profiles
  31. *
  32. * @author Alexander Brakowski
  33. * @created 10.02.14 20:24
  34. */
  35. public class ProfileManager {
  36. /**
  37. * The singleton instance holder
  38. */
  39. private static ProfileManager INSTANCE = null;
  40. /**
  41. * An list adapter, which the profile manager informs about data changes
  42. */
  43. private ProfileManagerListAdapter mProfileListAdapter = null;
  44. /**
  45. * Holds a reference to the currently active profile
  46. */
  47. private Profile mCurrentActivatedProfile = null;
  48. /**
  49. * Holds a reference to the random profile
  50. */
  51. private Profile mRandomProfile = null;
  52. /**
  53. * The profiles are being serialized and persisted into this file
  54. */
  55. private static final String PERSIST_FILENAME = "hostage_profiles.json";
  56. /**
  57. * Hold the current profile id, it will be incremented each time a new profile is added.
  58. * The new profile will get the newly incremented value as an id.
  59. */
  60. public int mIncrementValue = 1;
  61. /**
  62. * Holds all the available profiles. The key in the map is the ID of the profile.
  63. */
  64. public HashMap<Integer, Profile> mProfiles;
  65. private SharedPreferences mSharedPreferences;
  66. private SharedPreferences.Editor mSharedEditor;
  67. /**
  68. * Since the profile manager should only have one instance in the whole app, we are using the singleton pattern.
  69. * This method creates a new instance of the profile manager, if no instance already exists, and returns it.
  70. *
  71. * @return the singleton instance
  72. */
  73. public static ProfileManager getInstance(){
  74. if(INSTANCE == null){
  75. INSTANCE = new ProfileManager();
  76. }
  77. if(INSTANCE.getNumberOfProfiles() == 0){
  78. INSTANCE.loadData();
  79. }
  80. return INSTANCE;
  81. }
  82. /**
  83. * A private constructor, that can/should only be called by getInstance, since we want to enforce the usage of the singleton.
  84. */
  85. private ProfileManager(){
  86. mProfiles = new HashMap<Integer, Profile>();
  87. String sharedPreferencePath = MainActivity.getContext().getString(R.string.shared_preference_path);
  88. mSharedPreferences = MainActivity.getContext().getSharedPreferences(sharedPreferencePath, Hostage.MODE_PRIVATE);
  89. mSharedEditor = mSharedPreferences.edit();
  90. }
  91. /**
  92. * Reads all data from an inputstream and appends it to a string
  93. *
  94. * @param is The input stream to read the data from
  95. * @return the whole data from the input stream as an string
  96. */
  97. public String readAll( final InputStream is ) {
  98. if( null == is ) {
  99. return "";
  100. }
  101. StringBuilder sb = new StringBuilder();
  102. int rc;
  103. try {
  104. while( (rc = is.read()) >= 0 ){
  105. sb.append( (char) rc );
  106. }
  107. } catch (IOException e) {
  108. e.printStackTrace();
  109. }
  110. return sb.toString();
  111. }
  112. /**
  113. * Reads all the data of the the profiles, that were persisted and unserializes them.
  114. *
  115. * The profiles were serialized into JSON and persisted into the android private file.
  116. * See {@see ProfileManager#persistData}.
  117. */
  118. public void loadData(){
  119. try {
  120. FileInputStream fin = MainActivity.getContext().openFileInput(PERSIST_FILENAME);
  121. JSONArray arr = new JSONArray(readAll(fin));
  122. fin.close();
  123. for(int i=0; i<arr.length(); i++){
  124. JSONObject obj = arr.getJSONObject(i);
  125. Profile p = new Profile();
  126. p.fromJSON(obj);
  127. mProfiles.put(p.mId, p);
  128. if(p.mId > mIncrementValue){
  129. mIncrementValue = p.mId;
  130. }
  131. if(p.mActivated){
  132. activateProfile(p, false);
  133. }
  134. if(p.mIsRandom){
  135. this.mRandomProfile = p;
  136. }
  137. }
  138. } catch (FileNotFoundException e) {
  139. e.printStackTrace();
  140. } catch (StreamCorruptedException e) {
  141. e.printStackTrace();
  142. } catch (IOException e) {
  143. e.printStackTrace();
  144. } catch (JSONException e) {
  145. e.printStackTrace();
  146. } finally {
  147. if(mProfiles.size() == 0){
  148. this.fillWithDefaultData();
  149. }
  150. if(this.mRandomProfile != null){
  151. randomizeProtocols(mRandomProfile);
  152. }
  153. }
  154. }
  155. /**
  156. * All the profiles that are hold by the profile manager are being serialized into JSON and then written into an private android file.
  157. */
  158. public void persistData(){
  159. try {
  160. FileOutputStream fout = MainActivity.getContext().openFileOutput(PERSIST_FILENAME, Context.MODE_PRIVATE);
  161. JSONArray arr = new JSONArray();
  162. for(Profile p: mProfiles.values()){
  163. arr.put(p.toJSON());
  164. }
  165. fout.write(arr.toString().getBytes());
  166. fout.close();
  167. } catch (FileNotFoundException e) {
  168. e.printStackTrace();
  169. } catch (IOException e) {
  170. e.printStackTrace();
  171. }
  172. }
  173. /**
  174. * Retrieves all the profiles as an List
  175. * @return a list that holds all the profiles
  176. */
  177. public List<Profile> getProfilesList(){
  178. return new ArrayList<Profile>(getProfilesCollection());
  179. }
  180. /**
  181. * Retrieves all the profiles as an collection
  182. * @return a collection of all the profiles
  183. */
  184. public Collection<Profile> getProfilesCollection(){
  185. if(mProfiles.size() == 0 || mProfiles == null){
  186. this.loadData();
  187. }
  188. return mProfiles.values();
  189. }
  190. /**
  191. * Retrieves an map of all the profiles. The key in the map is the ID of an profile.
  192. * @return a map of profiles
  193. */
  194. public Map<Integer, Profile> getMapProfiles(){
  195. return mProfiles;
  196. }
  197. /**
  198. * Activates and deactivates protocols randomly in the given profile
  199. *
  200. * @param profile the profile to randomize the protocols for
  201. */
  202. public void randomizeProtocols(Profile profile){
  203. LinkedList<String> protocols = new LinkedList<String>(Arrays.asList(MainActivity.getContext().getResources().getStringArray(R.array.protocols)));
  204. protocols.remove("GHOST");
  205. profile.mActiveProtocols.clear();
  206. Random rand = new Random();
  207. int numberOfProtocolsToActivate = rand.nextInt(protocols.size()) + 1;
  208. while(numberOfProtocolsToActivate-- > 0){
  209. int randomIndex = rand.nextInt(protocols.size());
  210. String protocol = protocols.get(randomIndex);
  211. profile.mActiveProtocols.put(protocol, true);
  212. protocols.remove(protocol);
  213. }
  214. persistData();
  215. }
  216. /**
  217. * Adds or updates a given profile.
  218. *
  219. * @param profile the profile to persist
  220. * @return the id the profile was assigned to
  221. */
  222. public int persistProfile(Profile profile){
  223. if(profile.mId == -1){
  224. profile.mId = ++mIncrementValue;
  225. }
  226. mProfiles.put(profile.mId, profile);
  227. this.persistData();
  228. if(this.mProfileListAdapter != null){
  229. this.mProfileListAdapter.notifyDataSetChanged();
  230. }
  231. return profile.mId;
  232. }
  233. /**
  234. * Retrieves the profile with the given id
  235. *
  236. * @param id the id of the profile
  237. * @return the profile
  238. */
  239. public Profile getProfile(int id){
  240. if(mProfiles.size() == 0){
  241. loadData();
  242. }
  243. if(this.mProfiles.containsKey(id)){
  244. return this.mProfiles.get(id);
  245. }
  246. return null;
  247. }
  248. /**
  249. * Adds a profile
  250. *
  251. * @param profile the profile to add
  252. */
  253. public void addProfile(Profile profile){
  254. this.addProfile(profile, true);
  255. }
  256. /**
  257. * Adds a given profile to the profile manager.
  258. *
  259. * @param profile the profile to add
  260. * @param persist true, if the profile should also be persisted immediatly,
  261. * false, if the profile should just be added internally without being persisted
  262. * (Note: you can still call persistData later to persist all the profiles)
  263. */
  264. public void addProfile(Profile profile, boolean persist){
  265. if(profile.mId == -1){
  266. profile.mId = ++mIncrementValue;
  267. }
  268. mProfiles.put(profile.mId, profile);
  269. if(persist){
  270. persistData();
  271. }
  272. if(this.mProfileListAdapter != null){
  273. this.mProfileListAdapter.add(profile);
  274. this.mProfileListAdapter.notifyDataSetChanged();
  275. }
  276. }
  277. /**
  278. * Deletes a given profile. These changes will be persisted immediatly.
  279. *
  280. * @param profile the profile to delete
  281. */
  282. public void deleteProfile(Profile profile){
  283. if(this.mProfiles.containsKey(profile.mId)){
  284. Profile p = getProfile(profile.mId);
  285. this.mProfiles.remove(profile.mId);
  286. if(p.mActivated || this.mCurrentActivatedProfile.mId == p.mId){
  287. mCurrentActivatedProfile = mRandomProfile;
  288. mRandomProfile.mActivated = true;
  289. }
  290. this.persistData();
  291. //this.dbh.deleteProfile(profile.mId);
  292. if(this.mProfileListAdapter != null){
  293. this.mProfileListAdapter.remove(profile);
  294. this.mProfileListAdapter.notifyDataSetChanged();
  295. }
  296. }
  297. }
  298. /**
  299. * Removes all profiles.
  300. */
  301. public void clearProfiles(){
  302. mProfiles.clear();
  303. persistData();
  304. }
  305. /**
  306. * Same as {@see activateProfile(Profile profile, boolean persist)} but with persist arg being always true
  307. * @param profile the profile to active
  308. */
  309. public void activateProfile(Profile profile){
  310. this.activateProfile(profile, true);
  311. }
  312. /**
  313. * Makes a given profile active.
  314. *
  315. * @param profile the profile to activate
  316. * @param persist indicates if the profile should be persisted after activating
  317. */
  318. public void activateProfile(Profile profile, boolean persist){
  319. if(profile.equals(this.mCurrentActivatedProfile)) return;
  320. if(this.mCurrentActivatedProfile != null){
  321. this.mCurrentActivatedProfile.mActivated = false;
  322. this.persistProfile(this.mCurrentActivatedProfile);
  323. }
  324. profile.mActivated = true;
  325. this.mCurrentActivatedProfile = profile;
  326. this.mProfiles.put(profile.mId, profile);
  327. mSharedEditor.putString("os", profile.mLabel);
  328. mSharedEditor.commit();
  329. if(persist) persistData();
  330. if(this.mProfileListAdapter != null){
  331. this.mProfileListAdapter.notifyDataSetChanged();
  332. }
  333. if(MainActivity.getInstance().getHostageService() != null){
  334. if(MainActivity.getInstance().getHostageService().hasRunningListeners()){
  335. List<String> protocolsToStart = profile.getActiveProtocols();
  336. if(profile.mGhostActive){
  337. protocolsToStart.add("GHOST");
  338. }
  339. for(Listener listener: MainActivity.getInstance().getHostageService().getListeners()){
  340. if(listener.isRunning()){
  341. if(protocolsToStart.contains(listener.getProtocolName()) && !listener.getProtocolName().equals("GHOST")){
  342. protocolsToStart.remove(listener.getProtocolName());
  343. } else {
  344. MainActivity.getInstance().getHostageService().stopListenerAllPorts(listener.getProtocolName());
  345. }
  346. }
  347. }
  348. MainActivity.getInstance().startMonitorServices(protocolsToStart);
  349. }
  350. }
  351. }
  352. /**
  353. * Checks if the "random" profile is currently active
  354. *
  355. * @return true, if active
  356. * false, otherwise
  357. */
  358. public boolean isRandomActive(){
  359. return this.mCurrentActivatedProfile.equals(this.mRandomProfile);
  360. }
  361. /**
  362. * Retrieves the "random" profile
  363. *
  364. * @return the "random" profile
  365. */
  366. public Profile getRandomProfile(){
  367. return this.mRandomProfile;
  368. }
  369. /**
  370. * Retrieves the currently active profile
  371. *
  372. * @return the active profile
  373. */
  374. public Profile getCurrentActivatedProfile(){
  375. return mCurrentActivatedProfile;
  376. }
  377. /**
  378. * Sets the list adapter that should also be managed by the profile manager
  379. *
  380. * @param profileListAdapter the list adapter to manage
  381. */
  382. public void setProfileListAdapter(ProfileManagerListAdapter profileListAdapter){
  383. this.mProfileListAdapter = profileListAdapter;
  384. }
  385. /**
  386. * Retrieves the list adapter, that is being managed by the profile manager
  387. * @return the list adapter
  388. */
  389. public ProfileManagerListAdapter getProfileListAdapter(){
  390. return this.mProfileListAdapter;
  391. }
  392. /**
  393. * Retrieves the number of profiles
  394. *
  395. * @return the number of profiles
  396. */
  397. public int getNumberOfProfiles(){
  398. return mProfiles.size();
  399. }
  400. /**
  401. * Pick n numbers between 0 (inclusive) and k (inclusive)
  402. * While there are very deterministic ways to do this,
  403. * for large k and small n, this could be easier than creating
  404. * an large array and sorting, i.e. k = 10,000
  405. */
  406. public Set<Integer> pickRandom(int n, int s, int k) {
  407. Random random = new Random(); // if this method is used often, perhaps define random at class level
  408. Set<Integer> picked = new HashSet<Integer>();
  409. while(picked.size() < n) {
  410. picked.add(random.nextInt(k-s) + s);
  411. }
  412. return picked;
  413. }
  414. /**
  415. * Fills the profiles manager with default profiles
  416. */
  417. public void fillWithDefaultData(){
  418. Profile windowsSeven = new Profile(
  419. 0,
  420. "Windows 7",
  421. MainActivity.getInstance().getString(R.string.profile_seven_desc),
  422. R.drawable.ic_profile_vista,
  423. false
  424. );
  425. windowsSeven.mActiveProtocols.put("SMB", true);
  426. windowsSeven.mGhostActive = true;
  427. windowsSeven.mGhostPorts = "135,5357";
  428. for(int i: pickRandom(3, 49152, 70000)){
  429. windowsSeven.mGhostPorts += "," + i;
  430. }
  431. windowsSeven.mActiveProtocols.put("ECHO", true);
  432. this.addProfile(windowsSeven, false);
  433. Profile windowsXP = new Profile(
  434. 1,
  435. "Windows XP",
  436. MainActivity.getInstance().getString(R.string.profile_xp_desc),
  437. R.drawable.ic_profile_xp,
  438. false
  439. );
  440. windowsXP.mActiveProtocols.put("SMB", true);
  441. windowsXP.mGhostActive = true;
  442. windowsXP.mGhostPorts = "135";
  443. for(int i: pickRandom(3, 49152, 80000)){
  444. windowsXP.mGhostPorts += "," + i;
  445. }
  446. windowsXP.mActiveProtocols.put("ECHO", true);
  447. this.addProfile(windowsXP, false);
  448. Profile serverHTTP = new Profile(
  449. 2,
  450. "Web Server Apache",
  451. MainActivity.getInstance().getString(R.string.profile_webserv_apache_desc),
  452. R.drawable.ic_profile_apache,
  453. false
  454. );
  455. serverHTTP.mActiveProtocols.put("HTTP", true);
  456. serverHTTP.mActiveProtocols.put("HTTPS", true);
  457. serverHTTP.mActiveProtocols.put("MYSQL", true);
  458. this.addProfile(serverHTTP, false);
  459. Profile serverWeb = new Profile(
  460. 3,
  461. "Web Server IIS",
  462. MainActivity.getInstance().getString(R.string.profile_webserv_iis_desc),
  463. R.drawable.ic_profile_apache,
  464. false
  465. );
  466. serverWeb.mActiveProtocols.put("HTTP", true);
  467. serverWeb.mActiveProtocols.put("HTTPS", true);
  468. serverWeb.mActiveProtocols.put("FTP", true);
  469. this.addProfile(serverWeb, false);
  470. Profile unixMachine = new Profile(
  471. 4,
  472. "\"Hardened\" Linux system ",
  473. MainActivity.getInstance().getString(R.string.profile_linux_hard_desc),
  474. R.drawable.ic_profile_unix,
  475. false
  476. );
  477. unixMachine.mActiveProtocols.put("SSH", true);
  478. this.addProfile(unixMachine, false);
  479. Profile linuxMachine = new Profile(
  480. 5,
  481. "Linux system",
  482. MainActivity.getInstance().getString(R.string.profile_linux_desc),
  483. R.drawable.ic_profile_linux,
  484. false
  485. );
  486. linuxMachine.mActiveProtocols.put("FTP", true);
  487. linuxMachine.mActiveProtocols.put("TELNET", true);
  488. linuxMachine.mActiveProtocols.put("HTTP", true);
  489. linuxMachine.mActiveProtocols.put("HTTPS", true);
  490. linuxMachine.mActiveProtocols.put("MYSQL", true);
  491. this.addProfile(linuxMachine, false);
  492. Profile voipServer = new Profile(
  493. 6,
  494. "VOIP Server",
  495. MainActivity.getInstance().getString(R.string.profile_voip_desc),
  496. R.drawable.ic_profile_asterisks,
  497. false
  498. );
  499. voipServer.mActiveProtocols.put("SIP", true);
  500. this.addProfile(voipServer, false);
  501. Profile randomProfile = new Profile(
  502. 7,
  503. "Random",
  504. MainActivity.getInstance().getString(R.string.profile_random_desc),
  505. R.drawable.ic_launcher,
  506. false
  507. );
  508. randomProfile.mIsRandom = true;
  509. this.addProfile(randomProfile, false);
  510. Profile paranoidProfile = new Profile(
  511. 8,
  512. "Paranoid",
  513. MainActivity.getInstance().getString(R.string.profile_paranoid_desc),
  514. R.drawable.ic_profile_paranoid,
  515. false
  516. );
  517. for(String protocol: MainActivity.context.getResources().getStringArray(R.array.protocols)){
  518. if(protocol.equals("GHOST")) continue;
  519. paranoidProfile.mActiveProtocols.put(protocol, true);
  520. }
  521. paranoidProfile.mActivated = true;
  522. this.addProfile(paranoidProfile, false);
  523. mIncrementValue = 8;
  524. this.activateProfile(paranoidProfile, false);
  525. persistData();
  526. }
  527. }