【Windows逆向】Windows进程动态patch入门+pyqt5搭建GUI

news2024/11/25 20:45:03

目标

希望学习对Windows进程的动态patch,我们选择的exe是buuoj的“不一样的flag”。这题是迷宫题的hello world,没有加壳,没有任何代码混淆,且可以把它当成一个超小型的游戏,有助于提升信心

为了直观,以GUI的形式提供对目标进程的动态patch功能:

1.未检测到目标进程,相关的输入控件不可使用。
2.检测到目标进程时,读取目标进程内存中的数据,显示目标进程信息、迷宫和当前人物的坐标。
3.检测到目标进程时,提供“允许在障碍物上”、“允许不合法输入”和修改人物坐标功能(这些需求是根据其反汇编代码确定的)。

作者:hans774882968以及hans774882968

本文代码已在GitHub开源

本文juejin

效果图:

检测到进程:

未检测到进程:

依赖

  • spy++:<visual studio安装路径>\2019\Community\Common7\Tools\spyxx_amd64.exe
  • 用pyqt5提供的qtdesigner来设计GUI。
  • 用pyinstaller打包成exe。
  • pywin32和pyqt5:
pip install pywin32
pip install PyQt5
pip install PyQt5-tools
pip install pyinstaller 

pyqt5安装完毕后可以把<python安装路径>\Lib\site-packages\pyqt5_tools加到环境变量上。

不想用visual studio+cpp,太臃肿了。所以选择了pywin32。

分析

打开IDA,立刻得到反汇编代码。

void main()
{char v0; // [sp+17h] [bp-35h]@1int v1; // [sp+30h] [bp-1Ch]@1int v2; // [sp+34h] [bp-18h]@1signed int v3; // [sp+38h] [bp-14h]@2signed int i; // [sp+3Ch] [bp-10h]@14int v5; // [sp+40h] [bp-Ch]@20__main();v1 = 0;v2 = 0;qmemcpy(&v0, _data_start__, 0x19u);while ( 1 ){puts("you can choose one action to execute");puts("1 up");puts("2 down");puts("3 left");printf("4 right\n:");scanf("%d", &v3);if ( v3 == 2 ){++v1;}else if ( v3 > 2 ){if ( v3 == 3 ){--v2;}else{if ( v3 != 4 )
LABEL_13:exit(1);++v2;}}else{if ( v3 != 1 )goto LABEL_13;--v1;}for ( i = 0; i <= 1; ++i ){if ( *(&v1 + i) < 0 || *(&v1 + i) > 4 )exit(1);}if ( *((_BYTE *)&v5 + 5 * v1 + v2 - 41) == '1' )exit(1);if ( *((_BYTE *)&v5 + 5 * v1 + v2 - 41) == '#' ){puts("\nok, the order you enter is the flag!");exit(0);}}
} 

这道迷宫题很简单,只说结论:通过patch

if ( *((_BYTE *)&v5 + 5 * v1 + v2 - 41) == '1' ) exit(1); 

实现“允许在障碍物上”。通过patch

if ( v3 != 4 )
LABEL_13:exit(1);
if ( v3 != 1 ) goto LABEL_13; 

实现“允许不合法输入”。

GUI

打开<python安装路径>\Lib\site-packages\qt5_applications\Qt\bin下的designer.exe。拖完ui后用pyuic把.ui文件(实质上是xml文件)转为.py

pyuic5 -o destination.py source.ui 

其中:

  • -o表示生成一个文件。
  • destination.py是要生成的.py文件。

见“代码”章节的different_flag_ui.py

Windows进程动态patch

监控进程

因为还需要响应UI,我们需要开一个子线程,pyqt5提供的解决方案是QThread+pyqtSignal。子线程的伪代码:

while True:监控进程是否存在,然后self.某signal.emit()向主线程发数据time.sleep(1) 
  • 子线程不要更新任何ui,ui和业务逻辑要分开。
  • pyqtSignal的int对应c语言的signed int,试出bug了才知道的。

可以用手工输入pid的方式来监控进程。但这里我们选择了另一种方式:先win32gui.FindWindow(wanted_window_classname, wanted_window_title)获取hWnd,然后去拿pidclassnametitle可以用visual studio附带的spy++工具来拿。

读取任意个字节

ctypes.create_string_buffer()创建字符串,然后kernel32.ReadProcessMemory读取。读取到的value是bytes类型。

kernel32,指kernel32.dll

kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")

def readMemStr(hProcess, address, buffLen):ReadProcessMemory = kernel32.ReadProcessMemoryp = ctypes.create_string_buffer(buffLen)ReadProcessMemory(int(hProcess),address,p,buffLen,None)return p.value 

写入任意个字节

网上大多数信息都是写入一个int的,怎样才能写入任意个字节呢?翻了很多乐色信息,终于翻到了参考链接1。用ctypes.c_char_p(bytes类型的数据),传入kernel32.WriteProcessMemory

# data: bytes
def writeMem(hProcess, address, data, buffLen):writeProcess = kernel32.WriteProcessMemorywriteProcess(int(hProcess),address,ctypes.c_char_p(data),buffLen,None)return data 

打包成exe

pyinstaller -F -w -i=assets/icon.ico main.py 

-i表示图标文件,图标只支持.ico

代码

different_flag_ui.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'different_flag_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.Do not edit this file unless you know what you are doing.

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):def setupUi(self, MainWindow):MainWindow.setObjectName("MainWindow")MainWindow.resize(800, 600)icon = QtGui.QIcon()icon.addPixmap(QtGui.QPixmap("assets/icon.png"),QtGui.QIcon.Normal,QtGui.QIcon.Off)MainWindow.setWindowIcon(icon)self.centralwidget = QtWidgets.QWidget(MainWindow)self.centralwidget.setObjectName("centralwidget")self.allowObstacle = QtWidgets.QCheckBox(self.centralwidget)self.allowObstacle.setGeometry(QtCore.QRect(30, 120, 151, 41))font = QtGui.QFont()font.setPointSize(14)self.allowObstacle.setFont(font)self.allowObstacle.setObjectName("allowObstacle")self.allowIllegalInput = QtWidgets.QCheckBox(self.centralwidget)self.allowIllegalInput.setGeometry(QtCore.QRect(30, 190, 151, 41))font = QtGui.QFont()font.setPointSize(14)self.allowIllegalInput.setFont(font)self.allowIllegalInput.setObjectName("allowIllegalInput")self.posLabel = QtWidgets.QLabel(self.centralwidget)self.posLabel.setGeometry(QtCore.QRect(30, 320, 351, 71))font = QtGui.QFont()font.setPointSize(14)self.posLabel.setFont(font)self.posLabel.setObjectName("posLabel")self.mazeTag = QtWidgets.QLabel(self.centralwidget)self.mazeTag.setGeometry(QtCore.QRect(480, 90, 81, 31))font = QtGui.QFont()font.setPointSize(14)self.mazeTag.setFont(font)self.mazeTag.setAlignment(QtCore.Qt.AlignCenter)self.mazeTag.setObjectName("mazeTag")self.mazeLabel = QtWidgets.QLabel(self.centralwidget)self.mazeLabel.setGeometry(QtCore.QRect(410, 140, 231, 101))font = QtGui.QFont()font.setPointSize(10)self.mazeLabel.setFont(font)self.mazeLabel.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)self.mazeLabel.setObjectName("mazeLabel")self.xLineEdit = QtWidgets.QLineEdit(self.centralwidget)self.xLineEdit.setGeometry(QtCore.QRect(460, 330, 121, 20))font = QtGui.QFont()font.setPointSize(10)self.xLineEdit.setFont(font)self.xLineEdit.setMaxLength(19)self.xLineEdit.setObjectName("xLineEdit")self.yLineEdit = QtWidgets.QLineEdit(self.centralwidget)self.yLineEdit.setGeometry(QtCore.QRect(460, 380, 121, 20))font = QtGui.QFont()font.setPointSize(10)self.yLineEdit.setFont(font)self.yLineEdit.setMaxLength(19)self.yLineEdit.setObjectName("yLineEdit")self.modifyPosTag = QtWidgets.QLabel(self.centralwidget)self.modifyPosTag.setGeometry(QtCore.QRect(450, 260, 141, 31))font = QtGui.QFont()font.setPointSize(14)self.modifyPosTag.setFont(font)self.modifyPosTag.setAlignment(QtCore.Qt.AlignCenter)self.modifyPosTag.setObjectName("modifyPosTag")self.processInfo = QtWidgets.QLabel(self.centralwidget)self.processInfo.setGeometry(QtCore.QRect(30, 30, 641, 41))font = QtGui.QFont()font.setPointSize(12)self.processInfo.setFont(font)self.processInfo.setObjectName("processInfo")self.modifyButton = QtWidgets.QPushButton(self.centralwidget)self.modifyButton.setGeometry(QtCore.QRect(460, 430, 121, 23))font = QtGui.QFont()font.setPointSize(10)self.modifyButton.setFont(font)self.modifyButton.setObjectName("modifyButton")MainWindow.setCentralWidget(self.centralwidget)self.menubar = QtWidgets.QMenuBar(MainWindow)self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))self.menubar.setObjectName("menubar")MainWindow.setMenuBar(self.menubar)self.statusbar = QtWidgets.QStatusBar(MainWindow)self.statusbar.setObjectName("statusbar")MainWindow.setStatusBar(self.statusbar)self.retranslateUi(MainWindow)QtCore.QMetaObject.connectSlotsByName(MainWindow)def retranslateUi(self, MainWindow):_translate = QtCore.QCoreApplication.translateMainWindow.setWindowTitle(_translate("MainWindow", "不一样的flag动态patch"))self.allowObstacle.setText(_translate("MainWindow", "允许在障碍物上"))self.allowIllegalInput.setText(_translate("MainWindow", "允许非法输入"))self.posLabel.setText(_translate("MainWindow", "未检测到“不一样的flag.exe”进程"))self.mazeTag.setText(_translate("MainWindow", "迷宫"))self.mazeLabel.setText(_translate("MainWindow","未检测到“不一样的flag.exe”进程"))self.xLineEdit.setPlaceholderText(_translate("MainWindow", "人物x坐标"))self.yLineEdit.setPlaceholderText(_translate("MainWindow", "人物y坐标"))self.modifyPosTag.setText(_translate("MainWindow", "修改人物坐标"))self.processInfo.setText(_translate("MainWindow","未检测到“不一样的flag.exe”进程"))self.modifyButton.setText(_translate("MainWindow", "确定")) 

trainer.py

from win32api import OpenProcess
from win32con import PROCESS_ALL_ACCESS
from win32process import GetWindowThreadProcessId
import ctypes

kernel32 = ctypes.windll.LoadLibrary("kernel32.dll")

def getProcessHandle(hWnd):# 获取窗口pidhpid, pid = GetWindowThreadProcessId(hWnd)# 获取进程句柄hProcess = OpenProcess(PROCESS_ALL_ACCESS, False, pid)return hProcess, pid

def readMemVal(hProcess, address, buffLen):ReadProcessMemory = kernel32.ReadProcessMemoryaddr = ctypes.c_ulong()ReadProcessMemory(int(hProcess),address,ctypes.byref(addr),buffLen,None)return addr.value

def readMemStr(hProcess, address, buffLen):ReadProcessMemory = kernel32.ReadProcessMemoryp = ctypes.create_string_buffer(buffLen)ReadProcessMemory(int(hProcess),address,p,buffLen,None)return p.value

def writeMemInt(hProcess, address, data):writeProcessInt = kernel32.WriteProcessMemorywriteProcessInt(int(hProcess),address,ctypes.byref(ctypes.c_ulong(data)),4,None)return data

# data: bytes
def writeMem(hProcess, address, data, buffLen):writeProcess = kernel32.WriteProcessMemorywriteProcess(int(hProcess),address,ctypes.c_char_p(data),buffLen,None)return data

# jmp = 0xeb, jnz = 0x75, jz = 0x74
def setAllowObstacle(hProcess, isChecked):# 判s[v1][v2] == '1'的跳转address = 0x40146fif isChecked:# jmp short loc_40147DwriteMem(hProcess, address, b"\xeb\x0c", 2)else:# jnz short loc_40147DwriteMem(hProcess, address, b"\x75\x0c", 2)val = readMemVal(hProcess, address, 3)print(hex(val))# 小端 75 0c c7

def setAllowIllegalInput(hProcess, isChecked):# 判定输入是否合法:4013CF和4013DBad1, ad2 = 0x4013CF, 0x4013DBif isChecked:# jmp short loc_4013DFwriteMem(hProcess, ad1, b"\xeb\x0e", 2)# jmp short loc_401400writeMem(hProcess, ad2, b"\xeb\x23", 2)else:# jz short loc_4013DFwriteMem(hProcess, ad1, b"\x74\x0e", 2)# jz short loc_401400writeMem(hProcess, ad2, b"\x74\x23", 2)val = readMemVal(hProcess, ad1, 3)print(hex(val))# 小端 74 0e ebval = readMemVal(hProcess, ad2, 3)print(hex(val))# 小端 74 23 eb

def modifyPos(x, y, hProcess):esp = 0x60FEB0xAddr = esp + 0x30yAddr = esp + 0x34writeMemInt(hProcess, xAddr, x)writeMemInt(hProcess, yAddr, y) 

main.py

from win32gui import FindWindow
import time
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtGui import QIntValidator
from PyQt5.QtCore import pyqtSignal, QThread
from different_flag_ui import Ui_MainWindow
from PyQt5 import QtCore
import sys
from trainer import *

LB, UB = -2147483648, 2147483646
wanted_window_classname = "ConsoleWindowClass"
wanted_window_title = r"不一样的flag.exe"

class MonitorThread(QThread):get_proc_signal = pyqtSignal(int, int, int)get_maze_signal = pyqtSignal(str)get_pos_signal = pyqtSignal(int, int)proc_exist_signal = pyqtSignal(bool)def __init__(self):super().__init__()def run(self):while True:hWnd = FindWindow(wanted_window_classname, wanted_window_title)if not hWnd:self.get_proc_signal.emit(0, 0, 0)self.get_maze_signal.emit("")self.get_pos_signal.emit(UB + 1, UB + 1)self.proc_exist_signal.emit(False)else:hProcess, pid = getProcessHandle(hWnd)self.get_proc_signal.emit(hWnd, pid, hProcess)# 读迷宫字符串bs = readMemStr(hProcess, 0x402000, 26)self.get_maze_signal.emit(bs.decode("utf-8", "ignore"))# 读人物坐标esp = 0x60FEB0xAddr = esp + 0x30yAddr = esp + 0x34x = readMemVal(hProcess, xAddr, 4)y = readMemVal(hProcess, yAddr, 4)self.get_pos_signal.emit(x, y)self.proc_exist_signal.emit(True)time.sleep(1)

class MainWindow(QMainWindow, Ui_MainWindow):def __init__(self, parent=None):super(MainWindow, self).__init__(parent)self.setupUi(self)self.hProcess = 0self.pid = 0self.allowObstacle.stateChanged.connect(self.allowObstacleChange)self.allowIllegalInput.stateChanged.connect(self.allowIllegalInputChange)self.modifyButton.clicked.connect(self.modifyPos)intValidator1 = QIntValidator(LB, UB)intValidator2 = QIntValidator(LB, UB)self.xLineEdit.setValidator(intValidator1)self.yLineEdit.setValidator(intValidator2)self.monitor_thread = MonitorThread()self.monitor_thread.get_proc_signal.connect(self.set_process_info)self.monitor_thread.get_maze_signal.connect(self.set_maze_label)self.monitor_thread.get_pos_signal.connect(self.set_pos_label)self.monitor_thread.proc_exist_signal.connect(self.set_ui_enabled_state)self.monitor_thread.start()def set_ui_enabled_state(self, fl):self.allowObstacle.setEnabled(fl)self.allowIllegalInput.setEnabled(fl)self.modifyButton.setEnabled(fl)if not fl:self.allowObstacle.setChecked(False)self.allowIllegalInput.setChecked(False)def set_process_info(self, hWnd, pid, hProcess):self.hProcess = hProcessself.pid = pidself.processInfo.setText("不一样的flag.exe " +("hWnd = %s,进程pid = %s" % (hex(hWnd), self.pid) if hWnd else "not found"))def set_maze_label(self, s):if not s:self.mazeLabel.setText("未检测到“不一样的flag.exe”进程")returnmaze_str = "\n".join([s[i * 5:i * 5 + 5] for i in range(5)])self.mazeLabel.setText(maze_str)def set_pos_label(self, x, y):if x == UB + 1:self.posLabel.setText("未检测到“不一样的flag.exe”进程")returnself.posLabel.setText("当前人物坐标:(%s,%s)" % (x, y))def allowObstacleChange(self):isChecked = self.allowObstacle.isChecked()# 进程不存在if not self.pid:returnprint("allowObstacle", isChecked, "pid = %s" % self.pid)setAllowObstacle(self.hProcess, isChecked)def allowIllegalInputChange(self):isChecked = self.allowIllegalInput.isChecked()# 进程不存在if not self.pid:returnprint("allowIllegalInput", isChecked, "pid = %s" % self.pid)setAllowIllegalInput(self.hProcess, isChecked)def modifyPos(self):try:x = int(self.xLineEdit.text())except BaseException:x = 0try:y = int(self.yLineEdit.text())except BaseException:y = 0modifyPos(x, y, self.hProcess)

if __name__ == "__main__":# 本机分辨率较高,使用以下语句即可让按钮大小符合期望QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)app = QApplication(sys.argv)x = MainWindow()x.show()sys.exit(app.exec_()) 

参考链接

1.www.coder.work/article/492…

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
视频配套资料&国内外网安书籍、文档&工具
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。
在这里插入图片描述
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/86457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【学习笔记】SAP固定资产的减值功能

SAP固定资产的减值功能 若固定资产出现减值迹象&#xff0c;也就是固定资产的可收回金额小于账面价值时&#xff0c;就要计提固定资产减值准备。 分录&#xff1a; 借&#xff1a;资产减值损失&#xff08;损益科目&#xff09; 贷&#xff1a;固定资产减值准备&#xff08;资…

软件测试 | APP测试 —— Appium 的环境搭建及工具安装教程

推荐阅读&#xff1a; [内部资源] 想拿年薪30W的软件测试人员&#xff0c;这份资料必须领取~ Python自动化测试全栈性能测试全栈&#xff0c;挑战年薪40W 从功能测试进阶自动化测试&#xff0c;熬夜7天整理出这一份超全学习指南【附网盘资源】 大家应该都有同一种感觉&…

传奇怎么设置GM帐号GM命令

传奇怎么设置GM帐号GM命令 我们先创建账号进入游戏 进入到服务器里面打开我的电脑 计算机D:\Mirserver\Mir200\Envir打开AdminList.txt文件 退出保存文本&#xff0c;小退一下游戏再次进入就是GM号了 方法二&#xff1a;在任务栏找到M2Server 点击查看-----列表信息一 保存后我…

微服务框架 SpringCloud微服务架构 分布式事务 38 动手实践 38.6 TCC 模式原理

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 分布式事务 文章目录微服务框架分布式事务38 动手实践38.6 TCC 模式原理38.6.1 TCC 模式原理38.6.2 举个栗子38.6.3 工作流程38.6.4 总结38 …

TensoRT—— buffers管理(samplesCommon::BufferManager)

BufferManager类处理主机和设备buffer分配和释放。 这个RAII类处理主机和设备buffer的分配和释放、主机和设备buffers之间的memcpy以帮助inference&#xff0c;以及debugging dumps以验证inference。BufferManager类用于简化buffer管理以及buffer和 engine之间的交互。 代码位…

web前端网页制作课作业:校园科技节活动网站 (纯HTML+CSS布局制作)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

cleanmymac x免费版安装包下载使用教程

我这台用了7年的MacBook Air &#xff0c;硬盘容量只有 128G &#xff0c;用段时间就会被系统提醒「您的磁盘几乎已满」&#xff0c;并且变得有点卡顿。这时&#xff0c;清理一下垃圾&#xff0c;腾出更多储存空间&#xff0c;就能让它满血复活。 这个时候我们可以用 CleanMyM…

Kafka系列之:使用kafka manager增加topic分区和副本

Kafka系列之:使用kafka manager增加topic分区和副本) 一、相关技术博客二、增加分区三、增加副本一、相关技术博客 Kafka系列之:实现kafka topic优先副本的选举Kafka系列之:使用Kafka Manager实现leader分区平衡和broker节点上分区平衡二、增加分区 增加分区: 增加完分区…

Adobe Acrobat 图标异常的解决办法

今天使用 Adobe Acrobat 打开文件阅读时&#xff0c;发现底部任务栏的图标是这样的&#xff0c;如下图所示。 这可不是常见的 Adobe Acrobat 图标&#xff0c;肯定是哪里出了问题&#xff0c;于是我在电脑开始这里找到 Adobe Acrobat 的快捷方式&#xff0c;其图标也是这样的&…

Android Binder 通信一次拷贝的原理

前言 对于 Android 开发者来说 Binder 应该不会陌生了&#xff0c;Binder 是 Android 提供的 IPC 通信机制&#xff0c;它是通过内存映射实现的&#xff0c;而这也是 Binder 相对于其他传统进程间通信方式的优点之一&#xff0c;即我们说的 Binder 只需要做“一次拷贝”&#…

基于springboot高校闲置物品交易系统微信小程序源码和论文

基于springboot二手物品交易系统微信小程序 互联网的兴起从本质上改变了整个社会的商品交易方式&#xff0c;国内各大企业从上个世纪 90 年代互联网兴起之时&#xff0c;就产生了通过网络进行销售经营商品的想法。但是由于在互网上企业的信誉难以认证、网络的法规政策不健全、物…

论文阅读|Embedding-based Retrieval in Facebook Search

该论文是facebook发表在KDD2020上的一篇关于搜索召回的paper。这篇文章提到的大多trick对于做过召回的同学比较熟悉了&#xff0c;可贵之处在于全面&#xff0c;包括了特征、样本、模型、全链路等各种细节知识。 1. 整体思路与框架 本文的出发点是搜索只做到query关键词匹配的…

电压放大器在农田灌溉管道缺陷检测研究中的应用

实验名称&#xff1a;电压放大器在农田灌溉管道缺陷检测研究中的应用 研究方向&#xff1a;管道检测、超声波检测 图&#xff1a;管道示意图 测试目的&#xff1a; 超声导波检测构件时&#xff0c;先激励导波使其在构件中传播&#xff0c;导波遇到构件中不连续处或有缺陷的地方…

Linux部署Tomcat和Nginx

目录一、Linux相关软件安装1. 安装gcc编译器2. 安装文件上传3. 安装wget4. 安装vim二、安装jdk和Apache-Tomcat1. 上传jdk和Apache-Tomcat2. 配置环境变量3. 测试&#xff08;1&#xff09;测试jdk&#xff08;2&#xff09;测试Apache-Tomcat三、安装Nginx1. 下载Nginx包2. 配…

哪路神仙写的421页MySQL高级笔记,涵盖MySQL所有技术!太香了

第2章MySQL权限与安全 对于企业而言&#xff0c;数据库中保存的企业业务数据是非常重要的信息&#xff0c;尤其是互联网企业&#xff0c;数据库中的用户信息是企业的根本资源。MySQL数据库管理系统的安全性涉及方方面面&#xff0c;不仅和操作系统本身有很大的关系&#xff0c;…

KD 树原理详解

一 点睛 KD 树&#xff08;K-Dimension tree&#xff09;是可以存储 K 维数据的树&#xff0c;是二叉搜索树的拓展&#xff0c;主要用于多维空间数据的搜索&#xff0c;例如范围搜索和最近邻搜索。BST、AVL、Treap 和伸展树等二叉搜索树的节点存储的都是一维信息&#xff0c;一…

上美股份在港交所开启招股:业绩将继续下滑,吕义雄提前大额套现

12月12日&#xff0c;上海上美化妆品股份有限公司&#xff08;HK:02145&#xff0c;下称“上美股份”&#xff09;在港交所开启招股。根据公告&#xff0c;上美股份本次拟全球发售3695.8万股&#xff0c;发售价将为每股发售股份25.20-29.80港元&#xff0c;预期将于2022年12月2…

基于二阶锥规划的主动配电网最优潮流求解(Matlab代码实现)

&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&#xff1a;行百里者&#xff0c;半于九十。 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff…

豪横卡塔尔!疯狂世界杯

豪横卡塔尔&#xff01;疯狂世界杯1.Big Data -- Postgres1.1 Big Data -- Postgres2.Big Data -- Postgres3.Big Data -- Postgres1.Big Data – Postgres 这届世界杯是有史以来最贵的一次世界杯&#xff0c;因为这次世界杯卡塔尔就花了2200多亿美元&#xff0c;可以说自世界杯…

[附源码]Node.js计算机毕业设计电影网上购票系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…