ProfileManager.java 17 KB

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