【Windows】实现窗口子类化(基于远程线程注入)

目录

前言

原理解释

完整项目

相关文献


文章出处链接:[https://blog.csdn.net/qq_59075481/article/details/140334106]

前言

众所周知,DLL 注入有多种用途,如热修补、日志记录、子类化等。本文重点介绍使用 DLL 注入对窗口进行子类化。子类化是通过更改窗口过程来重新定义窗口的行为。要更改窗口过程,窗口过程应驻留在创建窗口的进程中。我计划通过注入子类化模块在不了解创建窗口的进程的情况下替换窗口行为。本文涉及三个模块。

  1. GUITestProcess:这是创建窗口的目标进程。称为“GUITestProcess”,DLL(“SubClassModule”)被注入其中。这是一个简单的 Win32 应用程序,它有一个窗口,每当按下鼠标左键时,就会绘制一个圆圈。
  2. SubClassModule:这是一个 DLL,它具有新的窗口过程,可以挂接到被注入者的窗口。
  3. Injector:这是实际将注入物注入被注入者的进程。这是一个简单的控制台应用程序。

原理解释

窗口子类化有两种实现方式:一种是 SetWindowLongPtr 指定 GWLP_WNDPROC 参数,替换窗口过程函数,另外一种是使用 ComCtl32.dll (至少 6.0 版本)导出的 SetWindowSubclass 来设置子类化窗口过程(另外几个函数分别是:GetWindowSubclass、RemoveWindowSubclass 和 DefSubclassProc),后者提供更多扩展特性。本文以最基础的 SetWindowLongPtr 子类化进行讲解。

首先是一个桌面窗口程序实例代码:

// GUITestProcess.cpp : 定义应用程序的入口点。
//#include "framework.h"
#include "GUITestProcess.h"
#include <windowsx.h>#define MAX_LOADSTRING 100// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR    lpCmdLine,int       nCmdShow)
{UNREFERENCED_PARAMETER(hPrevInstance);UNREFERENCED_PARAMETER(lpCmdLine);// TODO: Place code here.MSG msg;HACCEL hAccelTable;// Initialize global stringsLoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);LoadString(hInstance, IDC_INJECTEE, szWindowClass, MAX_LOADSTRING);MyRegisterClass(hInstance);// Perform application initialization:if (!InitInstance(hInstance, nCmdShow)){return FALSE;}hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_INJECTEE));// Main message loop:while (GetMessage(&msg, NULL, 0, 0)){if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)){TranslateMessage(&msg);DispatchMessage(&msg);}}return (int)msg.wParam;
}//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_INJECTEE));wcex.hCursor = LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wcex.lpszMenuName = MAKEINTRESOURCE(IDC_INJECTEE);wcex.lpszClassName = szWindowClass;wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));return RegisterClassEx(&wcex);
}//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{HWND hWnd;hInst = hInstance; // Store instance handle in our global variablehWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);if (!hWnd){return FALSE;}ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);return TRUE;
}//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{int wmId, wmEvent;PAINTSTRUCT ps;HDC hdc;switch (message){case WM_LBUTTONDOWN:{int x = GET_X_LPARAM(lParam);int y = GET_Y_LPARAM(lParam);HDC hDC = ::GetDC(hWnd);::Ellipse(hDC, x, y, (x + 50), (y + 50));break;}case WM_COMMAND:wmId = LOWORD(wParam);wmEvent = HIWORD(wParam);// Parse the menu selections:switch (wmId){case IDM_ABOUT:DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}break;case WM_PAINT:hdc = BeginPaint(hWnd, &ps);// TODO: Add any drawing code here...EndPaint(hWnd, &ps);break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{UNREFERENCED_PARAMETER(lParam);switch (message){case WM_INITDIALOG:return (INT_PTR)TRUE;case WM_COMMAND:if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL){EndDialog(hDlg, LOWORD(wParam));return (INT_PTR)TRUE;}break;}return (INT_PTR)FALSE;
}

让我们看一下注入的源代码。众所周知,注入是一个简单的 DLL,当 DllMain 被调用时DLL_PROCESS_ATTACH,我正在进行 Hack。

//
case DLL_PROCESS_ATTACH:
{//Find the window of the Injectee using its titleHWND hwnd = ::FindWindow(NULL,TEXT("GUITestProcess") );//If the window found, then change it's window procif( hwnd ){oldWindowProc = ::SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR) HackingWndProc );}break;
}

新的窗口过程看起来像这样,这个窗口过程将替换原窗口的左键单击和右键单击操作:

LRESULT CALLBACK HackingWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{switch( message ) {case WM_LBUTTONDOWN:{int x = GET_X_LPARAM(lParam);int y = GET_Y_LPARAM(lParam);HDC hDC = ::GetDC( hWnd );//::Rectangle(  hDC,x,y,(x+50),(y+50) );DrawText( hDC,x,y);break;}case WM_RBUTTONDOWN:{int x = GET_X_LPARAM(lParam);int y = GET_Y_LPARAM(lParam);HDC hDC = ::GetDC( hWnd );::Ellipse( hDC,x,y,(x+50),(y+50));break;}case WM_DESTROY:{PostQuitMessage(0);break;}default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}

接下来我们看一下 Injector 的源代码。

这段代码很简单,首先我们要知道要注入到目标进程(injectee)中的 DLL(SubClassModule)的名字。

这个DLL名称应该是目标进程知道的。因此,它必须写入目标进程的地址空间中。

  • 使用窗口标题找出 GUITestProcess 的窗口。
  • 接下来使用 创建窗口的进程的句柄 GetWindowThreadProcessId。
  • 使用句柄打开注入者的进程。
  • 通过命令行的参数获取要注入的 DLL 的路径。
  • 然后在被注入者的地址空间中分配内存以写入要注入的 DLL 的名称。
  • 获取 LoadLibrary 函数的地址。这是将在注入对象的地址空间中创建的线程要调用的方法。
  • 然后调用 NtCreateThreadEx 在被注入者的地址空间内创建一个线程,这个线程将调用 LoadLibrary 函数来注入 DLL。
  • 当线程调用 LoadLibrary 方法加载 DLL 时,DllMain 被调用的原因为 DLL_PROCESS_ATTACH。参阅注入的代码以查看 DllMain 调用时会发生什么。
  • 等待远程线程执行完毕后再调用该 VirtualFree 方法,否则当远程线程查找 DLL 的名称时,保存 DLL 名称的地址块的名称已被注入器进程释放,这可能会导致崩溃!

注入器的命令行参数:注入器程序路径   Dll 文件路径。

注入器主要代码: 

#include <iostream>
#include <windows.h>
#include <vector>
#include <tlhelp32.h>
#include <shlwapi.h>#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "advapi32.lib")#define INJECTEE_NAME TEXT("GUITestProcess")BOOL ProcessHasLoadDll(DWORD pid,const TCHAR* dll
);BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId,const wchar_t* pszDllFileName
);int wmain(int argc, wchar_t* argv[])
{SetConsoleTitleW(L"SubClassWindowsInjector v.1.0");if (argc != 2){std::cerr << "错误:参数不合法!" << std::endl;std::cin.get();return -1;}const size_t dllpthlen =wcslen(argv[1]) * sizeof(wchar_t);if (dllpthlen < 1){std::cerr << "错误:文件路径错误!" << std::endl;std::cin.get();return -1;}wchar_t* dllpth = new wchar_t[dllpthlen];wcscpy_s(dllpth, dllpthlen, argv[1]);BOOL dllextflag = PathFileExistsW(dllpth);if (FALSE == dllextflag){std::cerr << "错误:文件不存在或者无法访问!" << std::endl;std::cin.get();return -1;}if (PathGetDriveNumberW(dllpth) == -1){TCHAR szPath[_MAX_PATH] = { 0 };TCHAR szDrive[_MAX_DRIVE] = { 0 };TCHAR szDir[_MAX_DIR] = { 0 };TCHAR szFname[_MAX_FNAME] = { 0 };TCHAR szExt[_MAX_EXT] = { 0 };TCHAR CurBinPath[MAX_PATH] = { 0 };GetModuleFileNameW(NULL, szPath, sizeof(szPath) / sizeof(TCHAR));ZeroMemory(CurBinPath, sizeof(CurBinPath));_wsplitpath_s(szPath, szDrive, szDir, szFname, szExt);wsprintf(CurBinPath, L"%s%s", szDrive, szDir);wcscat_s(CurBinPath, dllpth);dllextflag = PathFileExistsW(CurBinPath);if (FALSE == dllextflag){std::cerr << "错误:文件不存在或者无法访问!" << std::endl;std::cin.get();return -1;}dllpth = CurBinPath;}DWORD dwProcessID = 0;//Find the main window of the InjecteeHWND hwnd = ::FindWindow(NULL, INJECTEE_NAME);//Get the process hanlde of injecteeGetWindowThreadProcessId(hwnd, &dwProcessID);if (dwProcessID == 0) {std::cerr << "错误:找不到目标窗口!"<< std::endl;return 0;}if (ProcessHasLoadDll(dwProcessID, dllpth) != NULL){std::cerr << "警告:PID 为 " << dwProcessID << " 的进程已经包含目标 DLL。" << std::endl;return 1;}if (ZwCreateThreadExInjectDll(dwProcessID, dllpth) == FALSE){std::cerr << "错误:注入 PID 为 " << dwProcessID << " 的进程时失败。" << std::endl;std::cerr << "原因:" << GetLastError() << std::endl;return 1;}std::cout << "操作已经完成。" << std::endl;std::cin.get();return 0;
}BOOL ProcessHasLoadDll(DWORD pid, const TCHAR* dll) {/** 参数为TH32CS_SNAPMODULE 或 TH32CS_SNAPMODULE32时,如果函数失败并返回ERROR_BAD_LENGTH,则重试该函数直至成功* 进程创建未初始化完成时,CreateToolhelp32Snapshot会返回error 299,但其它情况下不会*/HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);while (INVALID_HANDLE_VALUE == hSnapshot) {DWORD dwError = GetLastError();if (dwError == ERROR_BAD_LENGTH) {hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);continue;}else {printf("CreateToolhelp32Snapshot failed: %d\ncurrentProcessId: %d \t targetProcessId:%d\n",dwError, GetCurrentProcessId(), pid);return FALSE;}}MODULEENTRY32W mi{};mi.dwSize = sizeof(MODULEENTRY32W); //第一次使用必须初始化成员BOOL bRet = Module32FirstW(hSnapshot, &mi);while (bRet) {// mi.szModule是短路径if (wcsstr(dll, mi.szModule) || wcsstr(mi.szModule, dll)) {if (hSnapshot != NULL) CloseHandle(hSnapshot);return TRUE;}mi.dwSize = sizeof(MODULEENTRY32W);bRet = Module32NextW(hSnapshot, &mi);}if (hSnapshot != NULL) CloseHandle(hSnapshot);return FALSE;
}BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId,const wchar_t* pszDllFileName
)
{size_t pathSize = (wcslen(pszDllFileName) + 1) * sizeof(wchar_t);// 1.打开目标进程HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, // 打开权限FALSE, // 是否继承dwProcessId); // 进程 PIDif (hProcess == nullptr){printf("错误:打开目标进程失败!\n");return FALSE;}// 2.在目标进程中申请空间LPVOID lpPathAddr = VirtualAllocEx(hProcess, 0, pathSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (lpPathAddr == nullptr){printf("错误:在目标进程中申请空间失败!\n");CloseHandle(hProcess);return FALSE;}// 3.在目标进程中写入Dll路径if (FALSE == WriteProcessMemory(hProcess, lpPathAddr, pszDllFileName, pathSize, NULL)) // 实际写入大小{printf("错误:目标进程中写入Dll路径失败!\n");CloseHandle(hProcess);return FALSE;}//4.加载ntdll.dllHMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");if (hNtdll == nullptr){printf("错误:加载ntdll.dll失败!\n");CloseHandle(hProcess);return FALSE;}//5.获取LoadLibraryA的函数地址//FARPROC可以自适应32位与64位FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle(L"Kernel32.dll"),"LoadLibraryW");if (pFuncProcAddr == nullptr){printf("错误:获取LoadLibrary函数地址失败!\n");CloseHandle(hProcess);return FALSE;}//6.获取ZwCreateThreadEx函数地址,该函数在32位与64位下原型不同//_WIN64用来判断编译环境 ,_WIN32用来判断是否是Windows系统
#ifdef _WIN64typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,ULONG CreateThreadFlags,SIZE_T ZeroBits,SIZE_T StackSize,SIZE_T MaximumStackSize,LPVOID pUnkown);
#elsetypedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,BOOL CreateSuspended,DWORD dwStackSize,DWORD dw1,DWORD dw2,LPVOID pUnkown);
#endif typedef_ZwCreateThreadEx ZwCreateThreadEx =(typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx");if (ZwCreateThreadEx == nullptr){printf("错误:获取ZwCreateThreadEx函数地址失败!\n");CloseHandle(hProcess);return FALSE;}//7.在目标进程中创建远线程HANDLE hRemoteThread = NULL;DWORD lpExitCode = 0;DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL,hProcess,(LPTHREAD_START_ROUTINE)pFuncProcAddr, lpPathAddr, 0, 0, 0, 0, NULL);if (hRemoteThread == nullptr){printf("错误:目标进程中创建线程失败!\n");CloseHandle(hProcess);return FALSE;}// 8.等待线程结束WaitForSingleObject(hRemoteThread, -1);GetExitCodeThread(hRemoteThread, &lpExitCode);if (lpExitCode == 0){printf("错误:目标进程中注入 DLL 失败,请检查提供的 DLL 是否有效!\n");CloseHandle(hProcess);return FALSE;}// 9.清理环境VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);CloseHandle(hRemoteThread);CloseHandle(hProcess);FreeLibrary(hNtdll);return TRUE;
}

子类化效果:

子类化效果

完整项目

你可以从这里获取完整的项目代码:

链接:https://pan.baidu.com/s/1Q3og8dgv7lyzXsrGqZy4kg?pwd=6666 
提取码:6666 

相关文献

  • https://www.cnblogs.com/1yzq/p/12939791.html
  • https://learn.microsoft.com/zh-cn/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass
  • https://stackoverflow.com/questions/65695710/how-to-subclass-another-application-control-using-dll-hook-in-win32
  • https://www.purebasic.fr/english/viewtopic.php?t=81714

文章出处链接:[https://blog.csdn.net/qq_59075481/article/details/140334106]。 

本文发布于:2024.07.10,修改于:2024.07.10。

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

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

相关文章

GOLLIE : ANNOTATION GUIDELINES IMPROVE ZERO-SHOT INFORMATION-EXTRACTION

文章目录 题目摘要引言方法实验消融 题目 Gollie&#xff1a;注释指南改进零样本信息提取 论文地址&#xff1a;https://arxiv.org/abs/2310.03668 摘要 大型语言模型 (LLM) 与指令调优相结合&#xff0c;在泛化到未见过的任务时取得了重大进展。然而&#xff0c;它们在信息提…

又上热搜!曝iPhone 16将支持40W快充

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 7月9日晚&#xff0c;微博话题“iPhone16系列或将支持40W快充”上了热搜榜&#xff0c;这已经是iPhone 16系列第N次上热搜了。 据爆料&#xff0c;iPhone 16系列充电功率将提升至40W&#xff0c;并且…

米家立式学习灯怎么样?书客、米家、孩视宝三款护眼大路灯巅峰PK!

米家立式学习灯怎么样?不知从什么时候开始&#xff0c;青少年成为了近视重灾区&#xff0c;主要促成近视的原因有长时间接触电子产品、学习时的不正确姿势、不良的灯光环境等&#xff0c;除了减少电子产品的使用以及多室外活动之外&#xff0c;剩下的就是室内孩子经常学习的光…

全球首款集成GPT-4o的智能眼镜AirGo Vision:AI眼镜的未来

引言 在人工智能和大模型技术迅猛发展的今天&#xff0c;AI硬件产品逐渐走入人们的生活。继Meta Ray-Ban智能眼镜之后&#xff0c;Solos公司在最近的香港智能眼镜峰会上发布了全球首款集成GPT-4o的智能眼镜AirGo Vision。本文将深入探讨这款AI智能眼镜的功能、技术特点以及其在…

侯捷C++面向对象高级编程(下)-2-non-explicit one argument constructor

1.构造函数 构造函数: Fraction(int num, int den 1) 初始化分子和分母&#xff0c;允许指定分子 num 和可选的分母 den。默认情况下&#xff0c;分母为 1。 加法运算符重载: Fraction operator(const Fraction& f) 重载了加法运算符 。这使得两个 Fraction 对象可以通过 …

Qt 异步实现事件的定时执行 - QTimer和QThread的联合使用

异步实现事件的定时执行 - QTimer和QThread的联合使用 引言一、核心源码二、其信号和槽函数简述三、定时器及其moveToThread简述 引言 在 Qt 中&#xff0c;如果想要定时执行某些事件或函数&#xff0c;通常会使用 QTimer 类。QTimer 允许设置一个时间间隔&#xff0c;当这个时…

echarts使用自定义图形实现3D柱状图

先看下效果吧 实现思路 使用graphic创建并注册自定义图形。根据每组的数据值&#xff0c;得到一个对应的点&#xff0c;从点出发用canvas绘制一组图形&#xff0c;分别为 顶部的菱形 const CubeTop echarts.graphic.extendShape({buildPath: function (ctx, shape) {const c1…

NVIDIA良心给显卡免费升级,只为挨更多的骂

起猛了&#xff0c;还真的以为 NVIDIA 良心发现了。 众所周知&#xff0c;英伟达对于咱们普通游戏玩家向来不屑一顾。只因为游戏业务在 NVIDIA 收入中占比较少。 在最新的 40 系显卡 RTX 4070 Ti Super 显卡中&#xff0c;NVIDIA悄悄给它来了一次核心「升级」&#xff0c;将原…

ARM学习(29)NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择

ARM学习&#xff08;28&#xff09;NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择 1、多种启动方式介绍 IMX1166 支持多组flexSPI 引脚启动&#xff0c;FlexSPI1以及FlexSPI2&#xff0c;通过boot cfg可以切换FlexSPI得实例。 每个实例又支持多组引脚&#xff0c;总共…

LiteOS增加执行自定义源码

开发过程注意事项&#xff1a; 源码工程路径不能太长 源码工程路径不能有中文 一定要关闭360等杀毒软件&#xff0c;否则编译的打包阶段会出错 增加自定义源码的步骤: 1.创建源码目录 2. 创建源文件 新建myhello目录后&#xff0c;再此目录下再新建源文件myhello_demo.c 3. 编…

程序员学长 | PyCaret,一个超强的 python 库

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;PyCaret&#xff0c;一个超强的 python 库 今天给大家分享一个超强的 python 库&#xff0c;PyCaret。 https://github.com/pycaret/pycaret 简介 …

[论文笔记]RAPTOR: RECURSIVE ABSTRACTIVE PROCESSING FOR TREE-ORGANIZED RETRIEVAL

引言 今天带来又一篇RAG论文笔记&#xff1a;RAPTOR: RECURSIVE ABSTRACTIVE PROCESSING FOR TREE-ORGANIZED RETRIEVAL。 检索增强语言模型能够更好地适应世界状态的变化并融入长尾知识。然而&#xff0c;大多数现有方法只能从检索语料库中检索到短的连续文本片段&#xff0…

FP5207+音频功率放大器的组合解决方案-适用于便携式音频播放器、无线耳机、智能音箱和车载音响系统等高质量音频输出需求的产品,以提高电池供电的效率和输出功率

随着消费者对智能家居的需求增长&#xff0c;智能音响市场成为重要增长点。同时&#xff0c;音响技术也在不断发展&#xff0c;音响及扬声器的功能和性能不断提升。 蓝牙音箱&#xff0c;这类音箱供电是以锂电池为主&#xff0c;一般选用内置升压的音频功放芯片&#xff0c;音响…

windows 构建nginx本地服务随系统自启

1.先去官网下载一个nginxzip 2.将zip解压&#xff0c;将nginx-server.exe文件放入文件夹 3.创建nginx-server.xml&#xff0c;将以下内容放进文件内 <service> <id>nginx</id> <name>Nginx Service</name> <description>High Per…

强化学习中的蒙特卡洛算法和时序差分算法

在强化学习&#xff08;Reinforcement Learning, RL&#xff09;中&#xff0c;价值函数的估计是核心任务之一。蒙特卡洛&#xff08;Monte Carlo, MC&#xff09;方法和时序差分&#xff08;Temporal Difference, TD&#xff09;方法是两种常用的策略&#xff0c;用于估计状态…

软件架构之架构风格

软件架构之架构风格 9.3 软件架构风格9.3.1 软件架构风格分类9.3.2 数据流风格9.3.3 调用/返回风格9.3.4 独立构件风格9.3.5 虚拟机风格9.3.6 仓库风格 9.4 层次系统架构风格9.4.1 二层及三层 C/S 架构风格9.4.2 B/S 架构风格9.4.3 MVC 架构风格9.4.4 MVP 架构风格 9.5 面向服务…

机器学习筑基篇,​Ubuntu 24.04 编译安装 Python 及多版本切换

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] Ubuntu 24.04 编译安装最新Python及多版本切换 描述&#xff1a;说到机器学习&#xff0c;人工智能&#xff0c;深度学习不免会提到Python这一门编程语言&#xff08;人生苦短&#xff0c;及时Pyt…

windows防火墙端口设置

PS&#xff1a;本文实例为Windows Server 2019&#xff0c;其他Windows版本大同小异。 1、首先打开windows防火墙&#xff0c;点击“高级设置” 2、 高级设置界面 3、假设需要开放一个端口为3306应该怎么做 光标对准“入站规则”右键新建规则&#xff0c;选择“端口” 协议这…

C++类和对象(一)

目录 面向过程和面向对象 面向过程编程&#xff08;Procedural Programming&#xff09; 面向对象编程&#xff08;Object-Oriented Programming&#xff09; 一、类的定义 类定义格式 类域 二、类的访问限定符及封装 访问限定符 封装 三、实例化 实例化概念 对象大小…

常见WAF拦截页面总结

(1) D盾 (2) 云锁 (3) UPUPW安全防护 (4) 宝塔网站防火墙 (5) 网防G01 (6) 护卫神 (7) 网站安全狗 (8) 智创防火墙 (9) 360主机卫士或360webscan (10) 西数WTS-WAF (11) Naxsi WAF (12) 腾讯云 (13) 腾讯宙斯盾 (14) 百度云 图片 (15) 华为云 (16) 网宿云 (17) 创宇盾 图片 (…