对象回调初步研究

news2025/6/10 15:47:09

_OBJECT_TYPE结构分析

在介绍什么是对象回调前,首先要熟悉下结构

以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光聚焦到0x28这个TypeInfo结构上

1: kd> dd PsProcessType
83f7a02c  86ae5e38 86ae5f00 86ae5ca8 86ae5be0
83f7a03c  86ae58a8 83f45640 8fe87988 00010000
83f7a04c  00000cd8 00000258 00000000 00000001
83f7a05c  00000000 0000004b 00000001 00989680
83f7a06c  00000000 00000029 00000002 c0ffffff
83f7a07c  c0607ff8 00000000 00000000 c0403080
83f7a08c  00000000 00010100 00000500 00000000
83f7a09c  00000001 00000000 0000000d 00040107
1: kd> dt _OBJECT_TYPE 86ae5e38
ntdll!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0x86ae5e38 - 0x86ae5e38 ]
   +0x008 Name             : _UNICODE_STRING "Process"
   +0x010 DefaultObject    : (null) 
   +0x014 Index            : 0x7 ''
   +0x018 TotalNumberOfObjects : 0x21
   +0x01c TotalNumberOfHandles : 0xc1
   +0x020 HighWaterNumberOfObjects : 0x2a
   +0x024 HighWaterNumberOfHandles : 0xe3
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER//
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : 0x636f7250
   +0x080 CallbackList     : _LIST_ENTRY [ 0x86ae5eb8 - 0x86ae5eb8 ]//

这个TypeInfo里面也存着一部分值得关注的信息,从0x30开始的回调函数的调用会优先于我们将要介绍的对象回调,比如0x34的这个回调就是在OpenProcess执行之后先被执行,然后其中会进行一系列处理(比如之前也介绍过的通过进程id打开进程对象)

1: kd> dt _OBJECT_TYPE_INITIALIZER 86ae5e38+0x28
ntdll!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : 0x50
   +0x002 ObjectTypeFlags  : 0x4a 'J'
   +0x002 CaseInsensitive  : 0y0
   +0x002 UnnamedObjectsOnly : 0y1
   +0x002 UseDefaultObject : 0y0
   +0x002 SecurityRequired : 0y1
   +0x002 MaintainHandleCount : 0y0
   +0x002 MaintainTypeList : 0y0
   +0x002 SupportsObjectCallbacks : 0y1//是否支持回调标志位,如果修改会被PG
   +0x004 ObjectTypeCode   : 0
   +0x008 InvalidAttributes : 0xb0
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : 0x1fffff//权限掩码
   +0x020 RetainAccess     : 0x101000
   +0x024 PoolType         : 0 ( NonPagedPool )
   +0x028 DefaultPagedPoolCharge : 0x1000
   +0x02c DefaultNonPagedPoolCharge : 0x2f0
   +0x030 DumpProcedure    : (null) 
   +0x034 OpenProcedure    : 0x84021ccb     long  nt!PspProcessOpen+0//win官方注册的回调
   +0x038 CloseProcedure   : 0x84081d2e     void  nt!PspProcessClose+0
   +0x03c DeleteProcedure  : 0x840845f5     void  nt!PspProcessDelete+0
   +0x040 ParseProcedure   : (null) 
   +0x044 SecurityProcedure : 0x840765b6     long  nt!SeDefaultObjectMethod+0
   +0x048 QueryNameProcedure : (null) 
   +0x04c OkayToCloseProcedure : (null) 

可以用pchunter看这些钩子,他们都是挂在内核里面的

在这里插入图片描述

这些对象的作用是全局性的,那么可以怎么用呢

DebugObject使用示例

在内核中的DbgkpInitializePhase0这个函数里面,有DbgObject的定义,这说明官方的符号表里面有这个对象

在这里插入图片描述

我们在Windbg中来看这个对象(DbgkDebugObjectType),

1: kd> dd DbgkDebugObjectType
83f48dac  86ae5528 00000000 8d86a330 00000003
83f48dbc  00000012 0000002a 000000a1 00000024
83f48dcc  8caa5b3c 01010000 00000002 8322ee0d
83f48ddc  83ee1508 00000000 00000000 00000000
83f48dec  00000003 83e10550 87dbb030 00000000
83f48dfc  00000000 00000000 00000000 00000003
83f48e0c  00000001 00000001 00000001 00000001
83f48e1c  83212408 83212006 8320c006 832122fa
1: kd> dt _OBJECT_TYPE 86ae5528
nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ 0x86ae5528 - 0x86ae5528 ]
   +0x008 Name             : _UNICODE_STRING "DebugObject"//对象名字
   +0x010 DefaultObject    : (null) 
   +0x014 Index            : 0xb ''
   +0x018 TotalNumberOfObjects : 0
   +0x01c TotalNumberOfHandles : 0
   +0x020 HighWaterNumberOfObjects : 0
   +0x024 HighWaterNumberOfHandles : 0
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : 0x75626544
   +0x080 CallbackList     : _LIST_ENTRY [ 0x86ae55a8 - 0x86ae55a8 ]

同理,也来看看它的TypeInfo

1: kd> dt _OBJECT_TYPE_INITIALIZER 86ae5528+0x28
nt!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : 0x50
   +0x002 ObjectTypeFlags  : 0x8 ''
   +0x002 CaseInsensitive  : 0y0
   +0x002 UnnamedObjectsOnly : 0y0
   +0x002 UseDefaultObject : 0y0
   +0x002 SecurityRequired : 0y1
   +0x002 MaintainHandleCount : 0y0
   +0x002 MaintainTypeList : 0y0
   +0x002 SupportsObjectCallbacks : 0y0
   +0x004 ObjectTypeCode   : 0
   +0x008 InvalidAttributes : 0
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : 0x1f000f//这是这个对象的权限掩码
   +0x020 RetainAccess     : 0
   +0x024 PoolType         : 0 ( NonPagedPool )
   +0x028 DefaultPagedPoolCharge : 0
   +0x02c DefaultNonPagedPoolCharge : 0x30
   +0x030 DumpProcedure    : (null) 
   +0x034 OpenProcedure    : (null) 
   +0x038 CloseProcedure   : 0x840be98b     void  nt!DbgkpCloseObject+0
   +0x03c DeleteProcedure  : 0x8408e87e     void  nt!ExpDeleteCallback+0
   +0x040 ParseProcedure   : (null) 
   +0x044 SecurityProcedure : 0x840765b6     long  nt!SeDefaultObjectMethod+0
   +0x048 QueryNameProcedure : (null) 
   +0x04c OkayToCloseProcedure : (null)

如果我们将它的ValidAccessMask置零,那么其他进程申请调试时就无法正确创建调试对象,从而达到简单反调试的作用,当然,这样做非常暴力。

1: kd> ed 86ae5528+0x28+1c 0
WriteVirtual: 86ae556c not properly sign extended
1: kd> dt _OBJECT_TYPE_INITIALIZER 86ae5528+0x28
nt!_OBJECT_TYPE_INITIALIZER
   +0x000 Length           : 0x50
   +0x002 ObjectTypeFlags  : 0x8 ''
   +0x002 CaseInsensitive  : 0y0
   +0x002 UnnamedObjectsOnly : 0y0
   +0x002 UseDefaultObject : 0y0
   +0x002 SecurityRequired : 0y1
   +0x002 MaintainHandleCount : 0y0
   +0x002 MaintainTypeList : 0y0
   +0x002 SupportsObjectCallbacks : 0y0
   +0x004 ObjectTypeCode   : 0
   +0x008 InvalidAttributes : 0
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : 0
   +0x020 RetainAccess     : 0
   +0x024 PoolType         : 0 ( NonPagedPool )
   +0x028 DefaultPagedPoolCharge : 0
   +0x02c DefaultNonPagedPoolCharge : 0x30
   +0x030 DumpProcedure    : (null) 
   +0x034 OpenProcedure    : (null) 
   +0x038 CloseProcedure   : 0x840be98b     void  nt!DbgkpCloseObject+0
   +0x03c DeleteProcedure  : 0x8408e87e     void  nt!ExpDeleteCallback+0
   +0x040 ParseProcedure   : (null) 
   +0x044 SecurityProcedure : 0x840765b6     long  nt!SeDefaultObjectMethod+0
   +0x048 QueryNameProcedure : (null) 
   +0x04c OkayToCloseProcedure : (null) 

这时候我们来看看电脑上的调试功能,比如CE

在这里插入图片描述

然后就是Dbgview,图有点看不清,但是意思就是调试没开始就结束了

在这里插入图片描述

我们在手动修改的层面上去实践了这种做法,这样我们就可以更好的理解了待会要做的事情。

注册我们的对象回调

在win中,注册对象我们一般使用ObRegisterCallbacks

先来看看这个函数的参数,参数有两个一个是CallbackRegistration,这是一个记录我们回调信息的结构;第二个参数是RegistrationHandle,这是一个用来接收我们用来标识注册回调例程的值

NTSTATUS ObRegisterCallbacks(
  [in]  POB_CALLBACK_REGISTRATION CallbackRegistration,
  [out] PVOID                     *RegistrationHandle
);

[in] CallbackRegistration
指向 OB_CALLBACK_REGISTRATION 结构的指针,该结构指定回调例程和其他注册信息的列表。

[out] RegistrationHandle
指向变量的指针,该变量接收标识注册的回调例程集的值。 调用方将此值传递给 ObUnRegisterCallbacks 例程,以取消注册回调集。

首先要了解这个POB_CALLBACK_REGISTRATION的结构,所以我们知道,我们在这个结构中要填四个值

typedef struct _OB_CALLBACK_REGISTRATION {
  USHORT                    Version;
  USHORT                    OperationRegistrationCount;
  UNICODE_STRING            Altitude;
  PVOID                     RegistrationContext;
  OB_OPERATION_REGISTRATION *OperationRegistration;
} OB_CALLBACK_REGISTRATION, *POB_CALLBACK_REGISTRATION;

Version
请求的对象回调注册的版本。 驱动程序应指定OB_FLT_REGISTRATION_VERSION。

OperationRegistrationCount
OperationRegistration 数组中的条目数。

Altitude
一个 Unicode 字符串,指定驱动程序的高度。 有关海拔的详细信息,请参阅 微型筛选器驱动程序的加载顺序组和高度。

RegistrationContext
运行回调例程时,系统将 RegistrationContext 值传递给回调例程。 此值的含义是驱动程序定义的。

OperationRegistration
指向 OB_OPERATION_REGISTRATION 结构的数组的指针。 每个结构指定 ObjectPreCallback 和 ObjectPostCallback 回调例程以及调用例程的作类型。

第一个参数就是Version,这里建议我们使用OB_FLT_REGISTRATION_VERSION这个宏来获取版本号

它其实就是这么一个值

#define OB_FLT_REGISTRATION_VERSION_0100  0x0100

这里我们建议使用ObGetFilterVersion这个函数,可以看见返回的值是一样的,但是用函数有一个好处,不管什么版本的内核,都会返回正确的版本,不会因为环境的改变而受影响。

在这里插入图片描述

第二个参数是OperationRegistrationCount,也就是下面第五个参数OperationRegistration是一个回调函数数组,第二个参数的意义就是说明这个数组里面有多少个回调

第三个参数被称为Altitude,它是海拔,现在先简单理解,这里说的“指定驱动程序的高度”,其实应该是驱动的优先级。这个东西比较复杂这里先不详细介绍。

在这里插入图片描述

这里稍微说明一下,不是必须要规定在这些海拔范围内才能生效,这些海拔本身只是一个参考值

第四个参数是RegistrationContext,这是我们传给回调例程的参数

第五个参数就是OperationRegistration,这也有一个结构OB_OPERATION_REGISTRATION,看看这个结构的样子,要填的包括例子官方已经给过了,需要注意的是PreOperation和PostOperation,这分别是我们前回调和后回调。

比如,我Operations填了OB_OPERATION_HANDLE_CREATE,那么就会在我创建这个对象前执行PreOperation,创建后执行PostOperation,这两个回调可以只填一个,但是不能两个都不填

typedef struct _OB_OPERATION_REGISTRATION {
  POBJECT_TYPE                *ObjectType;
  OB_OPERATION                Operations;
  POB_PRE_OPERATION_CALLBACK  PreOperation;
  POB_POST_OPERATION_CALLBACK PostOperation;
} OB_OPERATION_REGISTRATION, *POB_OPERATION_REGISTRATION;
ObjectType
指向触发回调例程的对象类型的指针。 指定以下值之一:
处理作的 PsProcessType
线程句柄作的 PsThreadType
ExDesktopObjectType 桌面句柄作。 此值在 Windows 10 中不受早期版本的作系统支持。

Operations
指定以下一个或多个标志:
OB_OPERATION_HANDLE_CREATE
新进程、线程或桌面句柄已打开或将打开。
OB_OPERATION_HANDLE_DUPLICATE
进程、线程或桌面句柄是或将被复制。

PreOperation
指向 ObjectPreCallback 例程的指针。 系统在请求的作发生之前调用此例程。

PostOperation
指向 ObjectPostCallback 例程的指针。 系统在请求的作发生后调用此例程。

关于这两个回调的结构,我同样给出。

typedef OB_PREOP_CALLBACK_STATUS
(*POB_PRE_OPERATION_CALLBACK) (
    _In_ PVOID RegistrationContext,
    _Inout_ POB_PRE_OPERATION_INFORMATION OperationInformation
    );

typedef VOID
(*POB_POST_OPERATION_CALLBACK) (
    _In_ PVOID RegistrationContext,
    _In_ POB_POST_OPERATION_INFORMATION OperationInformation
    );

以及回调中的POB_POST_OPERATION_INFORMATION


typedef struct _OB_PRE_OPERATION_INFORMATION {
    _In_ OB_OPERATION           Operation;
    union {
        _In_ ULONG Flags;
        struct {
            _In_ ULONG KernelHandle:1;
            _In_ ULONG Reserved:31;
        };
    };
    _In_ PVOID                         Object;
    _In_ POBJECT_TYPE                  ObjectType;
    _Out_ PVOID                        CallContext;
    _In_ POB_PRE_OPERATION_PARAMETERS  Parameters;
} OB_PRE_OPERATION_INFORMATION, *POB_PRE_OPERATION_INFORMATION;

现在我们所有的材料已经备齐了,可以来写一些demo

我们知道,任务管理器里面结束进程也是通过R3的API,它也是调用类似TerminateProcess这样的API对进程进行终结,让我们来回顾一下这个函数的参数,可以看见,我们就需要向其中传入句柄,传入句柄,也就是需要打开句柄OpenProcess

BOOL TerminateProcess(
  [in] HANDLE hProcess,
  [in] UINT   uExitCode
);

所以,我们能不能用注册一个对象回调,让进程对象在被申请的时候检查,如果是我们的程序,就清空当前进程权限位,使其无法打开我们的程序。

demo

#include<ntifs.h>


//0x78 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY InLoadOrderLinks;                                    //0x0
	LIST_ENTRY InMemoryOrderLinks;                                  //0x8
	LIST_ENTRY InInitializationOrderLinks;                          //0x10
	VOID* DllBase;                                                          //0x18
	VOID* EntryPoint;                                                       //0x1c
	ULONG SizeOfImage;                                                      //0x20
	UNICODE_STRING FullDllName;                                     //0x24
	UNICODE_STRING BaseDllName;                                     //0x2c
	ULONG Flags;                                                            //0x34
																			可以不要之后的内容
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

PVOID RegistrationHandle = NULL;

EXTERN_C PUCHAR NTAPI PsGetProcessImageFileName(PEPROCESS Process);


//前回调
OB_PREOP_CALLBACK_STATUS PreCallBack(
	_In_ PVOID RegistrationContext,
	_Inout_ POB_PRE_OPERATION_INFORMATION OperationInformation
	){
	//获取当前进程
	PEPROCESS CurProcess = IoGetCurrentProcess();
	//获取被操作的进程
	PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;
	//获取当前进程名字
	PUCHAR CurName = PsGetProcessImageFileName(CurProcess);
	//获取目标进程名字
	PUCHAR TargetName = PsGetProcessImageFileName(TargetProcess);
	//获取当前操作
	PUCHAR CurOperation = OperationInformation->Operation;
	//获取Flags
	ULONG KernelMode = OperationInformation->Flags&0x1;
	PUCHAR CurOperationName = NULL;
	if (CurOperation == 0x1) {
		CurOperationName = "Create";
		DbgPrint("[+] CurrentName is %s,TargeName is %s,Operation is %s in ring%d\n", CurName, TargetName, CurOperationName, KernelMode * 3);
	}
	else if(CurOperation == 0x10) {
		CurOperationName = "Duplicate";
	}
	else {
		CurOperationName = "Create and Duplicate";
	}

	

	if (strstr("my.exe", TargetName)) {
		OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;
	}
	
	return OB_PREOP_SUCCESS;
}
//后回调
VOID PostCallBack(
	_In_ PVOID RegistrationContext,
	_In_ POB_POST_OPERATION_INFORMATION OperationInformation
) {

}

VOID DriverUnload(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) {
	DbgBreakPoint();
	if (RegistrationHandle) {
		ObUnRegisterCallbacks(RegistrationHandle);
	}
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) {
	//初始化回调信息结构
	OB_CALLBACK_REGISTRATION obRegInfo = { 0 };
	obRegInfo.Version = ObGetFilterVersion();
	obRegInfo.OperationRegistrationCount = 1;
	obRegInfo.RegistrationContext = NULL;
	RtlInitUnicodeString(&obRegInfo.Altitude, L"123456");

	//回调函数结构
	OB_OPERATION_REGISTRATION obCallBcakInfo = { 0 };
	obCallBcakInfo.ObjectType = *PsProcessType;
	//OpenProcess和DunmplicateHandle都回调
	obCallBcakInfo.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
	obCallBcakInfo.PreOperation = PreCallBack;
	obCallBcakInfo.PostOperation = PostCallBack;

	PLDR_DATA_TABLE_ENTRY ex = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
	ex->Flags |= 0x20;
	//注册
	obRegInfo.OperationRegistration = &obCallBcakInfo;
	NTSTATUS state = ObRegisterCallbacks(&obRegInfo, &RegistrationHandle);
	DbgPrint("[+] The state is %x\n", state);
	if (NT_SUCCESS(state)) {

	}


	pDriver->DriverUnload = DriverUnload;
	return STATUS_SUCCESS;
}

这里只略微提到两点:1.这类回调如果出现了C0x000022的问题,那么大概率是Flags有问题,Flags检查没过,具体的东西我之前的文章里面说过了

2.PreCallBack和PostCallBack里面一般拦截放在PreCallBack,也就是Create或Delicate对象前。

通过上述代码,我们就可以实现类似杀软那种别人用任务管理器关不掉的效果

为了方便测试,又用的CPU-z,改了下名字

在这里插入图片描述

程序启动之后,加载驱动

这里说一下发现,上了驱动之后,任务管理器一直在不间断的操作几个进程的对象
在这里插入图片描述

这时候我们尝试使用任务管理器结束进程,可以看见我们操作相关对象的时候就被拒绝了,这就达到了我们的目的。

在这里插入图片描述

在这里插入图片描述

最后的问题

这里有一个问题我没有解决,那就是在停止驱动的时候会蓝屏,0x00000039,在取消回调的时候回调句柄是被赋了值的,但是我一直没搞清楚为什么会蓝屏。中间还修改了DriverSection的Flags的值,把它改了回去,但是还是无济于事。

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

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

相关文章

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…

PH热榜 | 2025-06-08

1. Thiings 标语&#xff1a;一套超过1900个免费AI生成的3D图标集合 介绍&#xff1a;Thiings是一个不断扩展的免费AI生成3D图标库&#xff0c;目前已有超过1900个图标。你可以按照主题浏览&#xff0c;生成自己的图标&#xff0c;或者下载整个图标集。所有图标都可以在个人或…

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…

聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇

根据 QYResearch 发布的市场报告显示&#xff0c;全球市场规模预计在 2031 年达到 9848 万美元&#xff0c;2025 - 2031 年期间年复合增长率&#xff08;CAGR&#xff09;为 3.7%。在竞争格局上&#xff0c;市场集中度较高&#xff0c;2024 年全球前十强厂商占据约 74.0% 的市场…

【iOS】 Block再学习

iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…

Mysql故障排插与环境优化

前置知识点 最上层是一些客户端和连接服务&#xff0c;包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念&#xff0c;为通过安全认证接入的客户端提供线程。同样在该层上可…

STM32标准库-ADC数模转换器

文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”&#xff1a;输入模块&#xff08;GPIO、温度、V_REFINT&#xff09;1.4.2 信号 “调度站”&#xff1a;多路开关1.4.3 信号 “加工厂”&#xff1a;ADC 转换器&#xff08;规则组 注入…

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践&#xff0c;很多人以为AI已经强大到不需要程序员了&#xff0c;其实不是&#xff0c;AI更加需要程序员&#xff0c;普通人…

【若依】框架项目部署笔记

参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作&#xff1a; 压缩包下载&#xff1a;http://download.redis.io/releases 1. 上传压缩包&#xff0c;并进入压缩包所在目录&#xff0c;解压到目标…

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…

链式法则中 复合函数的推导路径 多变量“信息传递路径”

非常好&#xff0c;我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题&#xff0c;统一使用 二重复合函数&#xff1a; z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y))​ 来全面说明。我们会展示其全微分形式&#xff08;偏导…

rm视觉学习1-自瞄部分

首先先感谢中南大学的开源&#xff0c;提供了很全面的思路&#xff0c;减少了很多基础性的开发研究 我看的阅读的是中南大学FYT战队开源视觉代码 链接&#xff1a;https://github.com/CSU-FYT-Vision/FYT2024_vision.git 1.框架&#xff1a; 代码框架结构&#xff1a;readme有…

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…

leetcode73-矩阵置零

leetcode 73 思路 记录 0 元素的位置&#xff1a;遍历整个矩阵&#xff0c;找出所有值为 0 的元素&#xff0c;并将它们的坐标记录在数组zeroPosition中置零操作&#xff1a;遍历记录的所有 0 元素位置&#xff0c;将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…

Xcode 16 集成 cocoapods 报错

基于 Xcode 16 新建工程项目&#xff0c;集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…