Nelson Shaw Design Project
Nelson Shaw (Talk | contribs) (→Design Ideas/Patterns/Principles) |
Nelson Shaw (Talk | contribs) (→Design Ideas/Patterns/Principles) |
||
Line 185: | Line 185: | ||
'''Consequences''': Although the Open/Closed principle should apply, in this way when any new Task subclass (e.g. PasswordSniffer) is added, changes to the GUI will have to be made. The violation of the Open/Closed principle in this case is quite small, only a few lines of code to add a new button and request a new Task would be needed. This is acceptable, as it is only a small violation, and the cost of finding an alternative solution would be too great. | '''Consequences''': Although the Open/Closed principle should apply, in this way when any new Task subclass (e.g. PasswordSniffer) is added, changes to the GUI will have to be made. The violation of the Open/Closed principle in this case is quite small, only a few lines of code to add a new button and request a new Task would be needed. This is acceptable, as it is only a small violation, and the cost of finding an alternative solution would be too great. | ||
+ | |||
+ | |||
+ | '''Open/Closed Principle''': This principle is adhered to in all parts of the design apart from the GUI. Classes such as Task and Result can easily be extended to incorporate new functionality. On the other hand, as discussed earlier, whenever new functionality is added it will inevitably trickle back to the GUI. This means that the GUI will have to be modified in some way, but generally it is only adding some code for a new button or so, which is acceptable. | ||
+ | |||
+ | '''Liskov Substitution Principle''': This principle basically states that wherever a super class is used, its sub-class should be able to be used also. This principle is evident with the design of the Task, Result and EncryptionStandard class hierarchies. | ||
+ | |||
+ | '''Design by Contract''': This principle involves designing methods in the software, such that they define conditions on their initial state and final state. In most cases contracts have been created, but due to some time constraints, others have not been created yet. | ||
+ | |||
+ | '''One key abstraction''': This principles states that each class should have one key abstraction captured within it. This principle is adhered to, as specific care was taken to ensure that each class didn't have more functionality than it should. In the case of the Task and Result classes, these were especially created separately, so that each would have a single principle they captured completely. | ||
=== Design Criticism and Discussion === | === Design Criticism and Discussion === |
Revision as of 23:41, 30 September 2010
Contents |
Trojan Project
As part of the COSC429 Information Warfare course, development of a Trojan module is required.
Background
The software should have command and remote modules, which are connected together via sockets. The command module will be running on the attackers computer, while the implant module will be running on the compromised computer. Bypassing security and installing the remote component onto the targeted computer is not required. It is assumed that the implant can be installed by a 3rd party and will be constantly running. Therefore, the focus of this design is providing the functionality required and ensuring secure communication.
Design Study
Functionality
Basic
- Remote command channel
- Implant registration with server
- Screen capture
- Keyboard capture
- Target File browse
- File upload/download
Advanced Features
- Cryptographic channel protection
- Implant hiding
- Targer user list
- List of active processes
- Extract web history
- Extract email
- Audit log
Requirements
- Secure - Components and communication of software should be secure and unable to modify
- Maintainability - Easy to add new functionality (e.g. Multiple remote modules)
- Extensible - Add additional software components easily (e.g. Software to deliver remote module onto target system)
Constraints
Remote and control module communication, processing speed and associated overhead costs.
Many of the tasks require use of the Win32 API. It is also thought that serialization would be a desirable property in terms of sending data between the two modules. For this reason the development will take place in C#.
Initial Design
The following is my first design attempt.
Description of Classes
ControlUnit ControlUnit is the main class of the control module. An observer pattern is used with the GUI and Controller. It contains a list of data objects which can then be displayed by the GUI. The data objects have a specified type e.g. Picture, Text, ... and so it will be up to the GUI to organise how to display them. Therefore any knowledge of the task from which the data came is unnecessary. The ControlUnit class will also have the appropriate update functions to enable an observer pattern.
Communicator Communicator is used to set up a connection between the control and remote modules. The type of encryption can be set, by passing an EncryptionStandard object. Both the ControlUnit and Remote objects create instances of the Communicator object. The main way the remote and control units will communicate, is by serializing and sending Task objects. Once an object is serialized it is then encrypted and sent. When received it is first decrypted and then de-serialized.
Serializer The serializer class is used to serialize task objects and return them as a byte array. It is also used to de-serialize objects.
EncryptionStandard The use of EncryptionStandard with the various subclasses allows the program to be extended to incorporate various encryption types. For the current implementation, it is expected that "Unsecured" and "RSA" Encryption Standards will be implemented. But in future it is possible to add say, AES encryption. By using the Strategy pattern, each subclass overrides the EncryptionStandard's functions such as encrypt and decrypt.
Task By using the strategy pattern for the Task class, it allows easy modification to add new functionality to the program. Currently, Screenshot and Keylog functionality is provided, but it should be relatively simple to add more. Once a task is performed by the remote program, data is added to the task which is then sent back to the control unit. The data class can be one of various types.
Data Data is a class that carries information produced on the Remote host from performing a Task. It has a type attribute which describes the type of data it is, as well as a text heading.
Initial Design Critique
As this was just an initial design, some elements are still missing. However, there are still some issues:
Displaying of the data by a list of Data objects in ControlUnit is perhaps not optimal? The GUI still has to know about the Data types that are possible, but is this an inevitable coupling?
Design patterns/ideas/principles used
Strategy is used in the Task and EncryptionStandard class design. In both classes, different types of tasks and encryption standards perform various functions differently. Because of this, strategy was used to create subclasses with functions that override that of it's super class.
Open Closed Principle The open closed principle states that the design should be open for extension but closed to modification. Taking the example of Tasks, a new task subclass can be easily added (extension), without the Task object itself being changed (no modification). In saying this, modification will be required in relation to the GUI providing the option to select this new task, but this is an inevitable problem.
Singleton Only one communicator object can exist in each program??
Observer The design pattern used linking the ControlUnit class with the GUI is an Observer pattern. The GUI will look at the list of data objects and display them according to their type.
Separation of concerns The classes are created to capture a single abstraction in each class.
2nd Attempt
Here is a slightly more updated version of my UML diagram. The basic structure is correct I think, but many more functions will be added to Communicator once I learn more about socket programming.
What Changed
More individual methods in classes have been added. The design now presents a simple version of the Observer pattern. The basic idea is that once a task is complete, it will add the collected data to it the ControlUnit's taskData list. The ControlUnit will then call notifyGUI, which calls the GUI's update method. The GUI's update method will look for new data to display from the getTaskData method in ControlUnit and display it. In this way, the ControlUnit doesn't have to know what data the GUI is interested in displaying, it only tells it when something changes.
Design Critique
There are still a number of design issues with this version. Namely, what's the best way for the GUI to know about display information regarded to each task? At this point I have opted for the GUI to only know about how to display a type T data. The DataType is defined as an Enum - which will be updated as the types of data required become clearer. This may be a unnecessary though, as the GUI is probably going to have to know about the various tasks that can be performed. This would be so that the user could choose which task they want performed.
Another issue is how the Communicator object will communicate with its counterpart object. This is related to how socket programming works, and will be updated once I learn more on the subject.
The EncryptionStandard object may not be as extensible as first thought. I originally planned for it to be able to handle all encryption standard types. At the moment I'm thinking the design may be limited to symmetric cryptography. Using asymmetric (public key) cryptography could be more of an issue with key development and negotiation, so at this stage it is based on a shared key system. However, through this design it should still be possible to add public key cryptography later. Effort will have to be made with the way that Communicator connects to its counterpart object, so that the connection procedure does not inhibit any possible key negotiation procedure.
3rd Attempt and Implementation
OK so it turns out that design and implementation are BOTH important parts of the whole software creation process. As Wal was saying in lectures, often it's not until you begin to implement your design that you discover problems. This is probably more exaggerated in my case, as I don't have a substantial OO design background. This meant my design probably had issues I should have seen earlier.
Here's a copy of the latest UML diagram.
What changed
In order to use serialization to send tasks between the implant and controller, the Task object had to be defined in a common way. This resulted in creating a DLL which included the Task, Encryption and Serializer classes and sub-classes. This DLL is then referenced from both the Implant and ControlUnit and allows tasks to be sent over the Socket connection and be serialized / de-serialized at each end.
How the data for each task is stored, and its relationship with the GUI. This basically comes down to the types of data that the GUI will have to be able to display. Originally, the idea was that the Data class would store the information created by performing the Task on the Implant machine. This Data would then be serialized and sent back to the ControlUnit where the GUI would know how to display it. After beginning the implementation, it became clear that there were many different types of data that the GUI would have to display. Examples are: Bitmap, List<String>, TreeNodes and so on. It also became clear that the where and how to display the Data in the GUI was dependent on the Task being performed. E.g. the ScreenShot task has a Bitmap that is displayed as a picture in the first tab. The idea of using a Data class seemed to just be creating a redundant accessory which was not needed. Since I am en devouring to "Keep things as simple as possible", I decided that the Data class should be removed and the data itself returned within the Task object. I argue that in order to know how to display the Data, the GUI has to know the type of the Data. This is no different to having to know the type of the Task which is returned. I also don't think it breaks the principle of "One key abstraction per class", as a Task should be able to perform its duty and store the resulting information under this idea. Any approach to this problem, whether using the Data class or not, leads to the inevitable conclusion of the GUI having to find out what type of data it is. This could be solved by the GUI requesting that a given task is performed (e.g. from User click on "Get Screenshot"), and the resulting task object received in the same method. In this way, depending on the button that was clicked, the GUI would always know what type of returning task it would be. This has the unfortunate property of "hanging" the GUI, while the task is performed (sent to the Implant and back). If this is something like a file upload/download, this is not acceptable. Because of this, the GUI has to be updated whenever a new task is received. It also has to know what type type of Task it is in order to display it correctly. One way of solving the problem is to use reflections in C#. Unfortunately, this is really just a switch statement. So I ended up with a huge switch statement smell.
After talking to Wal, I realized the Command pattern could be used to display the data in the GUI. Adding additional functionality to the Task class to accommodate this would violate the "One key abstraction" principle. For this reason, a new class called Result was created. Result's primary duty is to return a Control object (Windows Form Object) which can then be displayed by the GUI. The Result class uses the command pattern and has subclasses which override the getDisplayControl method. Depending on the type of Result sub-class, a different Control is returned. In this way, the GUI only has to keep track of where to display the data (Tabpage, Size, Location), but does not know anything more than this. For each of the Task's that are performed, it means that a Compatible Result sub-class must be defined. At the moment, this is limited to 3 classes - BitmapResult, ListStringResult and TreeResult.
The Observer pattern. As this software was implemented in C#, the Observer pattern is not incorporated as a language feature until the very latest release. Even though I tried many times, my Visual Studio would not recognise the latest C#, and so I couldn't get access to the IObservable library. As a side thought, I thought that if this must be shown to our lecturer on a University computer, it is unlikely that they will have the latest C# supporting these features. For these two reasons, I decided to implement my own home brew observer pattern. It has some limitations, i.e. that the Observer must be of type "Form" (GUI is a subclass of Form), but as it doesn't have multiple inheritance, there is no way to get around this. However, I do think that it is acceptable that any observer will be a Form object, as there should be no other reason to want to observe the ControlUnit. Whenever a task is received, the "notifyObservers" method is called, which in turns notifies all observers. The observers can then observe the changed state of the ControlUnit, which in this case is "getReceivedTask". This provides a clean and efficient way to manage any observers monitoring the ControlUnit, and provides an easy ability to add more GUI functionality/forms later.
For much of the design, I thought it was necessary that a duplicate code smell exist. This was in terms of the two slightly differing Communicator classes. One exists to serve the Implant and the other to serve the ControlUnit. Each version of the Communicator class had a reference to either the ControlUnit or Implant. Although the same class could be used for both, for the Implant Communicator class to have a reference to the ControlUnit would be bad design and vice versa. Instead, the Communicator class is slightly modified for both the ControlUnit and Implant, so that it only has reference to one of them. I thought the only other solution would be to make the Implant and ControlUnit subclasses of another class, which could be referenced by the Communicator. This is an inefficient and over-the-top approach to solve the problem (Inheritance for implementation). It was only when reviewing my project in class, that Joey told me how a delegate could be used in C#. In this way, the Communicator takes a delegate which is used whenever a Task is recieved. The delegate references the taskRecieved(Task task) method of either the ControlUnit or Implant. Cheers for that Joey!
Design Ideas/Patterns/Principles
Due to the nature of the design, there isn't necessarily the same potential for a beautiful OO design as there is in other problem domains. This concept is evident in my design, as there are very few of the clever design patterns and architectures applied. This isn't to say that I didn't consider them for certain parts of the design, but I was wary of applying design patterns for the sake of them. This is a common mistake -- Using design patterns without weighing up the consequences of using them. In many cases, I felt that a clever design pattern could be applied, but it was not necessary.
For ease of understanding the rationales behind specific design decisions, I have broken it down into the following list.
Task Class and Subclasses
Pattern: Command Pattern
Possible Alternatives: Composite and Memento
Rationale: The command pattern allows the creation, serialization and processing of Task objects. It means that the Implant that processes the Task object does not have to know what type of the Task the object is. The alternatives patterns weren't really considered as they would provide functionality which isn't required in this situation. The Composite pattern would allow several commands to be put together and sent, but this seems unnecessary as we only want a single command processed at a time. A memento pattern would allow commands to be un-done, but once again was unnecessary for this situation.
Result Class and Subclasses
Pattern: Command Pattern
Possible Alternatives: Reflections/Switch Statement
Rationale: The Result class was created as a way to display data in the GUI. It removes the coupling that existed between the GUI and the Task objects, with the GUI having to know all about the specific Task sub-classes and how to display them. By doing this, it means that the design of the GUI class is much simpler.
Consequences: The additional class hierarchy that has been created (Result), which complicates the design a bit. Also, the GUI has no control over the type of Control returned by the Result class, but it can still position it and change the size. It also means that the GUI has to know where to display the Control object, but this is certainly a manageable task for a GUI.
EncryptionStandard Class and Subclasses
Pattern: Command Pattern
Possible Alternatives: Composite
Rationale: Using the pattern enables different Encryption sub-classes to override the encrypt and decrypt functions with their own encryption mechanisms. Composite was considered to allow different encryption schemes to be added to together, but YAGNI prevailed. i.e. One good encryption scheme is probably enough.
ControlUnit and GUI
Pattern: Observer
Possible Alternatives: Model View Controller
Rationale: Using the Observer pattern allows the GUI reflect the ControlUnit class to the user. The GUI is informed when a new Result has been received, and makes appropriate changes. The MVC pattern was not used as the Controller class was not really required. There is only a very low level of coupling between the ControlUnit and GUI (e.g. when the GUI requests a ScreenShot after the "Screenshot" button is clicked).
Consequences: Although the Open/Closed principle should apply, in this way when any new Task subclass (e.g. PasswordSniffer) is added, changes to the GUI will have to be made. The violation of the Open/Closed principle in this case is quite small, only a few lines of code to add a new button and request a new Task would be needed. This is acceptable, as it is only a small violation, and the cost of finding an alternative solution would be too great.
Open/Closed Principle: This principle is adhered to in all parts of the design apart from the GUI. Classes such as Task and Result can easily be extended to incorporate new functionality. On the other hand, as discussed earlier, whenever new functionality is added it will inevitably trickle back to the GUI. This means that the GUI will have to be modified in some way, but generally it is only adding some code for a new button or so, which is acceptable.
Liskov Substitution Principle: This principle basically states that wherever a super class is used, its sub-class should be able to be used also. This principle is evident with the design of the Task, Result and EncryptionStandard class hierarchies.
Design by Contract: This principle involves designing methods in the software, such that they define conditions on their initial state and final state. In most cases contracts have been created, but due to some time constraints, others have not been created yet.
One key abstraction: This principles states that each class should have one key abstraction captured within it. This principle is adhered to, as specific care was taken to ensure that each class didn't have more functionality than it should. In the case of the Task and Result classes, these were especially created separately, so that each would have a single principle they captured completely.
Design Criticism and Discussion
Comments
Please feel free to leave any comments, criticisms, ideas or problems you have. Speak your mind! :P
Thanks to User: James Ashford for the project page template.