diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..4838749
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,190 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2016 Peter Kacherginsky
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index d71c0d4..991ea0c 100644
--- a/README.md
+++ b/README.md
@@ -14,15 +14,739 @@ by Andrew Honig and Michael Sikorski.
The tool allows you to intercept and redirect all or specific network traffic
while simulating legitimate network services. Using FakeNet-NG, malware analysts
-can quickly identify malware’s functionality and capture network signatures.
-Penetration testers and bug hunters will find FakeNet-NG’s configurable
+can quickly identify malware's functionality and capture network signatures.
+Penetration testers and bug hunters will find FakeNet-NG's configurable
interception engine and modular framework highly useful when testing
-application’s specific functionality and prototyping PoCs.
+application's specific functionality and prototyping PoCs.
-Release
-========
+Installation
+============
+
+You can install FakeNet-NG in a few different ways.
+
+Stand-alone executable
+----------------------
+
+It is easiest to simply download the compiled version which can be obtained from
+the releases page:
+
+ https://www.github.com/fireeye/flare-fakenet-ng/releases
+
+Execute FakeNet-NG by running 'fakenet.exe' from the downloaded directory.
+
+This is the preferred method as it does not require you to install any
+additional modules, which is ideal for a malware analysis machine.
+
+Installing module
+-----------------
+
+Alternatively you can install FakeNet-NG as a python module using pip:
+
+ pip install https://github.com/fireeye/flare-fakenet-ng/zipball/master
+
+Or by obtaining the latest source code and installing it manually:
+
+ git clone https://www.github.com/fireeye/flare-fakenet-ng/
+
+Change directory to the downloaded flare-fakenet-ng and run:
+
+ python setup.py install
+
+Execute FakeNet-NG by running 'fakenet' in any directory.
+
+No installation
+---------------
+
+Finally if you would like to avoid installing FakeNet-NG and just want to run it
+as is (e.g. for development), then you would need to obtain the source code and
+install dependencies as follows:
+
+1) Install 64-bit or 32-bit Python 2.7.x for the 64-bit or 32-bit versions
+ of Windows respectively.
+
+2) Install Python dependencies:
+
+ pip install pydivert
+ pip install dnslib
+ pip install dpkt
+
+ *NOTE*: pydivert will also download and install WinDivert library and
+ driver in the `%PYTHONHOME%\DLLs` directory. FakeNet-NG bundles those
+ files so they are not necessary for normal use.
+
+2b) Optionally, you can install the following module used for testing:
+
+ pip install requests
+
+3) Download the FakeNet-NG source code:
+
+ git clone https://github.com/fireeye/flare-fakenet-ng
+
+Execute FakeNet-NG by running it with a Python interpreter:
+
+ python fakenet.py
+
+Usage
+=====
+
+The easiest way to run FakeNet-NG is to simply execute the provided
+executable as an Administrator. You can provide `--help` command-line
+parameter to get simple help:
+
+ C:\tools\fakenet-ng>fakenet.exe --help
+ ______ _ ________ _ _ ______ _______ _ _ _____
+ | ____/\ | |/ / ____| \ | | ____|__ __| | \ | |/ ____|
+ | |__ / \ | ' /| |__ | \| | |__ | |______| \| | | __
+ | __/ /\ \ | < | __| | . ` | __| | |______| . ` | | |_ |
+ | | / ____ \| . \| |____| |\ | |____ | | | |\ | |__| |
+ |_|/_/ \_\_|\_\______|_| \_|______| |_| |_| \_|\_____|
+
+ Version 1.0
+ _____________________________________________________________
+ Developed by
+ Peter Kacherginsky
+ FLARE (FireEye Labs Advanced Reversing Engineering)
+ _____________________________________________________________
+ Usage: fakenet.py [options]:
+
+ Options:
+ -h, --help show this help message and exit
+ -c FILE, --config-file=FILE
+ configuration filename
+ -v, --verbose print more verbose messages.
+ -l LOG_FILE, --log-file=LOG_FILE
+
+As you can see from the simple help above it is possible to configure the
+configuration file used to start FakeNet-NG. By default, the tool uses
+`configs\default.ini`; however, it can be changed with the `-c` parameter.
+There are several example configuration files in the `configs` directory.
+Due to the large number of different settings, FakeNet-NG relies on the
+configuration files to control its functionality.
+
+NOTE: FakeNet-NG will attempt to locate the specified configuration file, first
+by using the provided absolute or relative path in case you want to store all of
+your configurations. If the specified configuration file is not found,
+then it will try to look in its `configs` directory.
+
+The rest of the command-line options allow you to control the amount
+of logging output displayed as well as redirecting it to a file as
+opposed to dumping it on the screen.
+
+Simple run
+----------
+
+Before we dive in and run FakeNet-NG let's go over a few basic concepts. The
+tool consists of several modules working together. One such important module is
+the diverter which is responsible for redirecting traffic to a collection of
+listeners. The diverter forces applications to interact with FakeNet-NG as
+opposed to real servers. Listeners are individual services handling incoming
+connections and allowing us to examine application's traffic (e.g. malware
+signatures).
+
+Let's launch FakeNet-NG using default settings by running the following command:
+
+ C:\tools\fakenet-ng>fakenet.exe
+
+Below is the annotated output log illustrating a sample intercepted DNS request
+and an HTTP connection:
+
+ ______ _ ________ _ _ ______ _______ _ _ _____
+ | ____/\ | |/ / ____| \ | | ____|__ __| | \ | |/ ____|
+ | |__ / \ | ' /| |__ | \| | |__ | |______| \| | | __
+ | __/ /\ \ | < | __| | . ` | __| | |______| . ` | | |_ |
+ | | / ____ \| . \| |____| |\ | |____ | | | |\ | |__| |
+ |_|/_/ \_\_|\_\______|_| \_|______| |_| |_| \_|\_____|
+
+ Version 1.0
+ _____________________________________________________________
+ Developed by
+ Peter Kacherginsky
+ FLARE (FireEye Labs Advanced Reverse Engineering)
+ _____________________________________________________________
+
+ 07/06/16 10:20:52 PM [ FakeNet] Loaded configuration file: configs/default.ini
+ /
+ default configuration file /
+
+ 07/06/16 10:20:52 PM [ Diverter] Capturing traffic to packets_20160706_222052.pcap
+ /
+ PCAP output file /
+
+ 07/06/16 10:20:52 PM [ FakeNet] Anonymous Forwarder listener on TCP port 8080...
+ \
+ \ Anonymous Listener rule
+
+ 07/06/16 10:20:52 PM [ RawTCPListener] Starting...
+ 07/06/16 10:20:52 PM [ RawUDPListener] Starting...
+ 07/06/16 10:20:52 PM [ FilteredListener] Starting...
+ 07/06/16 10:20:52 PM [ DNS Server] Starting...
+ 07/06/16 10:20:52 PM [ HTTPListener80] Starting...
+ 07/06/16 10:20:52 PM [ HTTPListener443] Starting...
+ 07/06/16 10:20:52 PM [ SMTPListener] Starting...
+ 07/06/16 10:20:52 PM [ Diverter] Starting...
+ \
+ \ Listeners starting up
+
+ 07/06/16 10:20:52 PM [ Diverter] Diverting ports:
+ 07/06/16 10:20:52 PM [ Diverter] TCP: 1337, 80, 443, 25
+ 07/06/16 10:20:52 PM [ Diverter] UDP: 1337, 53
+ /
+ Summary of diverted ports /
+
+ 07/06/16 10:21:03 PM [ Diverter] Modifying outbound external UDP request packet:
+ 07/06/16 10:21:03 PM [ Diverter] from: 192.168.250.140:49383 -> 4.2.2.1:53
+ 07/06/16 10:21:03 PM [ Diverter] to: 192.168.250.140:49383 -> 192.168.250.140:53
+ 07/06/16 10:21:03 PM [ Diverter] pid: 456 name: malware.exe
+ /
+ Intercepted traffic to the DNS server from malware.exe /
+
+ 07/06/16 10:21:03 PM [ DNS Server] Received A request for domain 'evil.com'.
+ \
+ \ Fake DNS Listener handling the above request
+
+ 07/06/16 10:21:04 PM [ Diverter] Modifying outbound external TCP request packet:
+ 07/06/16 10:21:04 PM [ Diverter] from: 192.168.250.140:2179 -> 192.0.2.123:80
+ 07/06/16 10:21:04 PM [ Diverter] to: 192.168.250.140:2179 -> 192.168.250.140:80
+ 07/06/16 10:21:04 PM [ Diverter] pid: 456 name: malware.exe
+ /
+ Intercepted traffic to the web server from malware.exe /
+
+ 07/06/16 10:21:08 PM [ HTTPListener80] Received a GET request.
+ 07/06/16 10:21:08 PM [ HTTPListener80] --------------------------------------------------------------------------------
+ 07/06/16 10:21:08 PM [ HTTPListener80] GET / HTTP/1.0
+ 07/06/16 10:21:08 PM [ HTTPListener80]
+ 07/06/16 10:21:08 PM [ HTTPListener80] --------------------------------------------------------------------------------
+ \
+ \ Fake HTTP Listener handling the above request
+
+Notice that each log line has a name of the currently running FakeNet-NG
+modules. For example, when it is diverting traffic, the logs will be prefixed
+with the `Diverter` label:
+
+ 07/06/16 10:21:03 PM [ Diverter] Modifying outbound external UDP request packet:
+ 07/06/16 10:21:03 PM [ Diverter] from: 192.168.250.140:49383 -> 4.2.2.1:53
+ 07/06/16 10:21:03 PM [ Diverter] to: 192.168.250.140:49383 -> 192.168.250.140:53
+ 07/06/16 10:21:03 PM [ Diverter] pid: 456 name: malware.exe
+
+At the same time, whenever individual listeners are handling diverted traffic,
+logs will be labeled with the name set in the configuration file:
+
+ 07/06/16 10:21:03 PM [ DNS Server] Received A request for domain 'evil.com'.
+
+To stop FakeNet-NG and close out the generated PCAP file simply press `CTRL-C`:
+
+ 07/06/16 10:21:41 PM [ FakeNet] Stopping...
+ 07/06/16 10:21:42 PM [ HTTPListener80] Stopping...
+ 07/06/16 10:21:42 PM [ HTTPListener443] Stopping...
+ 07/06/16 10:21:42 PM [ SMTPListener] Stopping...
+ 07/06/16 10:21:43 PM [ Diverter] Stopping...
+
+Configuration
+-------------
+
+In order to take full advantage of FakeNet-NG's capabilities we must understand
+its configuration file structure and settings. Below is a sample configuration
+file:
+
+ ###############################################################################
+ # Fakenet Configuration
+
+ [FakeNet]
+
+ DivertTraffic: Yes
+
+ ###############################################################################
+ # Diverter Configuration
+
+ [Diverter]
+
+ DumpPackets: Yes
+ DumpPacketsFilePrefix: packets
+
+ ModifyLocalDNS: No
+ StopDNSService: Yes
+
+ ###############################################################################
+ # Listener Configuration
+
+ [DNS Server]
+ Enabled: True
+ Port: 53
+ Protocol: UDP
+ Listener: DNSListener
+ DNSResponse: 192.0.2.123
+ NXDomains: 0
+
+ [RawTCPListener]
+ Enabled: True
+ Port: 1337
+ Protocol: TCP
+ Listener: RawListener
+ UseSSL: No
+ Timeout: 10
+
+The configuration file is broken up into several sections.
+
+* **[FakeNet]** - Controls the behavior of the application itself. The only valid
+option at this point is `DivertTraffic`. When enabled, it instructs the tool
+to launch the appropriate diverter plugin and intercept traffic. If this option
+is disabled, FakeNet-NG will still launch listeners, but will rely on another
+method to direct traffic to them (e.g. manually change DNS server).
+
+* **[Diverter]** - Settings for redirecting traffic. Covered in detail below.
+
+* **[Listener Name]** - A collection of listener configurations. Each listener
+has a set of default settings (e.g. port, protocol) as well as listener
+specific configurations (e.g. DumpHTTPPosts for the HTTPListener).
+
+Diverter Configuration
+----------------------
+
+Considering you have enabled the `DivertTraffic` setting in the `[FakeNet]`
+configuration block, the tool will enable its traffic redirection engine to
+which we will call `diverter` from now on as a reference to the excellent
+`WinDiverter` library used to perform the magic behind the scenes.
+
+The diverter will examine all of the outgoing packets and match them against
+a list of protocols, ports of enabled listeners. If there is a listener
+listening on the packet's port and protocol, then the destination address
+will be changed to the local machine's IP address where the listener will
+handle the request. At the same time, responses coming from the listener
+will be changed so that the source IP address would appear as if the packet
+is coming from the originally requested host.
+
+You can optionally enable the `DumpPackets` setting to store all traffic
+observed by FakeNet-NG (redirected or forwarded) to a PCAP file. It is possible
+to decrypt SSL traffic between an intercepted application and one of the
+listeners with SSL support. Use the instructions at the following page:
+
+ https://wiki.wireshark.org/SSL
+
+The keys `privkey.pem` and `server.pem` used by FakeNet-NG's servers are in the
+application's root directory.
+
+Last but not least, the Windows diverter supports two DNS related settings:
+
+* **ModifyLocalDNS** - modify local machine's DNS service.
+* **StopDNSService** - stops Windows DNS client (Dnscache). This allows FakeNet
+ to see the actual processes resolving domains as opposed
+ to the generic 'svchost.exe' process.
+
+Redirecting All Traffic
+-----------------------
+
+By default the diverter will only intercept traffic that has a dedicated
+listener created for it. However, by enabling `RedirectAllTraffic` setting
+and configuring the default TCP and UDP handlers with the `DefaultTCPListener`
+and `DefaultUDPListener` settings it is possible to dynamically handle traffic
+going to ports not explicitly defined in one of the listeners. For example,
+let's look at a sample configuration which redirects all traffic to
+local TCP and UDP listeners on ports 1234:
+
+ RedirectAllTraffic: Yes
+ DefaultTCPListener: TCPListener1234
+ DefaultUDPListener: UDPListener1234
+
+*NOTE*: We are jumping a bit ahead with listener definitions, but just
+consider that `TCPListener1234` and `UDPListener1234` will be defined in
+the section below.
+
+With the `RedirectAllTraffic` setting, FakeNet-NG will modify not only the
+destination address, but also the destination port so it can be handled
+by one of the default listeners. Below is a sample log of traffic destined to
+an external host IP address 1.1.1.1 on port 4444 which was redirected to the
+default listener on port 1234 instead:
+
+ 07/06/16 01:13:47 AM [ Diverter] Modifying outbound external TCP request packet:
+ 07/06/16 01:13:47 AM [ Diverter] from: 192.168.66.129:1650 -> 1.1.1.1:4444
+ 07/06/16 01:13:47 AM [ Diverter] to: 192.168.66.129:1650 -> 192.168.66.129:1234
+ 07/06/16 01:13:47 AM [ Diverter] pid: 3716 name: malware.exe
+
+It is important to note that traffic destined to the port from one of the
+explicitly defined listeners will still be handled by that listener and
+not the default listener. For example, default UDP listener will not handle
+DNS traffic if a separate UDP port 53 DNS listener is defined.
+
+One issue when enabling the `RedirectAllTraffic` options is that you may
+still want to let some traffic through to ensure normal operation of the
+machine. Consider a scenario where you are trying to analyze an application
+ that still needs to connect to an external DNS server. You can utilize the
+`BlackListPortsTCP` and `BlackListPortsUDP` settings to define a list of
+ports to which traffic will be ignored and forwarded unaltered:
+
+ BlackListPortsUDP: 53
+
+Some other diverter settings that you may consider are `ProcessBlackList`
+and `HostBlackList` which allow diverter to ignore and forward traffic
+coming from a specific process name or destined for a specific host
+respectively.
+
+Listener Configurations
+----------------------
+
+Listener configurations define the behavior of individual listeners. Let's
+look at a sample listener configuration:
+
+ [TCPListener1234]
+ Enabled: True
+ Port: 1234
+ Protocol: TCP
+ Listener: RawListener
+ UseSSL: Yes
+ Timeout: 10
+
+The configuration above consists of the listener name `TCPListener1234`. It
+will be used for logging purposes so you can distinguish between different
+listeners handling connections even if they are handling the same protocol.
+
+The following settings are generic for all listeners:
+
+ * **Enabled** - specify whether or not the listener is enabled.
+ * **Port** - TCP or UDP port to listen on.
+ * **Protocol** - TCP or UDP
+ * **Listener** - Listener name to handle traffic.
+ * **ProcessWhiteList** - Only traffic from these processes will be modified
+ and the rest will simply be forwarded.
+ * **ProcessBlackList** - Traffic from all but these processes will be simply
+ forwarded and the rest will be modified as needed.
+ * **HostWhiteList** - Only traffic to these hosts will be modified and
+ the rest will be simply forwarded.
+ * **HostBlackList** - Traffic to these hosts will be simply forwarded
+ and the rest will be modified as needed.
+ * **ExecuteCmd** - Execute command on the first connection packet. This
+ feature is useful for extending FakeNet-NG's functionality
+ (e.g. launch a debugger on the connecting pid to help with
+ unpacking and decoding.)
+
+The `Port` and `Protocol` settings are necessary for the listeners to know to
+which ports to bind and, if they support multiple protocol (e.g RawListener),
+decide which protocol to use. They are also used by the diverter to figure out
+which ports and protocols to redirect.
+
+The `Listener` setting defines one of the available listener plugins to handle
+redirected traffic. The current version of FakeNet-NG comes with the following
+listeners:
+
+* **DNSListener** - supports DNS protocol and replies to A records with either
+ a local machine's IP address or a configurable address in
+ the `DNSResponse` setting. You can also set the `NXDomains`
+ attribute to the number of requests the listener should ignore.
+ This way you may be able to get the malware to request all of
+ its backup C2 controller names. The listener supports both TCP
+ and UDP protocols.
+* **RawListener** - supports basic TCP and UDP binary protocols. The default
+ behavior is to simply echo the received packets back to
+ the client. Supports SSL connections.
+* **HTTPListener** - supports HTTP and HTTPS protocols. Responds with different
+ files in the configurable `Webroot` directory based on the
+ requested file extension. Optionally dumps POST requests to
+ a configurable file which can be specified using
+ `DumpHTTPPosts` and `DumpHTTPPostsFilePrefix` settings.
+* **SMTPListener** - supports SMTP protocol.
+
+
+NOTE: FakeNet-NG will attempt to locate the webroot directory, first by using
+the provided absolute or relative paths. If the specified webroot path is not
+found, then it will try to look in its `defaultFiles` directory.
+
+As a special case, FakeNet-NG automatically responds to all ICMP requests while
+running. So in case a malware attempts to ping a host to test connectivity it
+will get a valid response.
+
+Listener Filtering
+------------------
+
+FakeNet-NG supports several filtering rules consisting of process and host
+blacklists and whitelists. The whitelists are treated as the rules that allow
+connections to the listeners while the blacklists are used to ignore the
+incoming connections and let them to be simply forwarded.
+
+For example, consider the configuration below with process and host filters:
+
+ [FilteredListener]
+ Enabled: True
+ Port: 31337
+ Protocol: TCP
+ Listener: RawListener
+ UseSSL: No
+ Timeout: 10
+ ProcessWhiteList: malware.exe, ncat.exe
+ HostBlackList: 5.5.5.5
+
+The `FilteredListener` above will only handle connection coming from the
+processes `malware.exe` and `ncat.exe`, but will ignore any connections
+destined for the host `5.5.5.5`. Meaning that if a process called `test.exe`
+attempted to connect on port 31337 it will not be redirected to the listener
+and will be forwarded to wherever it was originally intended if the route
+is available.
+
+At the same time of the process `malware.exe` attempted to connect to port 31337
+on any host other than `5.5.5.5` it will be diverted
+to the `FilteredListener`. Any connections from the process `malware.exe`
+destined to `5.5.5.5` would be allowed through.
+
+Listener Command Execution
+--------------------------
+
+Another powerful configuration setting is `ExecuteCmd`. It essentially
+allows you to execute an arbitrary command on the first detected packet
+of the connection. The value of `ExecuteCmd` can use several format string
+variables:
+
+ * `{pid}` - process id
+ * `{procname}` - process executable name
+ * `{src_addr}` - source address
+ * `{src_port}` - source port
+ * `{dst_addr}` - destination address
+ * `{dst_port}` - destination port
+
+Consider a scenario of a packed malware sample which connects to a configured
+C2 server on port 8443 (Use `RedirectAllTraffic` if the port is not known). In
+many cases the malware would unpack itself by the time it makes the connection
+making that point in execution ideal to attach to the process with a debugger
+and dump an unpacked version of it for further analysis.
+
+Let's see how this can be used to automatically launch a debugger on the
+first connection:
+
+ [C2Listener]
+ Enabled: True
+ Port: 8443
+ Protocol: TCP
+ Listener: RawListener
+ UseSSL: Yes
+ Timeout: 300
+ ProcessWhiteList: malware.exe
+ ExecuteCmd: C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe -p {pid}
+
+Once FakeNet-NG detects a new connection coming from the whitelisted process
+`malware.exe` (this setting is optional), it will automatically launch `windbg`
+and attach it to the connecting process.
+
+*NOTE*: You might want to extend the normal `Timeout` setting in case the malware
+ needs to further interact with the listener.
+
+Anonymous Listener
+------------------
+
+There is a special use case where you can create a new listener configuration
+without defining the actual listener to handle it:
+
+ [Forwarder]
+ Enabled: True
+ Port: 8080
+ Protocol: TCP
+ ProcessWhiteList: chrome.exe
+
+Without a listener defined, FakeNet-NG will still divert traffic to the local
+machine, but a separate listener must be launched by the user. For example,
+you could have an HTTP proxy listening for connections on port 8080 and let
+FakeNet-NG intercept all the traffic from applications which do not use system's
+proxy server settings or use hard-coded IP addresses. Using anonymous listeners
+you can bring FakeNet-NG's advanced traffic and process filtering capabilities
+to 3rd party tools.
+
+You may also want to enable diverter's `ProcessBlackList` setting to allow
+the external tool to communicate out to the Internet. For example, to allow
+an HTTP proxy to forward proxied traffic add its process name to the process
+blacklist. For example, add the following process to let Burp Proxy to
+communicate out to the Internet:
+
+ ProcessBlackList: java.exe
+
+In the scenario where application communicates on an unknown port, but you still
+want to redirect it to the anonymous listener on port 8080 you can define the
+default listener as follows:
+
+ RedirectAllTraffic: Yes
+ DefaultTCPListener: ForwarderTCP
+ DefaultUDPListener: RawUDPListener
+
+Finally, to allow DNS traffic to still go to the default DNS server on the
+Internet, while redirecting all other traffic, add port 53 to the diverter's
+UDP port blacklist as follows:
+
+ BlackListPortsUDP: 53
+
+Development
+===========
+
+FakeNet-NG is developed in Python which allows you to rapidly develop new
+plugins and extend existing functionality.
+
+Developing Listeners
+--------------------
+
+All listeners must implement just two methods: start() and stop(). Below is a
+sample listener template:
+
+
+ import logging
+
+ import sys
+
+ import threading
+ import socket
+
+ class MyListener():
+
+ def __init__(self, config, name = 'MyListener', logging_level = logging.INFO):
+ self.logger = logging.getLogger(name)
+ self.logger.setLevel(logging_level)
+
+ self.config = config
+ self.name = name
+
+ self.logger.info('Starting...')
+
+ self.logger.debug('Initialized with config:')
+ for key, value in config.iteritems():
+ self.logger.debug(' %10s: %s', key, value)
+
+ def start(self):
+
+ # Start listener
+ # ...
+
+ def stop(self):
+
+ # Stop listener
+ # ...
+
+The main listener class `MyListener()` will be provided with a parsed
+configuration dictionary containing information such as port to listen on,
+protocol, etc. The main listener class will also receive the current listener
+instance name and the logging info set by the user.
+
+The only requirement for listener implementation is that you use threading so
+that when FakeNet-NG calls the `start()` method, the listener will not block
+but spawn a new thread that handles incoming connections.
+
+Another requirement is to ensure that the listener can reliably shutdown when
+the `stop()` method is called. For example, make use of connection timeouts to
+ensure that the listener does not block on some connection for too long.
+
+The logging convention used by FakeNet-NG's listeners is to use the self.logger
+object for the output. That way the logging is uniform across the application.
+For example to display an error or warning you would use the following:
+
+ self.logger.error("This is an error")
+ self.logger.warning("This is a warning")
+
+Finally, after you finish developing the listener, copy it to the `listeners\`
+directory and append you module name to `__all__` varialbe in the `listeners\__init__`:
+
+ __all__ = ['RawListener', 'HTTPListener', 'DNSListener', 'SMTPListener', 'MyListener']
+
+At this point you can let the application use the newly created listener by
+adding it to the configuration file:
+
+ [MyAwesomeListener]
+ Enabled: True
+ Port: 1337
+ Protocol: TCP
+ Listener: MyListener
+
+Developing Diverters
+--------------------
+
+FakeNet-NG uses the open source WinDivert library in order to perform the
+traffic redirection on Windows Vista+ operating systems. The implementation of
+the windows diverter is located in `diverters\windows.py`.
+
+It is important to note that a lot of the Windows specific functionality is
+actually implemented in the `diverters\winutil.py` file using ctypes to call
+many of the Windows API functions directly.
+
+It is planned to expand the support for traffic divertion
+
+Building standalone executables
+-------------------------------
+
+If you would like to generate a stand-alone executable you would need to
+install the PyInstaller module:
+
+ pip install pyinstaller
+
+To generate the exe file run the pyinstaller as follows:
+
+ pyinstaller fakenet-ng.spec
+
+The standalone executable will be available in the `dist/` directory.
+
+Known Issues
+============
+
+Does not work on VMWare with host-only mode enabled
+---------------------------------------------------
+
+See "Not Intercepting Traffic" below.
+
+Not Intercepting Traffic
+------------------------
+
+In order to for FakeNet-NG to intercept and modify the packet, there must exist
+a valid network route for the packet to reach its destination.
+
+There is an easy way to check whether or not you have routes set up correctly.
+Without the tool running attempt to ping the destination host. You should
+observe either a valid response or a timeout message. If you receive a
+destination not reachable error instead, then you do not have a valid route.
+
+This is usually caused by your gateway being either not set or not reachable.
+For example, on a VMWare machine with host-only mode your machine will not have
+the gateway configured thus preventing FakeNet-NG from seeing any traffic.
+
+To correct this issue, manually configure your primary interface to the gateway
+in the same subnet. First check the interface name:
+
+ C:\>netsh interface show interface
+
+ Admin State State Type Interface Name
+ -------------------------------------------------------------------------
+ Enabled Connected Dedicated Local Area Connection
+
+In this case the interface name is "Local Area Connection" so we will use it for
+the rest of the commands.
+
+Manually configure the interface IP address and gateway as follows:
+
+ C:\>netsh interface ip set address name="Local Area Connection" static 192.168.249.123 255.255.255.0 192.168.249.254
+
+Manually set the DNS server IP address
+
+ C:\>netsh interface ip set dns name="Local Area Connection" static 4.2.2.2
+
+If you are still having issue ensure that the gateway IP address itself is
+routable.
+
+Error: Could not locate WinDivert DLL or one of its components
+--------------------------------------------------------------
+
+Please ensure that FakeNet-NG is extracted to the local C: drive to make
+sure the WinDivert driver is loaded correctly.
+
+Limitations
+===========
+
+* Only Windows Vista+ is supported. Please use the original Fakenet for
+ Windows XP/2003 operating systems.
+
+* Local machine only traffic is not intercepted (e.g. if you tried to connect
+ directly to one of the listeners).
+
+* Only traffic using TCP, UDP, and ICMP protocols are intercepted.
+
+Credits
+=======
+
+* FakeNet-NG was designed and developed by Peter Kacherginsky.
+* Special thanks to Andrew Honig, Michael Sikorski and others for the
+ original FakeNet which was the inspiration to develop this tool.
+
+Contact
+=======
+
+For bugs, crashes, or other comments please contact
+peter.kacherginsky@fireeye.com
-FakeNet-NG will be released during BlackHat 2016. Come visit our BlackHat
-Arsenal booth for a demo and a presentation of using FakeNet-NG for
-malware analysis and presentation at the Arsenal Theater on Wednesday,
-August 3rd.
diff --git a/fakenet.spec b/fakenet.spec
new file mode 100644
index 0000000..a6f9055
--- /dev/null
+++ b/fakenet.spec
@@ -0,0 +1,36 @@
+# -*- mode: python -*-
+
+block_cipher = None
+
+
+a = Analysis(['fakenet/fakenet.py'],
+ pathex=['fakenet'],
+ binaries=None,
+ datas=None,
+ hiddenimports=[],
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=[],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher)
+pyz = PYZ(a.pure, a.zipped_data,
+ cipher=block_cipher)
+exe = EXE(pyz,
+ a.scripts,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ icon='resources/fakenet.ico',
+ name='fakenet',
+ debug=False,
+ strip=False,
+ upx=True,
+ console=True,
+ uac_admin=True)
+
+coll = COLLECT(exe,
+ strip=False,
+ upx=True,
+ name='fakenet-dat'
+)
diff --git a/fakenet/__init__.py b/fakenet/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/fakenet/configs/burp.ini b/fakenet/configs/burp.ini
new file mode 100644
index 0000000..5826cfe
--- /dev/null
+++ b/fakenet/configs/burp.ini
@@ -0,0 +1,51 @@
+###############################################################################
+# Fakenet Configuration
+
+[FakeNet]
+
+DivertTraffic: Yes
+
+###############################################################################
+# Diverter Configuration
+
+[Diverter]
+
+DumpPackets: No
+DumpPacketsFilePrefix: packets
+
+ModifyLocalDNS: No
+StopDNSService: Yes
+
+RedirectAllTraffic: Yes
+DefaultTCPListener: ForwarderTCP
+DefaultUDPListener: ForwarderUDP
+
+BlackListPortsTCP: 139
+BlackListPortsUDP: 53, 67, 68, 137, 138, 1900, 5355
+
+# Ignore traffic generated by the java.exe process to let
+# Burp communicate out.
+ProcessBlackList: java.exe
+
+###############################################################################
+# Listener Configuration
+
+[ForwarderTCP]
+Enabled: True
+Port: 8080
+Protocol: TCP
+ProcessWhiteList: chrome.exe, firefox.exe, ncat.exe
+
+[ForwarderUDP]
+Enabled: True
+Port: 1234
+Protocol: UDP
+ProcessWhiteList: chrome.exe
+
+[DNS Server]
+Enabled: False
+Port: 53
+Protocol: UDP
+Listener: DNSListener
+DNSResponse: 127.0.0.1
+NXDomains: 0
\ No newline at end of file
diff --git a/fakenet/configs/debug.ini b/fakenet/configs/debug.ini
new file mode 100644
index 0000000..7375101
--- /dev/null
+++ b/fakenet/configs/debug.ini
@@ -0,0 +1,55 @@
+###############################################################################
+# Fakenet Configuration
+
+[FakeNet]
+
+DivertTraffic: Yes
+
+###############################################################################
+# Diverter Configuration
+
+[Diverter]
+
+DumpPackets: No
+DumpPacketsFilePrefix: packets
+
+ModifyLocalDNS: No
+StopDNSService: Yes
+
+RedirectAllTraffic: Yes
+DefaultTCPListener: RawTCPListener
+DefaultUDPListener: RawUDPListener
+
+BlackListPortsTCP: 139
+BlackListPortsUDP: 67, 68, 137, 138, 1900, 5355
+
+ProcessBlackList: windbg.exe
+
+###############################################################################
+# Listener Configuration
+
+[RawTCPListener]
+Enabled: True
+Port: 1337
+Protocol: TCP
+Listener: RawListener
+UseSSL: No
+Timeout: 300
+ProcessWhiteList: ncat.exe
+ExecuteCmd: C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe -p {pid}
+
+[RawUDPListener]
+Enabled: True
+Port: 1337
+Protocol: UDP
+Listener: RawListener
+UseSSL: No
+Timeout: 10
+
+[DNS Server]
+Enabled: Yes
+Port: 53
+Protocol: UDP
+Listener: DNSListener
+DNSResponse: 192.0.2.123
+NXDomains: 0
\ No newline at end of file
diff --git a/fakenet/configs/default.ini b/fakenet/configs/default.ini
new file mode 100644
index 0000000..a6b4ca9
--- /dev/null
+++ b/fakenet/configs/default.ini
@@ -0,0 +1,177 @@
+###############################################################################
+# Fakenet Configuration
+
+[FakeNet]
+
+# Specify whether or not FakeNet should divert traffic. Disable if you want to
+# just start listeners and direct traffic manually (e.g. modify DNS server)
+DivertTraffic: Yes
+
+###############################################################################
+# Diverter Configuration
+
+[Diverter]
+
+# Specify whether or not to save captured traffic. You can also change
+# the file prefix for the generated PCAPs.
+DumpPackets: Yes
+DumpPacketsFilePrefix: packets
+
+# Enable 'ModifyLocalDNS' to statically set DNS server to the local machine.
+ModifyLocalDNS: No
+
+# Enable 'StopDNSService' to stop Windows DNS client to see the actual
+# processes resolving domains.
+StopDNSService: Yes
+
+# Enable 'RedirectAllTraffic' to optionally divert traffic going to ports not
+# specifically listed in one of the listeners below. 'DefaultTCPListener' and
+# 'DefaultUDPListener' will handle TCP and UDP traffic going to unspecified ports.
+#
+# NOTE: Setting default UDP listener will intercept all DNS traffic unless you
+# enable a dedicated UDP port 53 DNS listener or add UDP port 53 to the
+# 'BlackListPortsUDP' below so that system's default DNS server is used instead.
+
+RedirectAllTraffic: Yes
+DefaultTCPListener: RawTCPListener
+DefaultUDPListener: RawUDPListener
+
+# Specify TCP and UDP ports to ignore when diverting packets.
+# For example, you may want to avoid diverting UDP port 53 (DNS) traffic
+# when trying to intercept a specific process while allowing the rest to
+# function normally
+#
+# NOTE: This setting is only honored when 'RedirectAllTraffic' is enabled.
+
+BlackListPortsTCP: 139
+BlackListPortsUDP: 67, 68, 137, 138, 1900, 5355
+
+# Specify processes to ignore when diverting traffic.
+# ProcessBlackList: java.exe
+
+# Specify hosts to ignore when diverting traffic.
+# HostBlackList: 6.6.6.6
+
+###############################################################################
+# Listener Configuration
+#
+# Listener configuration consists of generic settings used by the diverter which
+# are the same for all listeners and listener specific settings.
+#
+# NOTE: Listener section names will be used for logging.
+#
+# NOTE: Settings labels are not case-sensitive.
+#
+# The following settings are available for all listeners:
+# * Enabled - specify whether or not the listener is enabled.
+# * Port - TCP or UDP port to listen on.
+# * Protocol - TCP or UDP
+# * Listener - Listener name to handle traffic.
+# * ProcessWhiteList - Only traffic from these processes will be modified
+# and the rest will simply be forwarded.
+# * ProcessBlackList - Traffic from all but these processes will be simply forwarded
+# and the rest will be modified as needed.
+# * HostWhiteList - Only traffic to these hosts will be modified and
+# the rest will be simply forwarded.
+# * HostBlackList - Traffic to these hosts will be simply forwarded
+# and the rest will be modified as needed.
+# * ExecuteCmd - Execute command on the first connection packet. This is feature is useful
+# for extending FakeNet-NG's functionality (e.g. launch a debugger on the
+# connecting pid to help with unpacking and decoding.)
+#
+# The following format string variables are made available:
+# * {pid} - process id
+# * {procname} - process executable name
+# * {src_addr} - source address
+# * {src_port} - source port
+# * {dst_addr} - destination address
+# * {dst_port} - destination port
+#
+# Listener entry which does not specify a specific listener service
+# will still redirect all packets to the local machine on the specified port and
+# subject to all the filters (processes, hosts, etc.). However, you must set-up a
+# third party service (e.g. proxy servers) to accept these connections. This feature can be
+# used to provide FakeNet-NG's passive traffic diverting and filtering capabilities to other
+# applications.
+#
+# Listener specific settings:
+#
+# * Timeout - Set connection timeout for any listeners that support
+# TCP connections (e.g. RawListener, DNSListener, HTTPListener
+# SMTPListener).
+# * UseSSL - Enable SSL support on the listener (RawListener, HTTPListener)
+# * Webroot - Set webroot path for HTTPListener.
+# * DumpHTTPPosts - Store HTTP Post requests for the HTTPListener.
+# * DumpHTTPPostsFilePrefix - File prefix for the stored HTTP Post requests used by the HTTPListener.
+# * DNSResponse - IP address to respond with for A record DNS queries. (DNSListener)
+# * NXDomains - A number of DNS requests to ignore to let the malware cycle through
+# all of the backup C2 servers. (DNSListener)
+
+[Forwarder]
+Enabled: False
+Port: 8080
+Protocol: TCP
+ProcessWhiteList: chrome.exe
+
+[RawTCPListener]
+Enabled: True
+Port: 1337
+Protocol: TCP
+Listener: RawListener
+UseSSL: No
+Timeout: 10
+
+[RawUDPListener]
+Enabled: True
+Port: 1337
+Protocol: UDP
+Listener: RawListener
+UseSSL: No
+Timeout: 10
+
+[FilteredListener]
+Enabled: False
+Port: 31337
+Protocol: TCP
+Listener: RawListener
+UseSSL: No
+Timeout: 10
+ProcessWhiteList: ncat.exe, nc.exe
+HostBlackList: 5.5.5.5
+
+[DNS Server]
+Enabled: True
+Port: 53
+Protocol: UDP
+Listener: DNSListener
+DNSResponse: 192.0.2.123
+NXDomains: 0
+
+[HTTPListener80]
+Enabled: True
+Port: 80
+Protocol: TCP
+Listener: HTTPListener
+UseSSL: No
+Webroot: defaultFiles/
+Timeout: 10
+#ProcessBlackList: dmclient.exe, OneDrive.exe, svchost.exe, backgroundTaskHost.exe, GoogleUpdate.exe, chrome.exe
+DumpHTTPPosts: Yes
+DumpHTTPPostsFilePrefix: http
+
+[HTTPListener443]
+Enabled: True
+Port: 443
+Protocol: TCP
+Listener: HTTPListener
+UseSSL: Yes
+Webroot: defaultFiles/
+DumpHTTPPosts: Yes
+DumpHTTPPostsFilePrefix: http
+
+[SMTPListener]
+Enabled: True
+Port: 25
+Protocol: TCP
+Listener: SMTPListener
+UseSSL: No
\ No newline at end of file
diff --git a/fakenet/defaultFiles/FakeNet.gif b/fakenet/defaultFiles/FakeNet.gif
new file mode 100644
index 0000000..786bb11
Binary files /dev/null and b/fakenet/defaultFiles/FakeNet.gif differ
diff --git a/fakenet/defaultFiles/FakeNet.html b/fakenet/defaultFiles/FakeNet.html
new file mode 100644
index 0000000..8f327a0
--- /dev/null
+++ b/fakenet/defaultFiles/FakeNet.html
@@ -0,0 +1,37 @@
+
+
+FakeNet-NG
+
+
+
+
+ ______ _ ________ _ _ ______ _______ _ _ _____
+ | ____/\ | |/ / ____| \ | | ____|__ __| | \ | |/ ____|
+ | |__ / \ | ' /| |__ | \| | |__ | |______| \| | | __
+ | __/ /\ \ | < | __| | . ` | __| | |______| . ` | | |_ |
+ | | / ____ \| . \| |____| |\ | |____ | | | |\ | |__| |
+ |_|/_/ \_\_|\_\______|_| \_|______| |_| |_| \_|\_____|
+
+ H T T P L I S T E N E R
+
+
+FakeNet-NG is a next generation dynamic network analysis tool for malware
+analysts and penetration testers. It is open source and designed for the latest
+versions of Windows.
+
+The tool allows you to intercept and redirect all or specific network traffic
+while simulating legitimate network services. Using FakeNet-NG, malware analysts
+can quickly identify malware's functionality and capture network signatures.
+Penetration testers and bug hunters will find FakeNet-NG's configurable
+interception engine and modular framework highly useful when testing
+application's specific functionality and prototyping PoCs.
+
+FakeNet-NG is based on the excellent Fakenet tool developed
+by Andrew Honig and Michael Sikorski.
+
+Contact
+
+For bugs, crashes, or other comments please contact Peter Kacherginsky by email
+peter.kacherginsky@fireeye.com or Twitter @_iphelix.
+
+
\ No newline at end of file
diff --git a/fakenet/defaultFiles/FakeNet.ico b/fakenet/defaultFiles/FakeNet.ico
new file mode 100644
index 0000000..8431d83
Binary files /dev/null and b/fakenet/defaultFiles/FakeNet.ico differ
diff --git a/fakenet/defaultFiles/FakeNet.jpg b/fakenet/defaultFiles/FakeNet.jpg
new file mode 100644
index 0000000..f58ce1a
Binary files /dev/null and b/fakenet/defaultFiles/FakeNet.jpg differ
diff --git a/fakenet/defaultFiles/FakeNet.pdf b/fakenet/defaultFiles/FakeNet.pdf
new file mode 100644
index 0000000..9399b8c
Binary files /dev/null and b/fakenet/defaultFiles/FakeNet.pdf differ
diff --git a/fakenet/defaultFiles/FakeNet.png b/fakenet/defaultFiles/FakeNet.png
new file mode 100644
index 0000000..109479c
Binary files /dev/null and b/fakenet/defaultFiles/FakeNet.png differ
diff --git a/fakenet/defaultFiles/FakeNetMini.exe b/fakenet/defaultFiles/FakeNetMini.exe
new file mode 100644
index 0000000..94bb4aa
Binary files /dev/null and b/fakenet/defaultFiles/FakeNetMini.exe differ
diff --git a/fakenet/diverters/__init__.py b/fakenet/diverters/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/fakenet/diverters/__init__.pyc b/fakenet/diverters/__init__.pyc
new file mode 100644
index 0000000..ca7d9ab
Binary files /dev/null and b/fakenet/diverters/__init__.pyc differ
diff --git a/fakenet/diverters/windows.py b/fakenet/diverters/windows.py
new file mode 100644
index 0000000..16e5f3d
--- /dev/null
+++ b/fakenet/diverters/windows.py
@@ -0,0 +1,663 @@
+# Diverter for Windows implemented using WinDivert library
+
+import logging
+
+from pydivert.windivert import *
+from pydivert.enum import Direction, Defaults
+
+import socket
+
+import os
+
+import dpkt
+
+import time
+import threading
+
+import platform
+
+from winutil import *
+
+import subprocess
+
+class Diverter(WinUtilMixin):
+
+ def __init__(self, diverter_config, listeners_config, logging_level = logging.INFO):
+
+ self.logger = logging.getLogger('Diverter')
+ self.logger.setLevel(logging_level)
+
+ self.diverter_config = diverter_config
+ self.listeners_config = listeners_config
+
+ # Local IP address
+ self.external_ip = socket.gethostbyname(socket.gethostname())
+ self.loopback_ip = socket.gethostbyname('localhost')
+
+ # Sessions cache
+ # NOTE: A dictionary of source ports mapped to destination address, port tuples
+ self.sessions = dict()
+
+ #######################################################################
+ # Listener specific configuration
+ # NOTE: All of these definitions have protocol as the first key
+ # followed by a list or another nested dict with the actual definitions
+
+ # Diverted ports
+ self.diverted_ports = dict()
+
+ # Listener Port Process filtering
+ # TODO: Allow PIDs
+ self.port_process_whitelist = dict()
+ self.port_process_blacklist = dict()
+
+ # Listener Port Host filtering
+ # TODO: Allow domain name resolution
+ self.port_host_whitelist = dict()
+ self.port_host_blacklist = dict()
+
+ # Execute command list
+ self.port_execute = dict()
+
+ # Parse listener configurations
+ self.parse_listeners_config(listeners_config)
+
+ #######################################################################
+ # Diverter settings and filters
+
+ # Intercept filter
+ # NOTE: All relevant connections are recorded as outbound by WinDivert
+ # so additional filtering based on destination port will need to be
+ # performed in order to determine the correct traffic direction.
+ self.filter = None
+
+ # Default TCP/UDP listeners
+ self.default_listener_tcp_port = None
+ self.default_listener_udp_port = None
+
+ # Global TCP/UDP port blacklist
+ self.blacklist_ports_tcp = []
+ self.blacklist_ports_udp = []
+
+ # Global process blacklist
+ # TODO: Allow PIDs
+ self.blacklist_processes = []
+
+ # Global host blacklist
+ # TODO: Allow domain resolution
+ self.blacklist_hosts = []
+
+ # Parse diverter config
+ self.parse_diverter_config()
+
+ #######################################################################
+ # Network verification
+
+ # Check active interfaces
+ if not self.check_active_ethernet_adapters():
+ self.logger.warning('WARNING: No active ethernet interfaces detected!')
+ self.logger.warning(' Please enable a network interface.')
+
+ # Check configured gateways
+ if not self.check_gateways():
+ self.logger.warning('WARNING: No gateways configured!')
+ self.logger.warning(' Please configure a default gateway or route in order to intercept external traffic.')
+
+ # Check configured DNS servers
+ if not self.check_dns_servers():
+ self.logger.warning('WARNING: No DNS servers configured!')
+ self.logger.warning(' Please configure a DNS server in order to allow network resolution.')
+
+ #######################################################################
+ # Initialize WinDivert
+
+ # Locate the WinDivert driver
+ # NOTE: This is necessary to work in scenarios where the applications is
+ # executed as a python script, installed as an egg or with the pyinstaller
+
+ dll_arch = "64" if platform.machine() == 'AMD64' else "32"
+
+ dll_path = os.path.join('lib', dll_arch, 'WinDivert.dll')
+
+ if not os.path.exists(dll_path):
+
+ dll_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'lib', dll_arch, 'WinDivert.dll')
+
+ if not os.path.exists(dll_path):
+
+ self.logger.error('Could not open bundled WinDivert.dll')
+ sys.exit(1)
+
+ # Divert handle
+ driver = None
+
+ driver = WinDivert(dll_path = dll_path)
+
+ try:
+ self.handle = Handle(driver, filter=self.filter)
+ self.handle.open()
+ except WindowsError, e:
+ if e.winerror == 5:
+ self.logger.error('ERROR: Insufficient privileges to run windows diverter.')
+ self.logger.error(' Please restart with Administrator privileges.')
+ sys.exit(1)
+ elif e.winerror == 3:
+ self.logger.error('ERROR: Could not locate WinDivert DLL or one of its components.')
+ self.logger.error(' Please make sure you have copied FakeNet-NG to the C: drive.')
+ sys.exit(1)
+ else:
+ self.logger.error('ERROR: Failed to open a handle to the WinDivert driver: %s', e)
+ sys.exit(1)
+
+ # Capture packets configuration
+ self.capture_flag = False
+ self.dump_packets_file_prefix = "packets"
+ self.pcap = None
+
+ if self.diverter_config.get('dumppackets') and self.diverter_config['dumppackets'].lower() == 'yes':
+ self.capture_flag = True
+ pcap_filename = "%s_%s.pcap" % (diverter_config.get('dumppacketsfileprefix', 'packets'), time.strftime("%Y%m%d_%H%M%S"))
+
+ self.logger.info('Capturing traffic to %s', pcap_filename)
+ self.pcap = dpkt.pcap.Writer(open(pcap_filename, 'wb'), linktype=dpkt.pcap.DLT_RAW)
+
+ ###########################################################################
+ # Parse listener specific settings and filters
+
+ def parse_listeners_config(self, listeners_config):
+
+ #######################################################################
+ # Populate diverter ports and process filters from the configuration
+ for listener_name, listener_config in listeners_config.iteritems():
+
+ if 'port' in listener_config:
+
+ port = int(listener_config['port'])
+
+ if not 'protocol' in listener_config:
+ self.logger.error('ERROR: Protocol not defined for listener %s', listener_name)
+ sys.exit(1)
+
+ protocol = listener_config['protocol'].upper()
+
+ if not protocol in ['TCP', 'UDP']:
+ self.logger.error('ERROR: Invalid protocol %s for listener %s', protocol, listener_name)
+ sys.exit(1)
+
+ if not protocol in self.diverted_ports:
+ self.diverted_ports[protocol] = list()
+
+ self.diverted_ports[protocol].append(port)
+
+ ###############################################################
+ # Process filtering configuration
+ if 'processwhitelist' in listener_config and 'processblacklist' in listener_config:
+ self.logger.error('ERROR: Listener can\'t have both process whitelist and blacklist.')
+ sys.exit(1)
+
+ elif 'processwhitelist' in listener_config:
+
+ self.logger.debug('Process whitelist:')
+
+ if not protocol in self.port_process_whitelist:
+ self.port_process_whitelist[protocol] = dict()
+
+ self.port_process_whitelist[protocol][port] = [process.strip() for process in listener_config['processwhitelist'].split(',')]
+
+ for port in self.port_process_whitelist[protocol]:
+ self.logger.debug(' Port: %d (%s) Processes: %s', port, protocol, ', '.join(self.port_process_whitelist[protocol][port]))
+
+ elif 'processblacklist' in listener_config:
+ self.logger.debug('Process blacklist:')
+
+ if not protocol in self.port_process_blacklist:
+ self.port_process_blacklist[protocol] = dict()
+
+ self.port_process_blacklist[protocol][port] = [process.strip() for process in listener_config['processblacklist'].split(',')]
+
+ for port in self.port_process_blacklist[protocol]:
+ self.logger.debug(' Port: %d (%s) Processes: %s', port, protocol, ', '.join(self.port_process_blacklist[protocol][port]))
+
+ ###############################################################
+ # Host filtering configuration
+ if 'hostwhitelist' in listener_config and 'hostblacklist' in listener_config:
+ self.logger.error('ERROR: Listener can\'t have both host whitelist and blacklist.')
+ sys.exit(1)
+
+ elif 'hostwhitelist' in listener_config:
+
+ self.logger.debug('Host whitelist:')
+
+ if not protocol in self.port_host_whitelist:
+ self.port_host_whitelist[protocol] = dict()
+
+ self.port_host_whitelist[protocol][port] = [host.strip() for host in listener_config['hostwhitelist'].split(',')]
+
+ for port in self.port_host_whitelist[protocol]:
+ self.logger.debug(' Port: %d (%s) Hosts: %s', port, protocol, ', '.join(self.port_host_whitelist[protocol][port]))
+
+ elif 'hostblacklist' in listener_config:
+ self.logger.debug('Host blacklist:')
+
+ if not protocol in self.port_host_blacklist:
+ self.port_host_blacklist[protocol] = dict()
+
+ self.port_host_blacklist[protocol][port] = [host.strip() for host in listener_config['hostblacklist'].split(',')]
+
+ for port in self.port_host_blacklist[protocol]:
+ self.logger.debug(' Port: %d (%s) Hosts: %s', port, protocol, ', '.join(self.port_host_blacklist[protocol][port]))
+
+ ###############################################################
+ # Execute command configuration
+ if 'executecmd' in listener_config:
+
+ if not protocol in self.port_execute:
+ self.port_execute[protocol] = dict()
+
+ self.port_execute[protocol][port] = listener_config['executecmd'].strip()
+ self.logger.debug('Port %d (%s) ExecuteCmd: %s', port, protocol, self.port_execute[protocol][port] )
+
+ ###########################################################################
+ # Parse diverter settings and filters
+
+ def parse_diverter_config(self):
+
+ # Do not redirect blacklisted processes
+ if self.diverter_config.get('processblacklist') != None:
+ self.blacklist_processes = [process.strip() for process in self.diverter_config.get('processblacklist').split(',')]
+ self.logger.debug('Blacklisted processes: %s', ', '.join([str(p) for p in self.blacklist_processes]))
+
+ # Do not redirect blacklisted hosts
+ if self.diverter_config.get('hostblacklist') != None:
+ self.blacklist_hosts = [host.strip() for host in self.diverter_config.get('hostblacklist').split(',')]
+ self.logger.debug('Blacklisted hosts: %s', ', '.join([str(p) for p in self.blacklist_hosts]))
+
+ # Redirect all traffic
+ if self.diverter_config.get('redirectalltraffic') and self.diverter_config['redirectalltraffic'].lower() == 'yes':
+ self.filter = "outbound and ip and (icmp or tcp or udp)"
+
+ if self.diverter_config.get('defaulttcplistener') == None:
+ self.logger.error('ERROR: No default TCP listener specified in the configuration.')
+ sys.exit(1)
+
+ elif self.diverter_config.get('defaultudplistener') == None:
+ self.logger.error('ERROR: No default UDP listener specified in the configuration.')
+ sys.exit(1)
+
+ elif not self.diverter_config.get('defaulttcplistener') in self.listeners_config:
+ self.logger.error('ERROR: No configuration exists for default TCP listener %s', self.diverter_config.get('defaulttcplistener'))
+ sys.exit(1)
+
+ elif not self.diverter_config.get('defaultudplistener') in self.listeners_config:
+ self.logger.error('ERROR: No configuration exists for default UDP listener %s', self.diverter_config.get('defaultudplistener'))
+ sys.exit(1)
+
+ else:
+ self.default_listener_tcp_port = int( self.listeners_config[ self.diverter_config['defaulttcplistener'] ]['port'] )
+ self.logger.error('Using default listener %s on port %d', self.diverter_config['defaulttcplistener'], self.default_listener_tcp_port)
+
+ self.default_listener_udp_port = int( self.listeners_config[ self.diverter_config['defaultudplistener'] ]['port'] )
+ self.logger.error('Using default listener %s on port %d', self.diverter_config['defaultudplistener'], self.default_listener_udp_port)
+
+ # Do not redirect blacklisted TCP ports
+ if self.diverter_config.get('blacklistportstcp') != None:
+ self.blacklist_ports_tcp = [int(port.strip()) for port in self.diverter_config.get('blacklistportstcp').split(',')]
+ self.logger.debug('Blacklisted TCP ports: %s', ', '.join([str(p) for p in self.blacklist_ports_tcp]))
+
+ # Do not redirect blacklisted UDP ports
+ if self.diverter_config.get('blacklistportsudp') != None:
+ self.blacklist_ports_udp = [int(port.strip()) for port in self.diverter_config.get('blacklistportsudp').split(',')]
+ self.logger.debug('Blacklisted UDP ports: %s', ', '.join([str(p) for p in self.blacklist_ports_udp]))
+
+ # Redirect only specific traffic, build the filter dynamically
+ else:
+
+ filter_diverted_ports = list()
+
+ if self.diverted_ports.get('TCP') != None:
+ for tcp_port in self.diverted_ports.get('TCP'):
+ filter_diverted_ports.append("tcp.DstPort == %s" % tcp_port)
+ filter_diverted_ports.append("tcp.SrcPort == %s" % tcp_port)
+
+ if self.diverted_ports.get('UDP') != None:
+ for udp_port in self.diverted_ports.get('UDP'):
+ filter_diverted_ports.append("udp.DstPort == %s" % udp_port)
+ filter_diverted_ports.append("udp.SrcPort == %s" % udp_port)
+
+ if len(filter_diverted_ports) > 0:
+ self.filter = "outbound and ip and (icmp or %s)" % " or ".join(filter_diverted_ports)
+ else:
+ self.filter = "outbound and ip"
+
+ ###########################################################################
+ # Diverter controller functions
+
+ def start(self):
+
+ self.logger.info('Starting...')
+
+ # Set local DNS server IP address
+ if self.diverter_config.get('modifylocaldns') and self.diverter_config['modifylocaldns'].lower() == 'yes':
+ self.set_dns_server(self.loopback_ip)
+
+ # Stop DNS service
+ if self.diverter_config.get('stopdnsservice') and self.diverter_config['stopdnsservice'].lower() == 'yes':
+ self.stop_service_helper('Dnscache')
+
+ self.logger.info('Diverting ports: ')
+ if self.diverted_ports.get('TCP'): self.logger.info('TCP: %s', ', '.join("%d" % port for port in self.diverted_ports['TCP']))
+ if self.diverted_ports.get('UDP'): self.logger.info('UDP: %s', ', '.join("%d" % port for port in self.diverted_ports['UDP']))
+
+ self.flush_dns()
+
+ self.diverter_thread = threading.Thread(target=self.divert_thread)
+ self.diverter_thread.daemon = True
+
+ self.diverter_thread.start()
+
+
+
+ def divert_thread(self):
+
+ try:
+ while True:
+ packet = self.handle.receive()
+ self.handle_packet(packet)
+
+ # Handle errors related to shutdown process.
+ except WindowsError as e:
+ if e.winerror in [4,6,995]:
+ return
+ else:
+ raise
+
+ def stop(self):
+ self.logger.info('Stopping...')
+ if self.pcap:
+ self.pcap.close()
+
+ self.handle.close()
+
+ # Restore DNS server
+ if self.diverter_config.get('modifylocaldns') and self.diverter_config['modifylocaldns'].lower() == 'yes':
+ self.restore_dns_server()
+
+ # Restart DNS service
+ if self.diverter_config.get('stopdnsservice') and self.diverter_config['stopdnsservice'].lower() == 'yes':
+ self.start_service_helper('Dnscache')
+
+ self.flush_dns()
+
+
+ def handle_icmp_packet(self, packet):
+ # Modify outgoing ICMP packet to target local Windows host which will reply to the ICMP messages.
+ # HACK: Can't intercept inbound ICMP server, but still works for now.
+
+ if not ((packet.meta.is_loopback() and packet.src_addr == self.loopback_ip and packet.dst_addr == self.loopback_ip) or \
+ (packet.src_addr == self.external_ip and packet.dst_addr == self.external_ip)):
+
+ self.logger.info('Modifying %s ICMP packet:', 'loopback' if packet.meta.is_loopback() else 'external')
+ self.logger.info(' from: %s -> %s', packet.src_addr, packet.dst_addr)
+
+ # Direct packet to the right interface IP address to avoid routing issues
+ packet.dst_addr = self.loopback_ip if packet.meta.is_loopback() else self.external_ip
+
+ self.logger.info(' to: %s -> %s', packet.src_addr, packet.dst_addr)
+
+ return packet
+
+ def handle_tcp_udp_packet(self, packet, protocol, default_listener_port, blacklist_ports):
+
+ # Meta strings
+ interface_string = 'loopback' if packet.meta.is_loopback() else 'external'
+ direction_string = 'inbound' if packet.meta.is_inbound() else 'outbound'
+
+ # Protocol specific filters
+ diverted_ports = self.diverted_ports.get(protocol)
+ port_process_whitelist = self.port_process_whitelist.get(protocol)
+ port_process_blacklist = self.port_process_blacklist.get(protocol)
+ port_host_whitelist = self.port_host_whitelist.get(protocol)
+ port_host_blacklist = self.port_host_blacklist.get(protocol)
+ port_execute = self.port_execute.get(protocol)
+
+ if packet.src_port in blacklist_ports or packet.dst_port in blacklist_ports:
+ self.logger.debug('Forwarding blacklisted port %s %s %s packet:', direction_string, interface_string, protocol)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Check if a packet must be diverted to a local listener
+ # Rules:
+ # 1) Divert outbound packets only
+ # 2) Make sure we are not diverting response packet based on the source port
+ # 3) Make sure the destination port is a known diverted port or we have a default listener port defined
+ elif diverted_ports and (packet.dst_port in diverted_ports or default_listener_port != None) and not packet.src_port in diverted_ports:
+
+ # Find which process ID is sending the request
+ conn_pid = self.get_pid_port_tcp(packet.src_port) if type(packet.headers[1].hdr) == TcpHeader else self.get_pid_port_udp(packet.src_port)
+ process_name = self.get_process_image_filename(conn_pid) if conn_pid else None
+
+ # Check process blacklist
+ if process_name and process_name in self.blacklist_processes:
+ self.logger.debug('Ignoring %s %s %s request packet from process %s in the process blacklist.', direction_string, interface_string, protocol, process_name)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Check host blacklist
+ if packet.dst_addr in self.blacklist_hosts:
+ self.logger.debug('Ignoring %s %s %s request packet to %s in the host blacklist.', direction_string, interface_string, protocol, packet.dst_addr)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Check the port process whitelist
+ elif process_name and port_process_whitelist and \
+ ((packet.dst_port in port_process_whitelist and not process_name in port_process_whitelist[packet.dst_port]) or\
+ (default_listener_port and default_listener_port in port_process_whitelist and not process_name in port_process_whitelist[default_listener_port])) :
+ self.logger.debug('Ignoring %s %s %s request packet from process %s not in the listener process whitelist.', direction_string, interface_string, protocol, process_name)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Check the port process blacklist
+ elif process_name and port_process_blacklist and \
+ ((packet.dst_port in port_process_blacklist and process_name in port_process_blacklist[packet.dst_port]) or\
+ (default_listener_port and default_listener_port in port_process_blacklist and process_name in port_process_blacklist[default_listener_port])) :
+ self.logger.debug('Ignoring %s %s %s request packet from process %s in the listener process blacklist.', direction_string, interface_string, protocol, process_name)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Check the port host whitelist
+ elif packet.dst_addr and port_host_whitelist and \
+ ((packet.dst_port in port_host_whitelist and not packet.dst_addr in port_host_whitelist[packet.dst_port]) or\
+ (default_listener_port and default_listener_port in port_host_whitelist and not packet.dst_addr in port_host_whitelist[default_listener_port])) :
+ self.logger.debug('Ignoring %s %s %s request packet to %s not in the listener host whitelist.', direction_string, interface_string, protocol, packet.dst_addr)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Check the port host blacklist
+ elif packet.dst_addr and port_host_blacklist and \
+ ((packet.dst_port in port_host_blacklist and packet.dst_addr in port_host_blacklist[packet.dst_port]) or\
+ (default_listener_port and default_listener_port in port_host_blacklist and packet.dst_addr in port_host_blacklist[default_listener_port])) :
+ self.logger.debug('Ignoring %s %s %s request packet to %s in the listener host blacklist.', direction_string, interface_string, protocol, packet.dst_addr)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Make sure you are not intercepting packets from one of the FakeNet listeners
+ elif conn_pid and os.getpid() == conn_pid:
+ self.logger.debug('Skipping %s %s %s listener packet:', direction_string, interface_string, protocol)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Modify the packet
+ else:
+
+ # Adjustable log level output. Used to display info level logs for first packets of the session and
+ # debug level for the rest of the communication in order to reduce log output.
+ logger_level = self.logger.debug
+
+ # First packet in a new session
+ if not (packet.src_port in self.sessions and self.sessions[packet.src_port] == (packet.dst_addr, packet.dst_port)):
+
+ # Cache original target IP address based on source port
+ self.sessions[packet.src_port] = (packet.dst_addr, packet.dst_port)
+
+ # Override log level to display all information on info level
+ logger_level = self.logger.info
+
+ # Execute command
+ if conn_pid and port_execute and (packet.dst_port in port_execute or (default_listener_port and default_listener_port in port_execute)):
+
+
+ execute_cmd = port_execute[packet.dst_port if packet.dst_port in diverted_ports else default_listener_port].format(pid = conn_pid,
+ procname = process_name,
+ src_addr = packet.src_addr,
+ src_port = packet.src_port,
+ dst_addr = packet.dst_addr,
+ dst_port = packet.dst_port)
+
+ logger_level('Executing command: %s', execute_cmd)
+
+ self.execute_detached(execute_cmd)
+
+
+ logger_level('Modifying %s %s %s request packet:', direction_string, interface_string, protocol)
+ logger_level(' from: %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Direct packet to the right interface IP address to avoid routing issues
+ packet.dst_addr = self.loopback_ip if packet.meta.is_loopback() else self.external_ip
+
+ # Direct packet to an existing or a default listener
+ packet.dst_port = packet.dst_port if packet.dst_port in diverted_ports else default_listener_port
+
+ logger_level(' to: %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ if conn_pid:
+ logger_level(' pid: %d name: %s', conn_pid, process_name if process_name else 'Unknown')
+
+
+ # Restore diverted response from a local listener
+ # NOTE: The response can come from a legitimate request
+ elif diverted_ports and packet.src_port in diverted_ports:
+
+ # Find which process ID is sending the request
+ conn_pid = self.get_pid_port_tcp(packet.dst_port) if type(packet.headers[1].hdr) == TcpHeader else self.get_pid_port_udp(packet.dst_port)
+ process_name = self.get_process_image_filename(conn_pid)
+
+ if not packet.dst_port in self.sessions:
+ self.logger.debug('Unknown %s %s %s response packet:', direction_string, interface_string, protocol)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Restore original target IP address from the cache
+ else:
+ self.logger.debug('Modifying %s %s %s response packet:', direction_string, interface_string, protocol)
+ self.logger.debug(' from: %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ # Restore original target IP address based on destination port
+ packet.src_addr, packet.src_port = self.sessions[packet.dst_port]
+
+ self.logger.debug(' to: %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ if conn_pid:
+ self.logger.debug(' pid: %d name: %s', conn_pid, process_name if process_name else 'Unknown')
+
+ else:
+ self.logger.debug('Forwarding %s %s %s packet:', direction_string, interface_string, protocol)
+ self.logger.debug(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+
+ return packet
+
+ def handle_packet(self, packet):
+
+ if packet == None:
+ self.logger.error('ERROR: Can\'t handle packet.')
+ return
+
+ # Preserve destination address to detect packet being diverted
+ dst_addr = packet.dst_addr
+
+ #######################################################################
+ # Capture packet and store raw packet in the PCAP
+ if self.capture_flag:
+ self.pcap.writepkt(packet._raw_packet)
+
+ ###########################################################################
+ # Verify the IP packet has an additional header
+
+ if len(packet.headers) > 1 and packet.headers[1] and packet.headers[1].hdr:
+
+ #######################################################################
+ # Handle ICMP Packets
+
+ if type(packet.headers[1].hdr) in [IcmpHeader, Icmpv6Header]:
+ packet = self.handle_icmp_packet(packet)
+
+ #######################################################################
+ # Handle TCP/UDP Packets
+
+ elif type(packet.headers[1].hdr) == TcpHeader:
+ protocol = 'TCP'
+ packet = self.handle_tcp_udp_packet(packet,
+ protocol,
+ self.default_listener_tcp_port,
+ self.blacklist_ports_tcp)
+
+ elif type(packet.headers[1].hdr) == UdpHeader:
+ protocol = 'UDP'
+ packet = self.handle_tcp_udp_packet(packet,
+ protocol,
+ self.default_listener_udp_port,
+ self.blacklist_ports_udp)
+
+ else:
+ self.logger.error('ERROR: Unknown packet header type.')
+
+ #######################################################################
+ # Capture modified packet and store raw packet in the PCAP
+ # NOTE: While this results in potentially duplicate traffic capture, this is necessary
+ # to properly restore TLS/SSL sessions.
+ # TODO: Develop logic to record traffic before modification for both requests and
+ # responses to reduce duplicate captures.
+ if self.capture_flag and (dst_addr != packet.dst_addr):
+ self.pcap.writepkt(packet._raw_packet)
+
+ #######################################################################
+ # Attempt to send the processed packet
+ try:
+ self.handle.send(packet)
+ except Exception, e:
+
+ protocol = 'Unknown'
+
+ if type(packet.headers[1].hdr) == TcpHeader:
+ protocol = 'TCP'
+ elif type(packet.headers[1].hdr) == UdpHeader:
+ protocol = 'UDP'
+ elif type(packet.headers[1].hdr) in [IcmpHeader, Icmpv6Header]:
+ protocol = 'ICMP'
+
+ interface_string = 'loopback' if packet.meta.is_loopback() else 'external'
+ direction_string = 'inbound' if packet.meta.is_inbound() else 'outbound'
+
+ self.logger.error('ERROR: Failed to send %s %s %s packet', direction_string, interface_string, protocol)
+
+ if packet.src_port and packet.dst_port:
+ self.logger.error(' %s:%d -> %s:%d', packet.src_addr, packet.src_port, packet.dst_addr, packet.dst_port)
+ else:
+ self.logger.error(' %s -> %s', packet.src_addr, packet.dst_addr)
+
+ self.logger.error(' %s', e)
+
+def main():
+
+ self.diverter_config = {'redirectalltraffic': 'no', 'defaultlistener': 'DefaultListener', 'dumppackets': 'no'}
+ listeners_config = {'DefaultListener': {'port': '1337'}}
+
+ diverter = Diverter(diverter_config, listeners_config)
+ diverter.start()
+
+ ###########################################################################
+ # Run processing
+ import time
+
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ diverter.stop()
+
+ ###########################################################################
+ # Run tests
+ # TODO
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/fakenet/diverters/windows.pyc b/fakenet/diverters/windows.pyc
new file mode 100644
index 0000000..3ccb931
Binary files /dev/null and b/fakenet/diverters/windows.pyc differ
diff --git a/fakenet/diverters/winutil.py b/fakenet/diverters/winutil.py
new file mode 100644
index 0000000..c8f26a7
--- /dev/null
+++ b/fakenet/diverters/winutil.py
@@ -0,0 +1,1195 @@
+#!/usr/bin/env python
+import logging
+logging.basicConfig(format='%(asctime)s [%(name)18s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.DEBUG)
+
+from ctypes import *
+from ctypes.wintypes import *
+
+import os
+import sys
+import socket
+import struct
+
+import time
+
+from _winreg import *
+
+import subprocess
+
+NO_ERROR = 0
+
+AF_INET = 2
+AF_INET6 = 23
+
+ULONG64 = c_uint64
+
+
+##############################################################################
+# Services related functions
+##############################################################################
+
+SC_MANAGER_ALL_ACCESS = 0xF003F
+
+SERVICE_ALL_ACCESS = 0xF01FF
+SERVICE_STOP = 0x0020
+SERVICE_QUERY_STATUS = 0x0004
+SERVICE_ENUMERATE_DEPENDENTS = 0x0008
+
+SC_STATUS_PROCESS_INFO = 0x0
+
+SERVICE_STOPPED = 0x1
+SERVICE_START_PENDING = 0x2
+SERVICE_STOP_PENDING = 0x3
+SERVICE_RUNNING = 0x4
+SERVICE_CONTINUE_PENDING = 0x5
+SERVICE_PAUSE_PENDING = 0x6
+SERVICE_PAUSED = 0x7
+
+SERVICE_CONTROL_STOP = 0x1
+SERVICE_CONTROL_PAUSE = 0x2
+SERVICE_CONTROL_CONTINUE = 0x3
+
+SERVICE_NO_CHANGE = 0xffffffff
+
+SERVICE_AUTO_START = 0x2
+SERVICE_BOOT_START = 0x0
+SERVICE_DEMAND_START = 0x3
+SERVICE_DISABLED = 0x4
+SERVICE_SYSTEM_START = 0x1
+
+class SERVICE_STATUS_PROCESS(Structure):
+ _fields_ = [
+ ("dwServiceType", DWORD),
+ ("dwCurrentState", DWORD),
+ ("dwControlsAccepted", DWORD),
+ ("dwWin32ExitCode", DWORD),
+ ("dwServiceSpecificExitCode", DWORD),
+ ("dwCheckPoint", DWORD),
+ ("dwWaitHint", DWORD),
+ ("dwProcessId", DWORD),
+ ("dwServiceFlags", DWORD),
+ ]
+
+##############################################################################
+# Process related functions
+##############################################################################
+
+##############################################################################
+# GetExtendedTcpTable constants and structures
+
+TCP_TABLE_OWNER_PID_ALL = 5
+
+class MIB_TCPROW_OWNER_PID(Structure):
+ _fields_ = [
+ ("dwState", DWORD),
+ ("dwLocalAddr", DWORD),
+ ("dwLocalPort", DWORD),
+ ("dwRemoteAddr", DWORD),
+ ("dwRemotePort", DWORD),
+ ("dwOwningPid", DWORD)
+ ]
+
+class MIB_TCPTABLE_OWNER_PID(Structure):
+ _fields_ = [
+ ("dwNumEntries", DWORD),
+ ("table", MIB_TCPROW_OWNER_PID * 512)
+ ]
+
+##############################################################################
+# GetExtendedUdpTable constants and structures
+
+UDP_TABLE_OWNER_PID = 1
+
+class MIB_UDPROW_OWNER_PID(Structure):
+ _fields_ = [
+ ("dwLocalAddr", DWORD),
+ ("dwLocalPort", DWORD),
+ ("dwOwningPid", DWORD)
+ ]
+
+class MIB_UDPTABLE_OWNER_PID(Structure):
+ _fields_ = [
+ ("dwNumEntries", DWORD),
+ ("table", MIB_UDPROW_OWNER_PID * 512)
+ ]
+
+###############################################################################
+# GetProcessImageFileName constants and structures
+
+MAX_PATH = 260
+PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
+
+
+###############################################################################
+# Network interface related functions
+###############################################################################
+
+MIB_IF_TYPE_ETHERNET = 6
+MIB_IF_TYPE_LOOPBACK = 28
+IF_TYPE_IEEE80211 = 71
+
+###############################################################################
+# GetAdaptersAddresses constants and structures
+
+MAX_ADAPTER_ADDRESS_LENGTH = 8
+MAX_DHCPV6_DUID_LENGTH = 130
+
+IFOPERSTATUSUP = 1
+
+class SOCKADDR(Structure):
+ _fields_ = [
+ ("sa_family", c_ushort),
+ ("sa_data", c_char * 14),
+ ]
+
+class SOCKET_ADDRESS(Structure):
+ _fields_ = [
+ ("Sockaddr", POINTER(SOCKADDR)),
+ ("SockaddrLength", INT),
+ ]
+
+class IP_ADAPTER_PREFIX(Structure):
+ pass
+IP_ADAPTER_PREFIX._fields_ = [
+ ("Length", ULONG),
+ ("Flags", DWORD),
+ ("Next", POINTER(IP_ADAPTER_PREFIX)),
+ ("Address", SOCKET_ADDRESS),
+ ("PrefixLength", ULONG),
+ ]
+
+class IP_ADAPTER_ADDRESSES(Structure):
+ pass
+IP_ADAPTER_ADDRESSES._fields_ = [
+ ("Length", ULONG),
+ ("IfIndex", DWORD),
+ ("Next", POINTER(IP_ADAPTER_ADDRESSES)),
+ ("AdapterName", LPSTR),
+ ("FirstUnicastAddress", c_void_p), # Not used
+ ("FirstAnycastAddress", c_void_p), # Not used
+ ("FirstMulticastAddress", c_void_p), # Not used
+ ("FirstDnsServerAddress", c_void_p), # Not used
+ ("DnsSuffix", LPWSTR),
+ ("Description", LPWSTR),
+ ("FriendlyName", LPWSTR),
+ ("PhysicalAddress", BYTE * MAX_ADAPTER_ADDRESS_LENGTH),
+ ("PhysicalAddressLength", DWORD),
+ ("Flags", DWORD),
+ ("Mtu", DWORD),
+ ("IfType", DWORD),
+ ("OperStatus", DWORD),
+ ("Ipv6IfIndex", DWORD),
+ ("ZoneIndices", DWORD * 16),
+ ("FirstPrefix", POINTER(IP_ADAPTER_PREFIX)),
+ ("TransmitLinkSpeed", ULONG64),
+ ("ReceiveLinkSpeed", ULONG64),
+ ("FirstWinsServerAddress", c_void_p), # Not used
+ ("FirstGatewayAddress", c_void_p), # Not used
+ ("Ipv4Metric", ULONG),
+ ("Ipv6Metric", ULONG),
+ ("Luid", ULONG64),
+ ("Dhcpv4Server", SOCKET_ADDRESS),
+ ("CompartmentId", DWORD),
+ ("NetworkGuid", BYTE * 16),
+ ("ConnectionType", DWORD),
+ ("TunnelType", DWORD),
+ ("Dhcpv6Server", SOCKET_ADDRESS),
+ ("Dhcpv6ClientDuid", BYTE * MAX_DHCPV6_DUID_LENGTH),
+ ("Dhcpv6ClientDuidLength", ULONG),
+ ("Dhcpv6Iaid", ULONG),
+ ("FirstDnsSuffix", c_void_p), # Not used
+]
+
+###############################################################################
+# GetAdaptersInfo constants and structures
+
+MAX_ADAPTER_NAME_LENGTH = 256
+MAX_ADAPTER_DESCRIPTION_LENGTH = 128
+MAX_ADAPTER_LENGTH = 8
+
+MIB_IF_TYPE_ETHERNET = 6
+MIB_IF_TYPE_LOOPBACK = 28
+IF_TYPE_IEEE80211 = 71
+
+class IP_ADDRESS_STRING(Structure):
+ _fields_ = [
+ ("String", c_char * 16),
+ ]
+
+class IP_MASK_STRING(Structure):
+ _fields_ = [
+ ("String", c_char * 16),
+ ]
+
+class IP_ADDR_STRING(Structure):
+ pass
+IP_ADDR_STRING._fields_ = [
+ ("Next", POINTER(IP_ADDR_STRING)),
+ ("IpAddress", IP_ADDRESS_STRING),
+ ("IpMask", IP_MASK_STRING),
+ ("Context", DWORD),
+ ]
+
+class IP_ADAPTER_INFO(Structure):
+ pass
+IP_ADAPTER_INFO._fields_ = [
+ ("Next", POINTER(IP_ADAPTER_INFO)),
+ ("ComboIndex", DWORD),
+ ("AdapterName", c_char * (MAX_ADAPTER_NAME_LENGTH + 4)),
+ ("Description", c_char * (MAX_ADAPTER_DESCRIPTION_LENGTH + 4)),
+ ("AddressLength", UINT),
+ ("Address", BYTE * MAX_ADAPTER_LENGTH),
+ ("Index", DWORD),
+ ("Type", UINT),
+ ("DhcpEnabled", UINT),
+ ("CurrentIpAddress", c_void_p), # Not used
+ ("IpAddressList", IP_ADDR_STRING),
+ ("GatewayList", IP_ADDR_STRING),
+ ("DhcpServer", IP_ADDR_STRING),
+ ("HaveWins", BOOL),
+ ("PrimaryWinsServer", IP_ADDR_STRING),
+ ("SecondaryWinsServer", IP_ADDR_STRING),
+ ("LeaseObtained", c_ulong),
+ ("LeaseExpires", c_ulong),
+
+ ]
+
+###############################################################################
+# GetNetworkParams constants and structures
+
+MAX_HOSTNAME_LEN = 128
+MAX_DOMAIN_NAME_LEN = 128
+MAX_SCOPE_ID_LEN = 256
+
+###############################################################################
+# ConvertInterface constants and structures
+
+NDIS_IF_MAX_STRING_SIZE = 256
+
+class IP_ADDRESS_STRING(Structure):
+ _fields_ = [
+ ("String", c_char * 16),
+ ]
+
+class IP_MASK_STRING(Structure):
+ _fields_ = [
+ ("String", c_char * 16),
+ ]
+
+class IP_ADDR_STRING(Structure):
+ pass
+IP_ADDR_STRING._fields_ = [
+ ("Next", POINTER(IP_ADDR_STRING)),
+ ("IpAddress", IP_ADDRESS_STRING),
+ ("IpMask", IP_MASK_STRING),
+ ("Context", DWORD),
+ ]
+
+class FIXED_INFO(Structure):
+ _fields_ = [
+ ("HostName", c_char * (MAX_HOSTNAME_LEN + 4)),
+ ("DomainName", c_char * (MAX_DOMAIN_NAME_LEN + 4)),
+ ("CurrentDnsServer", c_void_p), # Not used
+ ("DnsServerList", IP_ADDR_STRING),
+ ("NodeType", UINT),
+ ("ScopeId", c_char * (MAX_SCOPE_ID_LEN + 4)),
+ ("EnableRouting", UINT),
+ ("EnableProxy", UINT),
+ ("EnableDns", UINT),
+ ]
+
+class WinUtilMixin():
+
+ ###########################################################################
+ # Service related functions
+ ###########################################################################
+
+ ###########################################################################
+ # Establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.
+ #
+ # SC_HANDLE WINAPI OpenSCManager(
+ # _In_opt_ LPCTSTR lpMachineName,
+ # _In_opt_ LPCTSTR lpDatabaseName,
+ # _In_ DWORD dwDesiredAccess
+ # );
+
+ def open_sc_manager(self):
+
+ sc_handle = windll.advapi32.OpenSCManagerA(0, 0, SC_MANAGER_ALL_ACCESS)
+ if sc_handle == 0:
+ self.logger.error("Failed to call OpenSCManager")
+ return
+
+ return sc_handle
+
+ ###########################################################################
+ # Closes a handle to a service control manager or service object
+ #
+ # BOOL WINAPI CloseServiceHandle(
+ # _In_ SC_HANDLE hSCObject
+ # );
+
+ def close_service_handle(self, sc_handle):
+
+ if windll.advapi32.CloseServiceHandle(sc_handle) == 0:
+ self.logger.error('Failed to call CloseServiceHandle')
+ return False
+
+ return True
+
+
+
+ ###########################################################################
+ # Opens an existing service.
+ #
+ # SC_HANDLE WINAPI OpenService(
+ # _In_ SC_HANDLE hSCManager,
+ # _In_ LPCTSTR lpServiceName,
+ # _In_ DWORD dwDesiredAccess
+ # );
+
+ def open_service(self, sc_handle, service_name, dwDesiredAccess = SERVICE_ALL_ACCESS):
+
+ if not sc_handle:
+ return
+
+ service_handle = windll.advapi32.OpenServiceA(sc_handle, service_name, dwDesiredAccess)
+
+ if service_handle == 0:
+ self.logger.error('Failed to call OpenService')
+ return
+
+ return service_handle
+
+ ###########################################################################
+ # Retrieves the current status of the specified service based on the specified information level.
+ #
+ # BOOL WINAPI QueryServiceStatusEx(
+ # _In_ SC_HANDLE hService,
+ # _In_ SC_STATUS_TYPE InfoLevel,
+ # _Out_opt_ LPBYTE lpBuffer,
+ # _In_ DWORD cbBufSize,
+ # _Out_ LPDWORD pcbBytesNeeded
+ # );
+
+ def query_service_status_ex(self, service_handle):
+
+ lpBuffer = SERVICE_STATUS_PROCESS()
+ cbBufSize = DWORD(sizeof(SERVICE_STATUS_PROCESS))
+ pcbBytesNeeded = DWORD()
+
+ if windll.advapi32.QueryServiceStatusEx(service_handle, SC_STATUS_PROCESS_INFO, byref(lpBuffer), cbBufSize, byref(pcbBytesNeeded)) == 0:
+ self.logger.error('Failed to call QueryServiceStatusEx')
+ return
+
+ return lpBuffer
+
+ ###########################################################################
+ # Sends a control code to a service.
+ #
+ # BOOL WINAPI ControlService(
+ # _In_ SC_HANDLE hService,
+ # _In_ DWORD dwControl,
+ # _Out_ LPSERVICE_STATUS lpServiceStatus
+ # );
+
+ def control_service(self, service_handle, dwControl):
+
+ lpServiceStatus = SERVICE_STATUS_PROCESS()
+
+ if windll.advapi32.ControlService(service_handle, dwControl, byref(lpServiceStatus)) == 0:
+ self.logger.error('Failed to call ControlService')
+ return
+
+ return lpServiceStatus
+
+ ###########################################################################
+ # Starts a service
+ #
+ # BOOL WINAPI StartService(
+ # _In_ SC_HANDLE hService,
+ # _In_ DWORD dwNumServiceArgs,
+ # _In_opt_ LPCTSTR *lpServiceArgVectors
+ # );
+
+ def start_service(self, service_handle):
+
+ if windll.advapi32.StartServiceA(service_handle, 0, 0) == 0:
+ self.logger.error('Failed to call StartService')
+ return False
+
+ else:
+ return True
+
+ ###########################################################################
+ # Changes the configuration parameters of a service.
+ #
+ # BOOL WINAPI ChangeServiceConfig(
+ # _In_ SC_HANDLE hService,
+ # _In_ DWORD dwServiceType,
+ # _In_ DWORD dwStartType,
+ # _In_ DWORD dwErrorControl,
+ # _In_opt_ LPCTSTR lpBinaryPathName,
+ # _In_opt_ LPCTSTR lpLoadOrderGroup,
+ # _Out_opt_ LPDWORD lpdwTagId,
+ # _In_opt_ LPCTSTR lpDependencies,
+ # _In_opt_ LPCTSTR lpServiceStartName,
+ # _In_opt_ LPCTSTR lpPassword,
+ # _In_opt_ LPCTSTR lpDisplayName
+ # );
+
+ def change_service_config(self, service_handle, dwStartType = SERVICE_DISABLED):
+
+ if windll.advapi32.ChangeServiceConfigA(service_handle, SERVICE_NO_CHANGE, dwStartType, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0) == 0:
+ self.logger.error('Failed to call ChangeServiceConfig')
+ raise WinError(get_last_error())
+ return False
+
+ else:
+ return True
+
+
+ def start_service_helper(self, service_name = 'Dnscache'):
+
+ sc_handle = None
+ service_handle = None
+
+ timeout = 5
+
+ sc_handle = self.open_sc_manager()
+
+ if not sc_handle:
+ return
+
+ service_handle = self.open_service(sc_handle, service_name)
+
+ if not service_handle:
+ self.close_service_handle(sc_handle)
+ return
+
+ # Enable the service
+ if not self.change_service_config(service_handle, SERVICE_AUTO_START):
+
+ # Backup enable the service
+ try:
+ subprocess.check_call("sc config %s start= auto" % service_name, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except subprocess.CalledProcessError, e:
+ self.logger.error('Failed to enable the service %s. (sc config)', service_name)
+ else:
+ self.logger.info('Successfully enabled the service %s. (sc config)', service_name)
+
+ else:
+ self.logger.info('Successfully enabled the service %s.', service_name)
+
+ service_status = self.query_service_status_ex(service_handle)
+
+ if service_status:
+
+ if not service_status.dwCurrentState in [SERVICE_RUNNING, SERVICE_START_PENDING]:
+
+ # Start service
+ if self.start_service(service_handle):
+
+ # Wait for the service to start
+ while timeout:
+ timeout -= 1
+ time.sleep(1)
+
+ service_status = self.query_service_status_ex(service_handle)
+ if service_status.dwCurrentState == SERVICE_RUNNING:
+ self.logger.info('Successfully started the service %s.', service_name)
+ break
+ else:
+ self.logger.error('Timed out while trying to start the service %s.', service_name)
+ else:
+ self.logger.error('Failed to start the service %s.', service_name)
+ else:
+ self.logger.error('Service %s is already running.', service_name)
+
+ # As a backup call net stop
+ if service_status.dwCurrentState != SERVICE_RUNNING:
+
+ try:
+ subprocess.check_call("net start %s" % service_name, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except subprocess.CalledProcessError, e:
+ self.logger.error('Failed to start the service %s. (net stop)', service_name)
+ else:
+ self.logger.info('Successfully started the service %s.', service_name)
+
+
+
+
+ self.close_service_handle(service_handle)
+ self.close_service_handle(sc_handle)
+
+ def stop_service_helper(self, service_name = 'Dnscache'):
+
+ sc_handle = None
+ service_handle = None
+
+ Control = SERVICE_CONTROL_STOP
+ dwControl = DWORD(Control)
+ timeout = 5
+
+ sc_handle = self.open_sc_manager()
+
+ if not sc_handle:
+ return
+
+ service_handle = self.open_service(sc_handle, service_name)
+
+ if not service_handle:
+ self.close_service_handle(sc_handle)
+ return
+
+ # Disable the service
+ if not self.change_service_config(service_handle, SERVICE_DISABLED):
+
+ # Backup disable the service
+ try:
+ subprocess.check_call("sc config %s start= disabled" % service_name, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except subprocess.CalledProcessError, e:
+ self.logger.error('Failed to disable the service %s. (sc config)', service_name)
+ else:
+ self.logger.info('Successfully disabled the service %s. (sc config)', service_name)
+
+ else:
+ self.logger.info('Successfully disabled the service %s.', service_name)
+
+ service_status = self.query_service_status_ex(service_handle)
+
+ if service_status:
+
+ if service_status.dwCurrentState != SERVICE_STOPPED:
+
+ # Send a stop code to the service
+ if self.control_service(service_handle, dwControl):
+
+ # Wait for the service to stop
+ while timeout:
+ timeout -= 1
+ time.sleep(1)
+
+ service_status = self.query_service_status_ex(service_handle)
+ if service_status.dwCurrentState == SERVICE_STOPPED:
+ self.logger.info('Successfully stopped the service %s.', service_name)
+ break
+
+ else:
+ self.logger.error('Timed out while trying to stop the service %s.', service_name)
+ else:
+ self.logger.error('Failed to stop the service %s.', service_name)
+ else:
+ self.logger.error('Service %s is already stopped.', service_name)
+
+ # As a backup call net stop
+ if service_status.dwCurrentState != SERVICE_STOPPED:
+
+ try:
+ subprocess.check_call("net stop %s" % service_name, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except subprocess.CalledProcessError, e:
+ self.logger.error('Failed to stop the service %s. (net stop)', service_name)
+ else:
+ self.logger.info('Successfully stopped the service %s.', service_name)
+
+ self.close_service_handle(service_handle)
+ self.close_service_handle(sc_handle)
+
+
+
+
+ ###########################################################################
+ # Process related functions
+ ###########################################################################
+
+ ###########################################################################
+ # The GetExtendedTcpTable function retrieves a table that contains a list of TCP endpoints available to the application.
+ #
+ # DWORD GetExtendedTcpTable(
+ # _Out_ PVOID pTcpTable,
+ # _Inout_ PDWORD pdwSize,
+ # _In_ BOOL bOrder,
+ # _In_ ULONG ulAf,
+ # _In_ TCP_TABLE_CLASS TableClass,
+ # _In_ ULONG Reserved
+ # );
+
+ def get_extended_tcp_table(self):
+
+ dwSize = DWORD(sizeof(MIB_TCPROW_OWNER_PID) * 512 + 4)
+
+ TcpTable = MIB_TCPTABLE_OWNER_PID()
+
+ if windll.iphlpapi.GetExtendedTcpTable(byref(TcpTable), byref(dwSize), False, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) != NO_ERROR:
+ self.logger.error("Failed to call GetExtendedTcpTable")
+ return
+
+ for item in TcpTable.table[:TcpTable.dwNumEntries]:
+ yield item
+
+ def get_pid_port_tcp(self, port):
+
+ for item in self.get_extended_tcp_table():
+
+ lPort = socket.ntohs(item.dwLocalPort)
+ lAddr = socket.inet_ntoa(struct.pack('L', item.dwLocalAddr))
+ pid = item.dwOwningPid
+
+ if lPort == port:
+ return pid
+ else:
+ return None
+
+ #################################################################################
+ # The GetExtendedUdpTable function retrieves a table that contains a list of UDP endpoints available to the application.
+ #
+ # DWORD GetExtendedUdpTable(
+ # _Out_ PVOID pUdpTable,
+ # _Inout_ PDWORD pdwSize,
+ # _In_ BOOL bOrder,
+ # _In_ ULONG ulAf,
+ # _In_ UDP_TABLE_CLASS TableClass,
+ # _In_ ULONG Reserved
+ # );
+
+ def get_extended_udp_table(self):
+
+ dwSize = DWORD(sizeof(MIB_UDPROW_OWNER_PID) * 512 + 4)
+
+ UdpTable = MIB_UDPTABLE_OWNER_PID()
+
+ if windll.iphlpapi.GetExtendedUdpTable(byref(UdpTable), byref(dwSize), False, AF_INET, UDP_TABLE_OWNER_PID, 0) != NO_ERROR:
+ self.logger.error("Failed to call GetExtendedUdpTable")
+ return
+
+ for item in UdpTable.table[:UdpTable.dwNumEntries]:
+ yield item
+
+ def get_pid_port_udp(self, port):
+
+ for item in self.get_extended_udp_table():
+
+ lPort = socket.ntohs(item.dwLocalPort)
+ lAddr = socket.inet_ntoa(struct.pack('L', item.dwLocalAddr))
+ pid = item.dwOwningPid
+
+ if lPort == port:
+ return pid
+ else:
+ return None
+
+ ###############################################################################
+ # Retrieves the name of the executable file for the specified process.
+ #
+ # DWORD WINAPI GetProcessImageFileName(
+ # _In_ HANDLE hProcess,
+ # _Out_ LPTSTR lpImageFileName,
+ # _In_ DWORD nSize
+ # );
+
+ def get_process_image_filename(self, pid):
+
+ process_name = None
+
+ hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
+ if hProcess:
+
+ lpImageFileName = create_string_buffer(MAX_PATH)
+
+ if windll.psapi.GetProcessImageFileNameA(hProcess, lpImageFileName, MAX_PATH) > 0:
+ process_name = os.path.basename(lpImageFileName.value)
+ else:
+ self.logger.error('Failed to call GetProcessImageFileNameA')
+
+ windll.kernel32.CloseHandle(hProcess)
+
+ return process_name
+
+
+
+
+ ###############################################################################
+ # The GetAdaptersAddresses function retrieves the addresses associated with the adapters on the local computer.
+ #
+ # ULONG WINAPI GetAdaptersAddresses(
+ # _In_ ULONG Family,
+ # _In_ ULONG Flags,
+ # _In_ PVOID Reserved,
+ # _Inout_ PIP_ADAPTER_ADDRESSES AdapterAddresses,
+ # _Inout_ PULONG SizePointer
+ # );
+
+ def get_adapters_addresses(self):
+
+ Size = ULONG(0)
+
+ windll.iphlpapi.GetAdaptersAddresses(AF_INET, 0, None, None, byref(Size))
+
+ AdapterAddresses = create_string_buffer(Size.value)
+ pAdapterAddresses = cast(AdapterAddresses, POINTER(IP_ADAPTER_ADDRESSES))
+
+ if not windll.iphlpapi.GetAdaptersAddresses(AF_INET, 0, None, pAdapterAddresses, byref(Size)) == NO_ERROR:
+ self.logger.error('Failed calling GetAdaptersAddresses')
+ return
+
+ while pAdapterAddresses:
+
+ yield pAdapterAddresses.contents
+ pAdapterAddresses = pAdapterAddresses.contents.Next
+
+ def get_active_ethernet_adapters(self):
+
+ for adapter in self.get_adapters_addresses():
+
+ if adapter.IfType == MIB_IF_TYPE_ETHERNET and adapter.OperStatus == IFOPERSTATUSUP:
+ yield adapter
+
+ def check_active_ethernet_adapters(self):
+
+ for adapter in self.get_adapters_addresses():
+
+ if adapter.IfType == MIB_IF_TYPE_ETHERNET and adapter.OperStatus == IFOPERSTATUSUP:
+ return True
+ else:
+ return False
+
+
+ ###########################################################################
+ # The GetAdaptersInfo function retrieves adapter information for the local computer.
+ #
+ # On Windows XP and later: Use the GetAdaptersAddresses function instead of GetAdaptersInfo.
+ #
+ # DWORD GetAdaptersInfo(
+ # _Out_ PIP_ADAPTER_INFO pAdapterInfo,
+ # _Inout_ PULONG pOutBufLen
+ # );
+
+ def get_adapters_info(self):
+
+ OutBufLen = DWORD(0)
+
+ windll.iphlpapi.GetAdaptersInfo(None, byref(OutBufLen))
+
+ AdapterInfo = create_string_buffer(OutBufLen.value)
+ pAdapterInfo = cast(AdapterInfo, POINTER(IP_ADAPTER_INFO))
+
+ if not windll.iphlpapi.GetAdaptersInfo(byref(AdapterInfo), byref(OutBufLen)) == NO_ERROR:
+ self.logger.error('Failed calling GetAdaptersInfo')
+ return
+
+ while pAdapterInfo:
+
+ yield pAdapterInfo.contents
+ pAdapterInfo = pAdapterInfo.contents.Next
+
+ def get_gateways(self):
+
+ for adapter in self.get_adapters_info():
+
+ gateway = adapter.GatewayList
+
+ while gateway:
+
+ yield gateway.IpAddress.String
+ gateway = gateway.Next
+
+ def check_gateways(self):
+
+ for gateway in self.get_gateways():
+ if gateway != '0.0.0.0':
+ return True
+ else:
+ return False
+
+
+ ###########################################################################
+ # The GetNetworkParams function retrieves network parameters for the local computer.
+ #
+ # DWORD GetNetworkParams(
+ # _Out_ PFIXED_INFO pFixedInfo,
+ # _In_ PULONG pOutBufLen
+ # );
+
+ def get_network_params(self):
+ OutBufLen = ULONG(sizeof(FIXED_INFO))
+ FixedInfo = FIXED_INFO()
+
+ if not windll.iphlpapi.GetNetworkParams(byref(FixedInfo), byref(OutBufLen)) == NO_ERROR:
+ self.logger.error('Failed calling GetNetworkParams')
+ return None
+
+ return FixedInfo
+
+ def get_dns_servers(self):
+
+ FixedInfo = self.get_network_params()
+
+ if not FixedInfo:
+ return
+
+ ip_addr_string = FixedInfo.DnsServerList
+
+ while ip_addr_string:
+
+ yield ip_addr_string.IpAddress.String
+ ip_addr_string = ip_addr_string.Next
+
+ def check_dns_servers(self):
+
+ FixedInfo = self.get_network_params()
+
+ if not FixedInfo:
+ return
+
+ ip_addr_string = FixedInfo.DnsServerList
+
+ if ip_addr_string:
+ return True
+
+ else:
+ return False
+
+
+ ###########################################################################
+ # The GetBestInterface function retrieves the index of the interface that has the best route to the specified IPv4 address.
+ #
+ # DWORD GetBestInterface(
+ # _In_ IPAddr dwDestAddr,
+ # _Out_ PDWORD pdwBestIfIndex
+ # );
+
+ def get_best_interface(self, ip='8.8.8.8'):
+ BestIfIndex = DWORD()
+ DestAddr = socket.inet_aton(ip)
+
+ if not windll.iphlpapi.GetBestInterface(DestAddr, byref(BestIfIndex)) == NO_ERROR:
+ self.logger.error('Failed calling GetBestInterface')
+ return None
+
+ return BestIfIndex.value
+
+ def check_best_interface(self, ip='8.8.8.8'):
+ BestIfIndex = DWORD()
+ DestAddr = socket.inet_aton(ip)
+
+ if not windll.iphlpapi.GetBestInterface(DestAddr, byref(BestIfIndex)) == NO_ERROR:
+ return False
+
+ return True
+
+ ###########################################################################
+ # Convert interface index to name
+ #
+ # NETIO_STATUS WINAPI ConvertInterfaceIndexToLuid(
+ # _In_ NET_IFINDEX InterfaceIndex,
+ # _Out_ PNET_LUID InterfaceLuid
+ # );
+ #
+ # NETIO_STATUS WINAPI ConvertInterfaceLuidToNameA(
+ # _In_ const NET_LUID *InterfaceLuid,
+ # _Out_ PSTR InterfaceName,
+ # _In_ SIZE_T Length
+ # );
+
+ def convert_interface_index_to_name(self, index):
+
+ InterfaceLuid = ULONG64()
+
+ if not windll.iphlpapi.ConvertInterfaceIndexToLuid(index, byref(InterfaceLuid)) == NO_ERROR:
+ self.logger.error('Failed calling ConvertInterfaceIndexToLuid')
+ return None
+
+ InterfaceName = create_string_buffer(NDIS_IF_MAX_STRING_SIZE + 1)
+
+ if not windll.iphlpapi.ConvertInterfaceLuidToNameA(byref(InterfaceLuid), InterfaceName, NDIS_IF_MAX_STRING_SIZE + 1) == NO_ERROR:
+ self.logger.error('Failed calling ConvertInterfaceLuidToName')
+ return None
+
+ return InterfaceName.value
+
+ ###########################################################################
+ # DnsFlushResolverCache
+ def notify_ip_change(self, adapter_name):
+
+ if windll.dhcpcsvc.DhcpNotifyConfigChange(0, adapter_name, 0, 0, 0, 0) == NO_ERROR:
+ self.logger.debug('Successfully performed adapter change notification on %s', adapter_name)
+ else:
+ self.logger.error('Failed to notify adapter change on %s', adapter_name)
+
+ ###########################################################################
+ # DnsFlushResolverCache
+ def flush_dns(self):
+ if windll.dnsapi.DnsFlushResolverCache():
+ self.logger.info('Flushed DNS cache.')
+ else:
+ self.logger.error('Failed to flush DNS cache. (DnsFlushResolverCache)')
+
+ # As a backup call ipconfig /flushdns because DnsFlushResolverCache is undocumented.
+ try:
+ subprocess.check_call('ipconfig /flushdns', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except subprocess.CalledProcessError, e:
+ self.logger.error("Failed to flush DNS cache. (ipconfig)")
+ else:
+ self.logger.info('Flushed DNS cache. (ipconfig)')
+
+ def get_reg_value(self, key, sub_key, value, sam = KEY_READ):
+
+ try:
+ handle = OpenKey(key, sub_key, 0, sam)
+ [data, regtype] = QueryValueEx(handle, value)
+ CloseKey(handle)
+ if data == '':
+ raise WindowsError
+
+ return data
+
+ except WindowsError:
+ self.logger.error('Failed getting registry value %s.', value)
+ return None
+
+ def set_reg_value(self, key, sub_key, value, data, type = REG_SZ, sam = KEY_WRITE):
+
+ try:
+ handle = CreateKeyEx(key, sub_key, 0, sam)
+ SetValueEx(handle, value, 0, type, data)
+ CloseKey(handle)
+
+ return True
+
+ except WindowsError:
+ self.logger.error('Failed setting registry value %s', value)
+ return False
+
+ ###########################################################################
+ # Set DNS Server
+
+ def set_dns_server(self, dns_server = '127.0.0.1'):
+
+ key = HKEY_LOCAL_MACHINE
+ sub_key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s"
+ value = 'NameServer'
+
+ for adapter in self.get_active_ethernet_adapters():
+
+ # Preserve existing setting
+ dns_server_backup = self.get_reg_value(key, sub_key % adapter.AdapterName, value)
+
+ # Restore previous value or a blank string if the key was not present
+ if dns_server_backup:
+ self.adapters_dns_server_backup[adapter.AdapterName] = (dns_server_backup, adapter.FriendlyName)
+ else:
+ self.adapters_dns_server_backup[adapter.AdapterName] = ('', adapter.FriendlyName)
+
+ # Set new dns server value
+ if self.set_reg_value(key, sub_key % adapter.AdapterName, value, dns_server):
+ self.logger.info('Set DNS server %s on the adapter: %s', dns_server, adapter.FriendlyName)
+ self.notify_ip_change(adapter.AdapterName)
+ else:
+ self.logger.error('Failed to set DNS server %s on the adapter: %s', dns_server, adapter.FriendlyName)
+
+ def restore_dns_server(self):
+
+ key = HKEY_LOCAL_MACHINE
+ sub_key = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%s"
+ value = 'NameServer'
+
+ for adapter_name in self.adapters_dns_server_backup:
+
+ (dns_server, adapter_friendlyname) = self.adapters_dns_server_backup[adapter_name]
+
+ # Restore dns server value
+ if self.set_reg_value(key, sub_key % adapter_name, value, dns_server):
+ self.logger.info('Restored DNS server %s on the adapter: %s', dns_server, adapter_friendlyname)
+ else:
+ self.logger.error('Failed to restore DNS server %s on the adapter: %s', dns_server, adapter_friendlyname)
+
+ ###########################################################################
+ # Check if user is an Administrator
+ def is_user_an_admin(self):
+ return ctypes.windll.shell32.IsUserAnAdmin()
+
+ ###########################################################################
+ # Execute process and detach
+ def execute_detached(self, execute_cmd):
+ DETACHED_PROCESS = 0x00000008
+
+ # import pdb
+ # pdb.set_trace()
+ try:
+ pid = subprocess.Popen(execute_cmd.split(), creationflags=DETACHED_PROCESS).pid
+ except Exception, e:
+ self.logger.error('Error: Failed to execute command: %s', execute_cmd)
+ self.logger.error(' %s', e)
+ else:
+ return pid
+
+def test_process_list():
+
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ pid = self.get_pid_port_tcp(135)
+ if pid:
+ self.logger.info('pid: %d name: %s', pid, self.get_process_image_filename(pid))
+ else:
+ self.logger.error('failed to get pid for tcp port 135')
+
+
+ pid = self.get_pid_port_udp(123)
+ if pid:
+ self.logger.info('pid: %d name: %s', pid, self.get_process_image_filename(pid))
+ else:
+ self.logger.error('failed to get pid for udp port 123')
+
+ pid = self.get_pid_port_tcp(1234)
+ if not pid:
+ self.logger.info('successfully returned None for unknown tcp port 1234')
+
+ pid = self.get_pid_port_udp(1234)
+ if not pid:
+ self.logger.info('successfully returned None for unknown udp port 1234')
+
+def test_interfaces_list():
+
+
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ # for adapter in self.get_adapters_addresses():
+ # self.logger.info('ethernet: %s enabled: %s index: %d friendlyname: %s name: %s', adapter.IfType == MIB_IF_TYPE_ETHERNET, adapter.OperStatus == IFOPERSTATUSUP, adapter.IfIndex, adapter.FriendlyName, adapter.AdapterName)
+
+
+ for dns_server in self.get_dns_servers():
+ self.logger.info('dns: %s', dns_server)
+
+ for gateway in self.get_gateways():
+ self.logger.info('gateway: %s', gateway)
+
+ for adapter in self.get_active_ethernet_adapters():
+ self.logger.info('active ethernet index: %s friendlyname: %s name: %s', adapter.IfIndex, adapter.FriendlyName, adapter.AdapterName)
+
+
+def test_registry_nameserver():
+
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ key = HKEY_LOCAL_MACHINE
+ sub_key = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{cd17d5b5-bf83-44f5-8de7-d988e3db5451}'
+ value = 'NameServer'
+ data = '127.0.0.1'
+
+
+ data_tmp = self.get_reg_value(key, sub_key, value)
+ self.logger.info('NameServer: %s', data_tmp)
+
+ if self.set_reg_value(key, sub_key, value, data):
+ self.logger.info('Successfully set value %s to data %s', value, data)
+
+ data_tmp = self.get_reg_value(key, sub_key, value)
+ self.logger.info('Nameserver: %s', data_tmp)
+ else:
+ self.logger.info('Failed to set value %s to data %s', value, data)
+
+ self.notify_ip_change('{cd17d5b5-bf83-44f5-8de7-d988e3db5451}')
+
+ self.flush_dns()
+
+
+def test_registry_gateway():
+
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ key = HKEY_LOCAL_MACHINE
+ sub_key = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{cd17d5b5-bf83-44f5-8de7-d988e3db5451}'
+ #value = 'NameServer'
+ #data = '127.0.0.1'
+
+ if self.get_reg_value(key, sub_key, 'DhcpDefaultGateway'):
+ self.logger.info('DefaultGateway is set')
+
+ else:
+ ip = self.get_reg_value(key, sub_key, 'Dhcp')
+ #self.logger
+
+
+ self.notify_ip_change('{cd17d5b5-bf83-44f5-8de7-d988e3db5451}')
+
+def test_check_connectivity():
+
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ if not self.check_gateways():
+ self.logger.warning('No gateways found.')
+ else:
+ self.logger.info('Gateways PASS')
+
+ if not self.check_active_ethernet_adapters():
+ self.logger.warning('No active ethernet adapters found')
+ else:
+ self.logger.info('Active ethernet PASS')
+
+ if not self.get_best_interface():
+ self.logger.warning('No routable interface found.')
+ else:
+ self.logger.info('Routable interface PASS')
+
+ if not self.check_dns_servers():
+ self.logger.warning('No DNS servers configured')
+ else:
+ self.logger.info('DNS server PASS')
+
+def test_stop_service():
+
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ self.stop_service_helper('Dnscache')
+
+
+def test_start_service():
+ class Test(WinUtilMixin):
+ def __init__(self, name = 'WinUtil'):
+ self.logger = logging.getLogger(name)
+
+ self = Test()
+
+ self.start_service_helper('Dnscache')
+
+def main():
+ pass
+
+ #test_process_list()
+
+ #test_interfaces_list()
+
+ #test_registry_gateway()
+
+
+ #test_check_connectivity()
+
+ test_stop_service()
+ #test_start_service()
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/fakenet/diverters/winutil.pyc b/fakenet/diverters/winutil.pyc
new file mode 100644
index 0000000..ed72773
Binary files /dev/null and b/fakenet/diverters/winutil.pyc differ
diff --git a/fakenet/fakenet.py b/fakenet/fakenet.py
new file mode 100644
index 0000000..f7c4207
--- /dev/null
+++ b/fakenet/fakenet.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+#
+# FakeNet-NG is a next generation dynamic network analysis tool for malware
+# analysts and penetration testers.
+#
+# Developed by Peter Kacherginsky
+
+import logging
+
+import os
+import sys
+import time
+
+from collections import OrderedDict
+
+from optparse import OptionParser,OptionGroup
+from ConfigParser import ConfigParser
+
+import platform
+
+from optparse import OptionParser
+
+###############################################################################
+# Listener services
+import listeners
+from listeners import *
+
+###############################################################################
+# FakeNet
+###############################################################################
+
+class Fakenet():
+
+ def __init__(self, logging_level = logging.INFO):
+
+ self.logger = logging.getLogger('FakeNet')
+ self.logger.setLevel(logging_level)
+
+ self.logging_level = logging_level
+
+ # Diverter used to intercept and redirect traffic
+ self.diverter = None
+
+ # FakeNet options and parameters
+ self.fakenet_config = dict()
+
+ # Diverter options and parameters
+ self.diverter_config = dict()
+
+ # Listener options and parameters
+ self.listeners_config = OrderedDict()
+
+ # List of running listener providers
+ self.running_listener_providers = list()
+
+ def parse_config(self, config_filename):
+
+ if not config_filename:
+
+ config_filename = os.path.join(os.path.dirname(__file__), 'configs', 'default.ini')
+
+ if not os.path.exists(config_filename):
+
+ config_filename = os.path.join(os.path.dirname(__file__), 'configs', config_filename)
+
+ if not os.path.exists(config_filename):
+
+ self.logger.error('Could not open configuration file %s', config_filename)
+ sys.exit(1)
+
+ config = ConfigParser()
+ config.read(config_filename)
+
+ self.logger.info('Loaded configuration file: %s', config_filename)
+
+ # Parse configuration
+ for section in config.sections():
+
+ if section == 'FakeNet':
+ self.fakenet_config = dict(config.items(section))
+
+ elif section == 'Diverter':
+ self.diverter_config = dict(config.items(section))
+
+ elif config.getboolean(section, 'enabled'):
+ self.listeners_config[section] = dict(config.items(section))
+
+ def start(self):
+
+ if self.fakenet_config.get('diverttraffic') and self.fakenet_config['diverttraffic'].lower() == 'yes':
+
+ # Select platform specific diverter
+ if platform.system() == 'Windows':
+
+ # Check Windows version
+ if platform.release() in ['2000', 'XP', '2003Server', 'post2003']:
+ self.logger.error('Error: FakeNet-NG only supports Windows Vista+.')
+ self.logger.error(' Please use the original Fakenet for older versions of Windows.')
+ sys.exit(1)
+
+ # Check architecture
+ if platform.machine() == 'AMD64' and platform.architecture()[0] != '64bit':
+ self.logger.error('Error: Please install 64-bit Python interpreter to support diverter functions.')
+ sys.exit(1)
+
+
+ from diverters.windows import Diverter
+ self.diverter = Diverter(self.diverter_config, self.listeners_config, self.logging_level)
+
+ else:
+ self.logger.error('Error: Your system %s is currently not supported.', platform.system())
+ sys.exit(1)
+
+
+ # Start all of the listeners
+ for listener_name in self.listeners_config:
+
+ listener_config = self.listeners_config[listener_name]
+
+ # Anonymous listener
+ if not 'listener' in listener_config:
+ self.logger.info('Anonymous %s listener on %s port %s...', listener_name, listener_config['protocol'], listener_config['port'])
+ continue
+
+ # Get a specific provider for the listener name
+ try:
+ listener_module = getattr(listeners, listener_config['listener'])
+ listener_provider = getattr(listener_module, listener_config['listener'])
+
+ except AttributeError as e:
+ self.logger.error('Listener %s is not implemented.', listener_config['listener'])
+ self.logger.error("%s" % e)
+
+ else:
+ # Listener provider object
+ listener_provider_instance = listener_provider(listener_config, listener_name, self.logging_level)
+
+ # Store listener provider object
+ self.running_listener_providers.append(listener_provider_instance)
+
+ try:
+ listener_provider_instance.start()
+ except Exception, e:
+ self.logger.error('Error starting %s listener:', listener_config['listener'])
+ self.logger.error(" %s" % e)
+
+
+ # Start the diverter
+ if self.diverter:
+ self.diverter.start()
+
+ def stop(self):
+
+ self.logger.info("Stopping...")
+
+ for running_listener_provider in self.running_listener_providers:
+ running_listener_provider.stop()
+
+ if self.diverter:
+ self.diverter.stop()
+
+ sys.exit(0)
+
+def main():
+
+ print """
+ ______ _ ________ _ _ ______ _______ _ _ _____
+ | ____/\ | |/ / ____| \ | | ____|__ __| | \ | |/ ____|
+ | |__ / \ | ' /| |__ | \| | |__ | |______| \| | | __
+ | __/ /\ \ | < | __| | . ` | __| | |______| . ` | | |_ |
+ | | / ____ \| . \| |____| |\ | |____ | | | |\ | |__| |
+ |_|/_/ \_\_|\_\______|_| \_|______| |_| |_| \_|\_____|
+
+ Version 1.0
+ _____________________________________________________________
+ Developed by
+ Peter Kacherginsky
+ FLARE (FireEye Labs Advanced Reversing Engineering)
+ _____________________________________________________________
+ """
+
+ # Parse command line arguments
+ parser = OptionParser(usage = "fakenet.py [options]:")
+ parser.add_option("-c", "--config-file", action="store", dest="config_file",
+ help="configuration filename", metavar="FILE")
+ parser.add_option("-v", "--verbose",
+ action="store_true", dest="verbose", default=False,
+ help="print more verbose messages.")
+ parser.add_option("-l", "--log-file", action="store", dest="log_file")
+
+ (options, args) = parser.parse_args()
+
+ logging_level = logging.DEBUG if options.verbose else logging.INFO
+
+ if options.log_file:
+ logging.basicConfig(format='%(asctime)s [%(name)18s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.INFO, filename=options.log_file)
+ else:
+ logging.basicConfig(format='%(asctime)s [%(name)18s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.INFO)
+
+ fakenet = Fakenet(logging_level)
+ fakenet.parse_config(options.config_file)
+ fakenet.start()
+
+ try:
+ while True:
+ time.sleep(1)
+
+ except KeyboardInterrupt:
+ fakenet.stop()
+
+ except:
+ e = sys.exc_info()[0]
+ fakenet.logger.error("ERROR: %s" % e)
+ fakenet.stop()
+
+if __name__ == '__main__':
+ main()
diff --git a/fakenet/lib/32/WinDivert.dll b/fakenet/lib/32/WinDivert.dll
new file mode 100644
index 0000000..5a58851
Binary files /dev/null and b/fakenet/lib/32/WinDivert.dll differ
diff --git a/fakenet/lib/32/WinDivert32.sys b/fakenet/lib/32/WinDivert32.sys
new file mode 100644
index 0000000..a1f83fb
Binary files /dev/null and b/fakenet/lib/32/WinDivert32.sys differ
diff --git a/fakenet/lib/64/WinDivert.dll b/fakenet/lib/64/WinDivert.dll
new file mode 100644
index 0000000..7b34194
Binary files /dev/null and b/fakenet/lib/64/WinDivert.dll differ
diff --git a/fakenet/lib/64/WinDivert64.sys b/fakenet/lib/64/WinDivert64.sys
new file mode 100644
index 0000000..245939b
Binary files /dev/null and b/fakenet/lib/64/WinDivert64.sys differ
diff --git a/fakenet/listeners/DNSListener.py b/fakenet/listeners/DNSListener.py
new file mode 100644
index 0000000..407f8a7
--- /dev/null
+++ b/fakenet/listeners/DNSListener.py
@@ -0,0 +1,222 @@
+import logging
+
+import threading
+import SocketServer
+from dnslib import *
+
+import ssl
+import socket
+
+class DNSListener():
+
+ def __init__(self, config = {}, name = 'DNSListener', logging_level = logging.INFO):
+
+ self.logger = logging.getLogger(name)
+ self.logger.setLevel(logging_level)
+
+ self.config = config
+ self.local_ip = '0.0.0.0'
+ self.server = None
+
+ self.logger.info('Starting...')
+
+ self.logger.debug('Initialized with config:')
+ for key, value in config.iteritems():
+ self.logger.debug(' %10s: %s', key, value)
+
+ def start(self):
+
+ # Start UDP listener
+ if self.config['protocol'].lower() == 'udp':
+ self.logger.debug('Starting UDP ...')
+ self.server = ThreadedUDPServer((self.local_ip, int(self.config.get('port', 53))), self.config, self.logger, UDPHandler)
+
+ # Start TCP listener
+ elif self.config['protocol'].lower() == 'tcp':
+ self.logger.debug('Starting TCP ...')
+ self.server = ThreadedTCPServer((self.local_ip, int(self.config.get('port', 53))), self.config, self.logger, TCPHandler)
+
+ self.server.nxdomains = int(self.config.get('nxdomains', 0))
+
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+
+ def stop(self):
+ self.logger.debug('Stopping...')
+
+ # Stop listener
+ if self.server:
+ self.server.shutdown()
+ self.server.server_close()
+
+
+class DNSHandler():
+
+ def parse(self,data):
+ response = ""
+
+ try:
+ # Parse data as DNS
+ d = DNSRecord.parse(data)
+
+ except Exception, e:
+ self.server.logger.error('Error: Invalid DNS Request')
+ self.server.logger.info('%s', '-'*80)
+ for line in hexdump_table(data):
+ self.server.logger.info(line)
+ self.server.logger.info('%s', '-'*80,)
+
+ else:
+ # Only Process DNS Queries
+ if QR[d.header.qr] == "QUERY":
+
+ # Gather query parameters
+ # NOTE: Do not lowercase qname here, because we want to see
+ # any case request weirdness in the logs.
+ qname = str(d.q.qname)
+
+ # Chop off the last period
+ if qname[-1] == '.': qname = qname[:-1]
+
+ qtype = QTYPE[d.q.qtype]
+
+ self.server.logger.info('Received %s request for domain \'%s\'.', qtype, qname)
+
+ # Create a custom response to the query
+ response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q)
+
+ # Get fake record from the configuration or use the external address
+ fake_record = self.server.config.get('dnsresponse', socket.gethostbyname(socket.gethostname()))
+
+ if qtype == 'A':
+
+ if self.server.nxdomains > 0:
+ self.server.logger.info('Ignoring query. NXDomains: %d', self.server.nxdomains)
+ self.server.nxdomains -= 1
+ else:
+ # dnslib doesn't like trailing dots
+ if fake_record[-1] == ".": fake_record = fake_record[:-1]
+
+ self.server.logger.info('Responding with \'%s\'', fake_record)
+ response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](fake_record)))
+
+ response = response.pack()
+
+ return response
+
+class UDPHandler(DNSHandler, SocketServer.BaseRequestHandler):
+
+ def handle(self):
+
+ try:
+ (data,socket) = self.request
+ response = self.parse(data)
+
+ if response:
+ socket.sendto(response, self.client_address)
+
+ except socket.error as msg:
+ self.server.logger.error('Error: %s', msg.strerror or msg)
+
+ except Exception, e:
+ self.server.logger.error('Error: %s', e)
+
+class TCPHandler(DNSHandler, SocketServer.BaseRequestHandler):
+
+ def handle(self):
+
+ # Timeout connection to prevent hanging
+ self.request.settimeout(int(self.server.config.get('timeout', 5)))
+
+ try:
+ data = self.request.recv(1024)
+
+ # Remove the addition "length" parameter used in the
+ # TCP DNS protocol
+ data = data[2:]
+ response = self.parse(data)
+
+ if response:
+ # Calculate and add the additional "length" parameter
+ # used in TCP DNS protocol
+ length = binascii.unhexlify("%04x" % len(response))
+ self.request.sendall(length+response)
+
+ except socket.timeout:
+ self.server.logger.warning('Connection timeout.')
+
+ except socket.error as msg:
+ self.server.logger.error('Error: %s', msg.strerror)
+
+ except Exception, e:
+ self.server.logger.error('Error: %s', e)
+
+class ThreadedUDPServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
+
+ # Override SocketServer.UDPServer to add extra parameters
+ def __init__(self, server_address, config, logger, RequestHandlerClass):
+ self.config = config
+ self.logger = logger
+ SocketServer.UDPServer.__init__(self, server_address, RequestHandlerClass)
+
+class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+
+ # Override default value
+ allow_reuse_address = True
+
+ # Override SocketServer.TCPServer to add extra parameters
+ def __init__(self, server_address, config, logger, RequestHandlerClass):
+ self.config = config
+ self.logger = logger
+ SocketServer.TCPServer.__init__(self,server_address,RequestHandlerClass)
+
+def hexdump_table(data, length=16):
+
+ hexdump_lines = []
+ for i in range(0, len(data), 16):
+ chunk = data[i:i+16]
+ hex_line = ' '.join(["%02X" % ord(b) for b in chunk ] )
+ ascii_line = ''.join([b if ord(b) > 31 and ord(b) < 127 else '.' for b in chunk ] )
+ hexdump_lines.append("%04X: %-*s %s" % (i, length*3, hex_line, ascii_line ))
+ return hexdump_lines
+
+###############################################################################
+# Testing code
+def test(config):
+
+ print "\t[DNSListener] Testing 'google.com' A record."
+ query = DNSRecord(q=DNSQuestion('google.com',getattr(QTYPE,'A')))
+ answer_pkt = query.send('localhost', int(config.get('port', 53)))
+ answer = DNSRecord.parse(answer_pkt)
+
+ print '-'*80
+ print answer
+ print '-'*80
+
+
+def main():
+ logging.basicConfig(format='%(asctime)s [%(name)15s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.DEBUG)
+
+ config = {'port': '53', 'protocol': 'UDP', 'dnsresponse': '127.0.0.1', 'nxdomains': 3 }
+
+ listener = DNSListener(config, logging_level = logging.DEBUG)
+ listener.start()
+
+
+ ###########################################################################
+ # Run processing
+ import time
+
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
+
+ ###########################################################################
+ # Run tests
+ test(config)
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/fakenet/listeners/DNSListener.pyc b/fakenet/listeners/DNSListener.pyc
new file mode 100644
index 0000000..e2b79a7
Binary files /dev/null and b/fakenet/listeners/DNSListener.pyc differ
diff --git a/fakenet/listeners/HTTPListener.py b/fakenet/listeners/HTTPListener.py
new file mode 100644
index 0000000..b058801
--- /dev/null
+++ b/fakenet/listeners/HTTPListener.py
@@ -0,0 +1,292 @@
+import logging
+
+import os
+import sys
+
+import threading
+import SocketServer
+import BaseHTTPServer
+
+import ssl
+import socket
+
+import posixpath
+import mimetypes
+
+import time
+
+MIME_FILE_RESPONSE = {
+ 'text/html': 'FakeNet.html',
+ 'image/png': 'FakeNet.png',
+ 'image/ico': 'FakeNet.ico',
+ 'image/jpeg': 'FakeNet.jpg',
+ 'application/octet-stream': 'FakeNetMini.exe',
+ 'application/x-msdownload': 'FakeNetMini.exe',
+ 'application/pdf': 'FakeNet.pdf',
+ 'application/xml': 'FakeNet.html'
+}
+
+class HTTPListener():
+
+ if not mimetypes.inited:
+ mimetypes.init() # try to read system mime.types
+ extensions_map = mimetypes.types_map.copy()
+ extensions_map.update({
+ '': 'text/html', # Default
+ })
+
+ def __init__(self, config = {}, name = 'HTTPListener', logging_level = logging.DEBUG):
+ self.logger = logging.getLogger(name)
+ self.logger.setLevel(logging_level)
+
+ self.config = config
+ self.name = name
+ self.local_ip = '0.0.0.0'
+ self.server = None
+
+ self.logger.info('Starting...')
+
+ self.logger.debug('Initialized with config:')
+ for key, value in config.iteritems():
+ self.logger.debug(' %10s: %s', key, value)
+
+ # Initialize webroot directory
+ self.webroot_path = self.config.get('webroot','defaultFiles')
+
+ # Try absolute path first
+ if not os.path.exists(self.webroot_path):
+
+ # Try to locate the webroot directory relative to application path
+ self.webroot_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), self.webroot_path)
+
+ if not os.path.exists(self.webroot_path):
+ self.logger.error('Could not locate webroot directory: %s', self.webroot_path)
+ sys.exit(1)
+
+ def start(self):
+ self.logger.debug('Starting...')
+
+ self.server = ThreadedHTTPServer((self.local_ip, int(self.config.get('port'))), ThreadedHTTPRequestHandler)
+ self.server.logger = self.logger
+ self.server.config = self.config
+ self.server.webroot_path = self.webroot_path
+ self.server.extensions_map = self.extensions_map
+
+ if self.config.get('usessl') == 'Yes':
+ self.logger.debug('Using SSL socket.')
+
+ keyfile_path = 'privkey.pem'
+ if not os.path.exists(keyfile_path):
+ keyfile_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), keyfile_path)
+
+ if not os.path.exists(keyfile_path):
+ self.logger.error('Could not locate privkey.pem')
+ sys.exit(1)
+
+ certfile_path = 'server.pem'
+ if not os.path.exists(certfile_path):
+ certfile_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), certfile_path)
+
+ if not os.path.exists(certfile_path):
+ self.logger.error('Could not locate certfile.pem')
+ sys.exit(1)
+
+ self.server.socket = ssl.wrap_socket(self.server.socket, keyfile=keyfile_path, certfile=certfile_path, server_side=True, ciphers='RSA')
+
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+
+ def stop(self):
+ self.logger.info('Stopping...')
+ if self.server:
+ self.server.shutdown()
+ self.server.server_close()
+
+class ThreadedHTTPServer(BaseHTTPServer.HTTPServer):
+
+ def handle_error(self, request, client_address):
+ exctype, value = sys.exc_info()[:2]
+ self.logger.error('Error: %s', value)
+
+class ThreadedHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+ def __init__(self, *args):
+ BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args)
+
+ def setup(self):
+ self.request.settimeout(int(self.server.config.get('timeout', 10)))
+ BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
+
+ def do_HEAD(self):
+ self.server.logger.info('Received HEAD request')
+
+ # Process request
+ self.server.logger.info('%s', '-'*80)
+ self.server.logger.info(self.requestline)
+ for line in str(self.headers).split("\n"):
+ self.server.logger.info(line)
+ self.server.logger.info('%s', '-'*80)
+
+ # Prepare response
+ self.send_response(200)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+
+ def do_GET(self):
+
+ self.server.logger.info('Received a GET request.')
+
+ # Process request
+ self.server.logger.info('%s', '-'*80)
+ self.server.logger.info(self.requestline)
+ for line in str(self.headers).split("\n"):
+ self.server.logger.info(line)
+ self.server.logger.info('%s', '-'*80)
+
+ # Get response type based on the requested path
+ response, response_type = self.get_response(self.path)
+
+ # Prepare response
+ self.send_response(200)
+ self.send_header("Content-Type", response_type)
+ self.send_header("Content-Length", len(response))
+ self.end_headers()
+
+ self.wfile.write(response)
+
+ def do_POST(self):
+ self.server.logger.info('Received a POST request')
+
+ post_body = ''
+
+ content_len = int(self.headers.get('content-length', 0))
+ post_body = self.rfile.read(content_len)
+
+ # Process request
+ self.server.logger.info('%s', '-'*80)
+ self.server.logger.info(self.requestline)
+ for line in str(self.headers).split("\n"):
+ self.server.logger.info(line)
+ for line in post_body.split("\n"):
+ self.server.logger.info(line)
+ self.server.logger.info('%s', '-'*80)
+
+ # Store HTTP Posts
+ if self.server.config.get('dumphttpposts') and self.server.config['dumphttpposts'].lower() == 'yes':
+ http_filename = "%s_%s.txt" % (self.server.config.get('dumphttppostsfileprefix', 'http'), time.strftime("%Y%m%d_%H%M%S"))
+
+ self.server.logger.info('Storing HTTP POST headers and data to %s.', http_filename)
+ http_f = open(http_filename, 'wb')
+
+ if http_f:
+ http_f.write(self.requestline + "\r\n")
+ http_f.write(str(self.headers) + "\r\n")
+ http_f.write(post_body)
+
+ http_f.close()
+ else:
+ self.server.logger.error('Failed to write HTTP POST headers and data to %s.', http_filename)
+
+ # Get response type based on the requested path
+ response, response_type = self.get_response(self.path)
+
+ # Prepare response
+ self.send_response(200)
+ self.send_header("Content-Type", response_type)
+ self.send_header("Content-Length", len(response))
+ self.end_headers()
+
+ self.wfile.write(response)
+
+ def get_response(self, path):
+ response = "FakeNetFakeNet
"
+ response_type = 'text/html'
+
+ if path[-1] == '/':
+ response_type = 'text/html'
+ path += 'index.html'
+ else:
+ _, ext = posixpath.splitext(path)
+ response_type = self.server.extensions_map.get(ext, 'text/html')
+
+ response_filename = os.path.join(self.server.webroot_path, path[1:])
+
+ # Check the requested path exists
+ if not os.path.exists(response_filename):
+
+ self.server.logger.debug('Could not find path: %s', response_filename)
+
+ # Try default MIME file
+ response_filename = os.path.join(self.server.webroot_path, MIME_FILE_RESPONSE.get(response_type, 'FakeNet.html'))
+
+ # Check default MIME file exists
+ if not os.path.exists(response_filename):
+ self.server.logger.debug('Could not find path: %s', response_filename)
+ self.server.logger.error('Could not locate requested file or default handler.')
+ return (response, response_type)
+
+ self.server.logger.info('Responding with mime type: %s file: %s', response_type, response_filename)
+
+ try:
+ f = open(response_filename, 'rb')
+ except Exception, e:
+ self.server.logger.error('Failed to open response file: %s', response_filename)
+ response_type = 'text/html'
+ else:
+ response = f.read()
+ f.close()
+
+ return (response, response_type)
+
+ def log_message(self, format, *args):
+ return
+
+
+###############################################################################
+# Testing code
+def test(config):
+
+ import requests
+
+ url = "%s://localhost:%s" % ('http' if config.get('usessl') == 'No' else 'https', int(config.get('port', 8080)))
+
+ print "\t[HTTPListener] Testing HEAD request."
+ print '-'*80
+ print requests.head(url, verify=False, stream=True).text
+ print '-'*80
+
+ print "\t[HTTPListener] Testing GET request."
+ print '-'*80
+ print requests.get(url, verify=False, stream=True).text
+ print '-'*80
+
+ print "\t[HTTPListener] Testing POST request."
+ print '-'*80
+ print requests.post(url, {'param1':'A'*80, 'param2':'B'*80}, verify=False, stream=True).text
+ print '-'*80
+
+def main():
+ logging.basicConfig(format='%(asctime)s [%(name)15s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.DEBUG)
+
+ config = {'port': '8443', 'usessl': 'Yes', 'webroot': '../defaultFiles' }
+
+ listener = HTTPListener(config)
+ listener.start()
+
+ ###########################################################################
+ # Run processing
+ import time
+
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
+
+ ###########################################################################
+ # Run tests
+ test(config)
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/fakenet/listeners/HTTPListener.pyc b/fakenet/listeners/HTTPListener.pyc
new file mode 100644
index 0000000..d3fdb97
Binary files /dev/null and b/fakenet/listeners/HTTPListener.pyc differ
diff --git a/fakenet/listeners/RawListener.py b/fakenet/listeners/RawListener.py
new file mode 100644
index 0000000..0927cfa
--- /dev/null
+++ b/fakenet/listeners/RawListener.py
@@ -0,0 +1,199 @@
+import logging
+
+import os
+import sys
+
+import threading
+import SocketServer
+
+import ssl
+import socket
+
+class RawListener():
+
+ def __init__(self, config, name = 'RawListener', logging_level = logging.INFO):
+ self.logger = logging.getLogger(name)
+ self.logger.setLevel(logging_level)
+
+ self.config = config
+ self.name = name
+ self.local_ip = '0.0.0.0'
+ self.server = None
+
+ self.logger.info('Starting...')
+
+ self.logger.debug('Initialized with config:')
+ for key, value in config.iteritems():
+ self.logger.debug(' %10s: %s', key, value)
+
+ def start(self):
+
+ # Start listener
+ if self.config.get('protocol') != None:
+
+ if self.config['protocol'].lower() == 'tcp':
+ self.logger.debug('Starting TCP ...')
+ self.server = ThreadedTCPServer((self.local_ip, int(self.config['port'])), ThreadedTCPRequestHandler)
+
+ elif self.config['protocol'].lower() == 'udp':
+ self.logger.debug('Starting UDP ...')
+ self.server = ThreadedUDPServer((self.local_ip, int(self.config['port'])), ThreadedUDPRequestHandler)
+
+ else:
+ self.logger.error('Unknown protocol %s', self.config['protocol'])
+ return
+ else:
+ self.logger.error('Protocol is not defined.')
+ return
+
+ self.server.logger = self.logger
+ self.server.config = self.config
+
+ if self.config.get('usessl') == 'Yes':
+ self.logger.debug('Using SSL socket.')
+
+ keyfile_path = 'privkey.pem'
+ if not os.path.exists(keyfile_path):
+ keyfile_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), keyfile_path)
+
+ if not os.path.exists(keyfile_path):
+ self.logger.error('Could not locate privkey.pem')
+ sys.exit(1)
+
+ certfile_path = 'server.pem'
+ if not os.path.exists(certfile_path):
+ certfile_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), certfile_path)
+
+ if not os.path.exists(certfile_path):
+ self.logger.error('Could not locate certfile.pem')
+ sys.exit(1)
+
+ self.server.socket = ssl.wrap_socket(self.server.socket, keyfile=keyfile_path, certfile=certfile_path, server_side=True, ciphers='RSA')
+
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+
+ def stop(self):
+ self.logger.debug('Stopping...')
+ if self.server:
+ self.server.shutdown()
+ self.server.server_close()
+
+class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
+
+ def handle(self):
+
+ # Timeout connection to prevent hanging
+ self.request.settimeout(int(self.server.config.get('timeout', 5)))
+
+ try:
+
+ while True:
+
+ data = self.request.recv(1024)
+
+ if not data:
+ break
+
+ self.server.logger.info('Received %d bytes.', len(data))
+ self.server.logger.info('%s', '-'*80)
+ for line in hexdump_table(data):
+ self.server.logger.info(line)
+ self.server.logger.info('%s', '-'*80,)
+
+ self.request.sendall(data)
+
+ except socket.timeout:
+ self.server.logger.warning('Connection timeout')
+
+ except socket.error as msg:
+ self.server.logger.error('Error: %s', msg.strerror or msg)
+
+ except Exception, e:
+ self.server.logger.error('Error: %s', e)
+
+class ThreadedUDPRequestHandler(SocketServer.BaseRequestHandler):
+
+ def handle(self):
+
+ try:
+ (data,socket) = self.request
+
+ if not data:
+ return
+
+ self.server.logger.info('Received %d bytes.', len(data))
+ self.server.logger.debug('%s', '-'*80,)
+ for line in hexdump_table(data):
+ self.server.logger.debug(line)
+ self.server.logger.debug('%s', '-'*80,)
+
+ socket.sendto(data, self.client_address)
+
+ except socket.error as msg:
+ self.server.logger.error('Error: %s', msg.strerror or msg)
+
+ except Exception, e:
+ self.server.logger.error('Error: %s', e)
+
+class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+ pass
+
+class ThreadedUDPServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
+ pass
+
+def hexdump_table(data, length=16):
+
+ hexdump_lines = []
+ for i in range(0, len(data), 16):
+ chunk = data[i:i+16]
+ hex_line = ' '.join(["%02X" % ord(b) for b in chunk ] )
+ ascii_line = ''.join([b if ord(b) > 31 and ord(b) < 127 else '.' for b in chunk ] )
+ hexdump_lines.append("%04X: %-*s %s" % (i, length*3, hex_line, ascii_line ))
+ return hexdump_lines
+
+###############################################################################
+# Testing code
+def test(config):
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ print "\t[RawListener] Sending request:\n%s" % "HELO\n"
+ try:
+ # Connect to server and send data
+ sock.connect(('localhost', int(config.get('port', 23))))
+ sock.sendall("HELO\n")
+
+ # Receive data from the server and shut down
+ received = sock.recv(1024)
+ finally:
+ sock.close()
+
+def main():
+ logging.basicConfig(format='%(asctime)s [%(name)15s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.DEBUG)
+
+ config = {'port': '1337', 'usessl': 'No', 'protocol': 'tcp'}
+
+ listener = RawListener(config)
+ listener.start()
+
+
+ ###########################################################################
+ # Run processing
+ import time
+
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
+
+ ###########################################################################
+ # Run tests
+ #test(config)
+
+ listener.stop()
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/fakenet/listeners/RawListener.pyc b/fakenet/listeners/RawListener.pyc
new file mode 100644
index 0000000..4409d20
Binary files /dev/null and b/fakenet/listeners/RawListener.pyc differ
diff --git a/fakenet/listeners/SMTPListener.py b/fakenet/listeners/SMTPListener.py
new file mode 100644
index 0000000..7ada194
--- /dev/null
+++ b/fakenet/listeners/SMTPListener.py
@@ -0,0 +1,176 @@
+import logging
+
+import sys
+import os
+
+import threading
+import SocketServer
+
+import ssl
+import socket
+
+class SMTPListener():
+
+ def __init__(self, config, name = 'SMTPListener', logging_level = logging.INFO):
+ self.logger = logging.getLogger(name)
+ self.logger.setLevel(logging_level)
+
+ self.config = config
+ self.name = name
+ self.local_ip = '0.0.0.0'
+ self.server = None
+
+ self.logger.info('Starting...')
+
+ self.logger.debug('Initialized with config:')
+ for key, value in config.iteritems():
+ self.logger.debug(' %10s: %s', key, value)
+
+ def start(self):
+ self.logger.debug('Starting...')
+
+ self.server = ThreadedTCPServer((self.local_ip, int(self.config['port'])), ThreadedTCPRequestHandler)
+
+ if self.config.get('usessl') == 'Yes':
+ self.logger.debug('Using SSL socket')
+
+ keyfile_path = 'privkey.pem'
+ if not os.path.exists(keyfile_path):
+ keyfile_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), keyfile_path)
+
+ if not os.path.exists(keyfile_path):
+ self.logger.error('Could not locate privkey.pem')
+ sys.exit(1)
+
+ certfile_path = 'server.pem'
+ if not os.path.exists(certfile_path):
+ certfile_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), certfile_path)
+
+ if not os.path.exists(certfile_path):
+ self.logger.error('Could not locate certfile.pem')
+ sys.exit(1)
+
+ self.server.socket = ssl.wrap_socket(self.server.socket, keyfile='privkey.pem', certfile='server.pem', server_side=True, ciphers='RSA')
+
+ self.server.logger = self.logger
+ self.server.config = self.config
+
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+ self.server_thread.start()
+
+ def stop(self):
+ self.logger.info('Stopping...')
+ if self.server:
+ self.server.shutdown()
+ self.server.server_close()
+
+class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
+
+ def handle(self):
+
+ # Timeout connection to prevent hanging
+ self.request.settimeout(int(self.server.config.get('timeout', 5)))
+
+ try:
+
+ self.request.sendall("220 PracticalMalwareAnalysis.COM STMP Service Ready\r\n")
+ while True:
+ data = self.request.recv(4096)
+ self.server.logger.info('Recieved Data.')
+ for line in data.split("\n"):
+ self.server.logger.debug(line)
+
+ command = data[:4].upper()
+
+ if command == '':
+ break
+
+ elif command == 'HELO':
+ self.request.sendall("250 PracticalMalwareAnalysis.com\r\n")
+
+ elif command in ['MAIL', 'RCPT', 'NOOP', 'RSET']:
+ self.request.sendall("250 OK\r\n")
+
+ elif command == 'QUIT':
+ self.request.sendall("221 PracticalMalwareAnalysis.com bye\r\n")
+
+ elif command == "DATA":
+ self.request.sendall("354 start mail input, end with .\r\n")
+
+ mail_data = ""
+ while True:
+ mail_data_chunk = self.request.recv(4096)
+
+ if not mail_data_chunk:
+ break
+
+ mail_data += mail_data_chunk
+
+ if "\r\n.\r\n" in mail_data:
+ break
+
+ self.server.logger.info('Received mail data.')
+ for line in mail_data.split("\n"):
+ self.server.logger.debug(line)
+
+ self.request.sendall("250 OK\r\n")
+
+ else:
+ self.request.sendall("503 Command not supported\r\n")
+
+ except socket.timeout:
+ self.server.logger.warning('Connection timeout')
+
+ except socket.error as msg:
+ self.server.logger.error('Error: %s', msg.strerror or msg)
+
+ except Exception, e:
+ self.server.logger.error('Error: %s', e)
+
+class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+ pass
+
+###############################################################################
+# Testing code
+def test(config):
+
+ import smtplib
+
+ logger = logging.getLogger('SMTPListenerTest')
+
+ server = smtplib.SMTP_SSL('localhost', config.get('port', 25))
+
+ message = "From: test@test.com\r\nTo: test@test.com\r\n\r\nTest message\r\n"
+
+ logger.info('Testing email request.')
+ logger.info('-'*80)
+ server.set_debuglevel(1)
+ server.sendmail('test@test.com','test@test.com', message)
+ server.quit()
+ logger.info('-'*80)
+
+def main():
+ logging.basicConfig(format='%(asctime)s [%(name)15s] %(message)s', datefmt='%m/%d/%y %I:%M:%S %p', level=logging.DEBUG)
+
+ config = {'port': '25', 'usessl': 'Yes', 'timeout': 10 }
+
+ listener = SMTPListener(config)
+ listener.start()
+
+ ###########################################################################
+ # Run processing
+ import time
+
+ try:
+ while True:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ pass
+
+ ###########################################################################
+ # Run tests
+ test(config)
+
+if __name__ == '__main__':
+ main()
diff --git a/fakenet/listeners/SMTPListener.pyc b/fakenet/listeners/SMTPListener.pyc
new file mode 100644
index 0000000..a078132
Binary files /dev/null and b/fakenet/listeners/SMTPListener.pyc differ
diff --git a/fakenet/listeners/__init__.py b/fakenet/listeners/__init__.py
new file mode 100644
index 0000000..a4458cc
--- /dev/null
+++ b/fakenet/listeners/__init__.py
@@ -0,0 +1,7 @@
+import RawListener
+import HTTPListener
+import DNSListener
+import SMTPListener
+
+__all__ = ['RawListener', 'HTTPListener', 'DNSListener', 'SMTPListener']
+
diff --git a/fakenet/listeners/__init__.pyc b/fakenet/listeners/__init__.pyc
new file mode 100644
index 0000000..50c46a1
Binary files /dev/null and b/fakenet/listeners/__init__.pyc differ
diff --git a/fakenet/listeners/privkey.pem b/fakenet/listeners/privkey.pem
new file mode 100644
index 0000000..1636941
--- /dev/null
+++ b/fakenet/listeners/privkey.pem
@@ -0,0 +1,47 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDzcX3h/MmqeYoj
+t3qVhgcV8NwApbBYr6iLmmx384ayykau/0lfcN2NGovofwMSGyqFom42d6GgFLEJ
+uaPiQ//RZK6NQl2w1X2wP/Un4SET5Id4sMBShR2zS/5H7GgSlgqwUqKegnel348b
+/A2YcdAd4iQ97yBxINxbkciN/d8L2omI4etreD/yghqX5WN2UH+NoSg7FdpvgBIh
+aMKsqC9vEqbgXaIFr3jDJVjyEy4wBfvFKTgEAIvHhE2B6B1/ij3eJk0ZKpub3Du2
+gekZTpqajQL4viE1wjvydjJq/ixL2dr3q3+Ko/4xl0bLU8r6+/0CkkFnMxMP2+mp
+4xVT9uM3AgMBAAECggEBAJpM7hAGDMCbxp361pzdVbJndtqGKm8b74WEvImO9mpu
+YTzcHGJ9BEBCejlD/+tDAsGvAZJOY0g2tTvHyYNJvGS5HxXz4bSKrN7Aux+qxy93
+oxIxXcUwEHIrkaF+yzw0k9PMnLxBT5r4Rxniua9NPC8kvfnvji2GOYVksqylI/G7
+NwBLrYjFOJLXqikVZveyG0uQPXxwtRwaVgDSU7QX8kxXfpwsaZGdnm7yk80hrNte
+U4U/A7kv63tE0+qFu9jQVatYwcGAR7kNr9qsFOHvxPbEQkvS6yOUHMdNF8ZpwFbA
+vv6O1pt4jV7kAo5TLFoGXgnONBifzZCTD127laH1j1kCgYEA/Te6CsWbZm/6pPFJ
+QpVyzDlSam7hCb5rmOneoOeIjo2CLn22DEeu9ZqETj2xLMfA0diV1r23Eia+kHwO
+3XdNKLg3/Ue22FpQuV4iCqxDXkwdM/hMeyeLssQmNlGE0+iBaXL8UZCu5zWnw2TD
+pkjeS6KGPCMN1MvYdhGIzYxt/zUCgYEA9h5FVLxBA0s40jgILpPvLbIF84cVDF46
+xnFs7c7jQSH+DYK9C7GFYKyDw3UTI5+UtwalxsBYWSLZuTSzdpUocKzT31TmbY2M
+nOYb3s7VHrJUXWnCl5m6EW1oqRUVMZuseECg7IABWUaI8qUQAbU2JAD1VeoslpVo
+s7C9TzuxCjsCgYEA395l3+Im6uDzkuIz5+cjEEVZhPm0gZ3VmOKjTlSFGasoPhws
+WB0EJOXQNTA7tSCBa8V1a95cvXJ9plXX+prgH7EG5ymBETSAC/KaXB9CjFr0sp7C
+V1t2Gb1rHzjhG0yDJYxgYWhuCcIZKRmsFBZ2Wh49WWuQbeMu4+vKrBeMpEUCgYEA
+4+Wh01XCaZdk1Ru3T9ICHnEDG75QmjRC/nAXKplxS2V7hu0xujs2Qw9br0igYVMq
+dNP9+20uIdOogduv9jUlzjfqtJk8CeORedOFqSoipBsDDcCZxKE/b1W9HRv9lQMO
+kAdgO75IAW/T7cM0cDBpIquUgWqmwQU2f3U/xreCNvMCfxQQiFTTSyhnZu195hjJ
+fgNZ3SODPMkQoh8Pah1E5Hb83umGBPvt4MN2bfm8wWmLEjpU93xiJa2uN8Jzx5Mb
+K3hs4JcPtjJ2m2x0aDt1RGFhllEl0l5tOQg+cKy9jnqA/O1wmMOXsFgYS5Fhg1Me
+o70kQ6YRH+pBgnP7Pgcu4X0=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIC+zCCAeOgAwIBAgIJAPHrk7xP1wNXMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
+BAMMCWxvY2FsaG9zdDAeFw0xNjAxMjEyMjM3MDdaFw0xNjAyMjAyMjM3MDdaMBQx
+EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPNxfeH8yap5iiO3epWGBxXw3AClsFivqIuabHfzhrLKRq7/SV9w3Y0ai+h/
+AxIbKoWibjZ3oaAUsQm5o+JD/9Fkro1CXbDVfbA/9SfhIRPkh3iwwFKFHbNL/kfs
+aBKWCrBSop6Cd6Xfjxv8DZhx0B3iJD3vIHEg3FuRyI393wvaiYjh62t4P/KCGpfl
+Y3ZQf42hKDsV2m+AEiFowqyoL28SpuBdogWveMMlWPITLjAF+8UpOAQAi8eETYHo
+HX+KPd4mTRkqm5vcO7aB6RlOmpqNAvi+ITXCO/J2Mmr+LEvZ2verf4qj/jGXRstT
+yvr7/QKSQWczEw/b6anjFVP24zcCAwEAAaNQME4wHQYDVR0OBBYEFHB5zTo/9cg+
+8kTzB3WDwATror8YMB8GA1UdIwQYMBaAFHB5zTo/9cg+8kTzB3WDwATror8YMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACH3jlYfLBR0kGALkJSq9tRI
+fB6sjjVOFrZtCt5sOqxh9We3wvMsr2TPAuEVEhiPQ1w1U4x4DTQU6A65N1KOj9Le
+anjjlyjNSTqXvNUbxp0wh8x4LJ8DtmsfdXXYp3LsColBmh7JQn/2TL687B9tBXiq
+p3P6wkuIOHX45UOQ0kvD4wRf3t+8mL4TmrCH4YHqzbcjI1KXGDfmVQ7i64nYrl3z
+v7UCA6Xh0MShv3c0AtX1ccsxObEwjJzWA30zYUQtIWxXpRtLlkiflFU8ak1HzmBU
+AHsg1T1KI1012+a1j/LWkKqb91EpUjC/DlaHz8zDOH/S1IEWM6UFjwKYnHLPxoM=
+-----END CERTIFICATE-----
+
diff --git a/fakenet/listeners/server.pem b/fakenet/listeners/server.pem
new file mode 100644
index 0000000..aff78bd
--- /dev/null
+++ b/fakenet/listeners/server.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+zCCAeOgAwIBAgIJAPHrk7xP1wNXMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
+BAMMCWxvY2FsaG9zdDAeFw0xNjAxMjEyMjM3MDdaFw0xNjAyMjAyMjM3MDdaMBQx
+EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPNxfeH8yap5iiO3epWGBxXw3AClsFivqIuabHfzhrLKRq7/SV9w3Y0ai+h/
+AxIbKoWibjZ3oaAUsQm5o+JD/9Fkro1CXbDVfbA/9SfhIRPkh3iwwFKFHbNL/kfs
+aBKWCrBSop6Cd6Xfjxv8DZhx0B3iJD3vIHEg3FuRyI393wvaiYjh62t4P/KCGpfl
+Y3ZQf42hKDsV2m+AEiFowqyoL28SpuBdogWveMMlWPITLjAF+8UpOAQAi8eETYHo
+HX+KPd4mTRkqm5vcO7aB6RlOmpqNAvi+ITXCO/J2Mmr+LEvZ2verf4qj/jGXRstT
+yvr7/QKSQWczEw/b6anjFVP24zcCAwEAAaNQME4wHQYDVR0OBBYEFHB5zTo/9cg+
+8kTzB3WDwATror8YMB8GA1UdIwQYMBaAFHB5zTo/9cg+8kTzB3WDwATror8YMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACH3jlYfLBR0kGALkJSq9tRI
+fB6sjjVOFrZtCt5sOqxh9We3wvMsr2TPAuEVEhiPQ1w1U4x4DTQU6A65N1KOj9Le
+anjjlyjNSTqXvNUbxp0wh8x4LJ8DtmsfdXXYp3LsColBmh7JQn/2TL687B9tBXiq
+p3P6wkuIOHX45UOQ0kvD4wRf3t+8mL4TmrCH4YHqzbcjI1KXGDfmVQ7i64nYrl3z
+v7UCA6Xh0MShv3c0AtX1ccsxObEwjJzWA30zYUQtIWxXpRtLlkiflFU8ak1HzmBU
+AHsg1T1KI1012+a1j/LWkKqb91EpUjC/DlaHz8zDOH/S1IEWM6UFjwKYnHLPxoM=
+-----END CERTIFICATE-----
diff --git a/fakenet/privkey.pem b/fakenet/privkey.pem
new file mode 100644
index 0000000..1636941
--- /dev/null
+++ b/fakenet/privkey.pem
@@ -0,0 +1,47 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDzcX3h/MmqeYoj
+t3qVhgcV8NwApbBYr6iLmmx384ayykau/0lfcN2NGovofwMSGyqFom42d6GgFLEJ
+uaPiQ//RZK6NQl2w1X2wP/Un4SET5Id4sMBShR2zS/5H7GgSlgqwUqKegnel348b
+/A2YcdAd4iQ97yBxINxbkciN/d8L2omI4etreD/yghqX5WN2UH+NoSg7FdpvgBIh
+aMKsqC9vEqbgXaIFr3jDJVjyEy4wBfvFKTgEAIvHhE2B6B1/ij3eJk0ZKpub3Du2
+gekZTpqajQL4viE1wjvydjJq/ixL2dr3q3+Ko/4xl0bLU8r6+/0CkkFnMxMP2+mp
+4xVT9uM3AgMBAAECggEBAJpM7hAGDMCbxp361pzdVbJndtqGKm8b74WEvImO9mpu
+YTzcHGJ9BEBCejlD/+tDAsGvAZJOY0g2tTvHyYNJvGS5HxXz4bSKrN7Aux+qxy93
+oxIxXcUwEHIrkaF+yzw0k9PMnLxBT5r4Rxniua9NPC8kvfnvji2GOYVksqylI/G7
+NwBLrYjFOJLXqikVZveyG0uQPXxwtRwaVgDSU7QX8kxXfpwsaZGdnm7yk80hrNte
+U4U/A7kv63tE0+qFu9jQVatYwcGAR7kNr9qsFOHvxPbEQkvS6yOUHMdNF8ZpwFbA
+vv6O1pt4jV7kAo5TLFoGXgnONBifzZCTD127laH1j1kCgYEA/Te6CsWbZm/6pPFJ
+QpVyzDlSam7hCb5rmOneoOeIjo2CLn22DEeu9ZqETj2xLMfA0diV1r23Eia+kHwO
+3XdNKLg3/Ue22FpQuV4iCqxDXkwdM/hMeyeLssQmNlGE0+iBaXL8UZCu5zWnw2TD
+pkjeS6KGPCMN1MvYdhGIzYxt/zUCgYEA9h5FVLxBA0s40jgILpPvLbIF84cVDF46
+xnFs7c7jQSH+DYK9C7GFYKyDw3UTI5+UtwalxsBYWSLZuTSzdpUocKzT31TmbY2M
+nOYb3s7VHrJUXWnCl5m6EW1oqRUVMZuseECg7IABWUaI8qUQAbU2JAD1VeoslpVo
+s7C9TzuxCjsCgYEA395l3+Im6uDzkuIz5+cjEEVZhPm0gZ3VmOKjTlSFGasoPhws
+WB0EJOXQNTA7tSCBa8V1a95cvXJ9plXX+prgH7EG5ymBETSAC/KaXB9CjFr0sp7C
+V1t2Gb1rHzjhG0yDJYxgYWhuCcIZKRmsFBZ2Wh49WWuQbeMu4+vKrBeMpEUCgYEA
+4+Wh01XCaZdk1Ru3T9ICHnEDG75QmjRC/nAXKplxS2V7hu0xujs2Qw9br0igYVMq
+dNP9+20uIdOogduv9jUlzjfqtJk8CeORedOFqSoipBsDDcCZxKE/b1W9HRv9lQMO
+kAdgO75IAW/T7cM0cDBpIquUgWqmwQU2f3U/xreCNvMCfxQQiFTTSyhnZu195hjJ
+fgNZ3SODPMkQoh8Pah1E5Hb83umGBPvt4MN2bfm8wWmLEjpU93xiJa2uN8Jzx5Mb
+K3hs4JcPtjJ2m2x0aDt1RGFhllEl0l5tOQg+cKy9jnqA/O1wmMOXsFgYS5Fhg1Me
+o70kQ6YRH+pBgnP7Pgcu4X0=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIC+zCCAeOgAwIBAgIJAPHrk7xP1wNXMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
+BAMMCWxvY2FsaG9zdDAeFw0xNjAxMjEyMjM3MDdaFw0xNjAyMjAyMjM3MDdaMBQx
+EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPNxfeH8yap5iiO3epWGBxXw3AClsFivqIuabHfzhrLKRq7/SV9w3Y0ai+h/
+AxIbKoWibjZ3oaAUsQm5o+JD/9Fkro1CXbDVfbA/9SfhIRPkh3iwwFKFHbNL/kfs
+aBKWCrBSop6Cd6Xfjxv8DZhx0B3iJD3vIHEg3FuRyI393wvaiYjh62t4P/KCGpfl
+Y3ZQf42hKDsV2m+AEiFowqyoL28SpuBdogWveMMlWPITLjAF+8UpOAQAi8eETYHo
+HX+KPd4mTRkqm5vcO7aB6RlOmpqNAvi+ITXCO/J2Mmr+LEvZ2verf4qj/jGXRstT
+yvr7/QKSQWczEw/b6anjFVP24zcCAwEAAaNQME4wHQYDVR0OBBYEFHB5zTo/9cg+
+8kTzB3WDwATror8YMB8GA1UdIwQYMBaAFHB5zTo/9cg+8kTzB3WDwATror8YMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACH3jlYfLBR0kGALkJSq9tRI
+fB6sjjVOFrZtCt5sOqxh9We3wvMsr2TPAuEVEhiPQ1w1U4x4DTQU6A65N1KOj9Le
+anjjlyjNSTqXvNUbxp0wh8x4LJ8DtmsfdXXYp3LsColBmh7JQn/2TL687B9tBXiq
+p3P6wkuIOHX45UOQ0kvD4wRf3t+8mL4TmrCH4YHqzbcjI1KXGDfmVQ7i64nYrl3z
+v7UCA6Xh0MShv3c0AtX1ccsxObEwjJzWA30zYUQtIWxXpRtLlkiflFU8ak1HzmBU
+AHsg1T1KI1012+a1j/LWkKqb91EpUjC/DlaHz8zDOH/S1IEWM6UFjwKYnHLPxoM=
+-----END CERTIFICATE-----
+
diff --git a/fakenet/server.pem b/fakenet/server.pem
new file mode 100644
index 0000000..aff78bd
--- /dev/null
+++ b/fakenet/server.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+zCCAeOgAwIBAgIJAPHrk7xP1wNXMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
+BAMMCWxvY2FsaG9zdDAeFw0xNjAxMjEyMjM3MDdaFw0xNjAyMjAyMjM3MDdaMBQx
+EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPNxfeH8yap5iiO3epWGBxXw3AClsFivqIuabHfzhrLKRq7/SV9w3Y0ai+h/
+AxIbKoWibjZ3oaAUsQm5o+JD/9Fkro1CXbDVfbA/9SfhIRPkh3iwwFKFHbNL/kfs
+aBKWCrBSop6Cd6Xfjxv8DZhx0B3iJD3vIHEg3FuRyI393wvaiYjh62t4P/KCGpfl
+Y3ZQf42hKDsV2m+AEiFowqyoL28SpuBdogWveMMlWPITLjAF+8UpOAQAi8eETYHo
+HX+KPd4mTRkqm5vcO7aB6RlOmpqNAvi+ITXCO/J2Mmr+LEvZ2verf4qj/jGXRstT
+yvr7/QKSQWczEw/b6anjFVP24zcCAwEAAaNQME4wHQYDVR0OBBYEFHB5zTo/9cg+
+8kTzB3WDwATror8YMB8GA1UdIwQYMBaAFHB5zTo/9cg+8kTzB3WDwATror8YMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACH3jlYfLBR0kGALkJSq9tRI
+fB6sjjVOFrZtCt5sOqxh9We3wvMsr2TPAuEVEhiPQ1w1U4x4DTQU6A65N1KOj9Le
+anjjlyjNSTqXvNUbxp0wh8x4LJ8DtmsfdXXYp3LsColBmh7JQn/2TL687B9tBXiq
+p3P6wkuIOHX45UOQ0kvD4wRf3t+8mL4TmrCH4YHqzbcjI1KXGDfmVQ7i64nYrl3z
+v7UCA6Xh0MShv3c0AtX1ccsxObEwjJzWA30zYUQtIWxXpRtLlkiflFU8ak1HzmBU
+AHsg1T1KI1012+a1j/LWkKqb91EpUjC/DlaHz8zDOH/S1IEWM6UFjwKYnHLPxoM=
+-----END CERTIFICATE-----
diff --git a/resources/fakenet.ico b/resources/fakenet.ico
new file mode 100644
index 0000000..8431d83
Binary files /dev/null and b/resources/fakenet.ico differ
diff --git a/resources/fakenet.png b/resources/fakenet.png
new file mode 100644
index 0000000..5b9c52b
Binary files /dev/null and b/resources/fakenet.png differ
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..07e2386
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,42 @@
+import os
+
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+
+requirements = [
+ "pydivert",
+ "dpkt",
+ "dnslib",
+]
+
+setup(
+ name='FakeNet NG',
+ version='1.0',
+ description="",
+ long_description="",
+ author="Peter Kacherginsky",
+ author_email='peter.kacherginsky@fireeye.com',
+ url='https://www.github.com/fireeye/flare-fakenet-ng',
+ packages=[
+ 'fakenet',
+ ],
+ package_dir={'fakenet': 'fakenet'},
+ package_data={'fakenet': ['*.pem','diverters/*.py', 'listeners/*.py', 'configs/*.ini', 'defaultFiles/*', 'lib/64/*', 'lib/32/*']},
+ entry_points={
+ "console_scripts": [
+ "fakenet=fakenet.fakenet:main",
+ ]
+ },
+ include_package_data=True,
+ install_requires=requirements,
+ zip_safe=False,
+ keywords='fakenet-ng',
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'Natural Language :: English',
+ "Programming Language :: Python :: 2",
+ ],
+)
\ No newline at end of file