7.2 Windows驱动开发:内核注册并监控对象回调

在笔者上一篇文章《内核枚举进程与线程ObCall回调》简单介绍了如何枚举系统中已经存在的进程与线程回调,本章LyShark将通过对象回调实现对进程线程的句柄监控,在内核中提供了ObRegisterCallbacks回调,使用这个内核回调函数,可注册一个对象回调,不过目前该函数只能监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止的目的。

ObRegisterCallbacks是Windows操作系统提供的一个内核API函数,它允许开发者注册一个回调函数,用于监控对象的创建、打开、关闭和删除等事件。对象可以是文件、目录、进程、线程、注册表键等等。

当操作系统创建、打开、关闭或删除一个对象时,它会触发注册的回调函数,然后在回调函数中调用开发者定义的代码。开发者可以在回调函数中执行自定义的逻辑,例如记录日志、过滤敏感数据、或者阻止某些操作。

ObRegisterCallbacks函数提供了多个回调函数的注册,这些回调函数包括:

  • PreOperation: 在操作执行之前被调用,可以阻止或修改操作。
  • PostOperation: 在操作执行之后被调用,可以记录操作结果或者清理资源。

需要注意的是,注册回调函数需要开发者有一定的内核开发经验,并且需要遵守一些约束条件,例如不能阻塞或挂起对象的操作,不能调用一些内核API函数等。

内核注册并监控对象回调ObRegisterCallbacks在安全软件、系统监控和调试工具等领域有着广泛的应用。开发者可以利用这个机制来监控系统对象的使用情况,以保护系统安全。

由于目前对象回调只能监控进程与线程,而这个监控是通过ObjectType这么一个成员控制的,如果成员是PsProcessType则代表监控进程,反之PsThreadType则是监控线程,无论监控进程还是线程都调用ObRegisterCallbacks这个函数来完成注册。

函数ObRegisterCallbacks其微软对他的定义是这样的,用户传入OB_OPERATION_REGISTRATION结构,以及OB_CALLBACK_REGISTRATION回调结构,其中PreOperation则是传入的回调函数,也是最重要的,其次是ObjectType指定成进程回调。

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

首先来实现一个检测的案例,注册一个进程回调对象MyLySharkComObjectCallBack,通过ObRegisterCallbacks注册的回调只需要传入一个填充好的OB_CALLBACK_REGISTRATION回调结构体,以及一个全局句柄即可,这个全局句柄的作用仅仅只是在程序结束时,调用ObUnRegisterCallbacks卸载监控而已,实现代码如下所示。

#include <ntddk.h>
#include <ntstrsafe.h>

PVOID Globle_Object_Handle;

// 绕过签名检测
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
    typedef struct _LDR_DATA
    {
        struct _LIST_ENTRY InLoadOrderLinks;
        struct _LIST_ENTRY InMemoryOrderLinks;
        struct _LIST_ENTRY InInitializationOrderLinks;
        VOID*        DllBase;
        VOID*        EntryPoint;
        ULONG32      SizeOfImage;
        UINT8        _PADDING0_[0x4];
        struct _UNICODE_STRING FullDllName;
        struct _UNICODE_STRING BaseDllName;
        ULONG32      Flags;
    }LDR_DATA, *PLDR_DATA;

    PLDR_DATA ldr;
    ldr = (PLDR_DATA)(pDriverObj->DriverSection);
    ldr->Flags |= 0x20;
}

// 自定义回调
OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
    DbgPrint("[lyshark] 执行回调函数... \n");
    return STATUS_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    ObUnRegisterCallbacks(Globle_Object_Handle);
    DbgPrint("回调卸载完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    BypassCheckSign(Driver);

    OB_OPERATION_REGISTRATION Base;                          // 回调函数结构体
    OB_CALLBACK_REGISTRATION CallbackReg;                    // 回调函数

    CallbackReg.RegistrationContext = NULL;                  // 注册上下文(你回调函数返回参数)
    CallbackReg.Version = OB_FLT_REGISTRATION_VERSION;       // 注册回调版本
    CallbackReg.OperationRegistration = &Base;               // 回调结构体
    CallbackReg.OperationRegistrationCount = 1;              // 操作计数(下钩数量)

    RtlUnicodeStringInit(&CallbackReg.Altitude, L"600000");   // 长度
    Base.ObjectType = PsProcessType;                          // 进程操作类型.此处为进程操作
    Base.Operations = OB_OPERATION_HANDLE_CREATE;             // 操作句柄创建
    Base.PreOperation = MyLySharkComObjectCallBack;           // 你自己的回调函数
    Base.PostOperation = NULL;

    // 注册回调
    if (ObRegisterCallbacks(&CallbackReg, &Globle_Object_Handle))
    {
        DbgPrint("[lyshark message] 回调注册成功...");
    }
    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

当驱动程序被加载以后,一旦有进程运行则会执行我们自己的MyLySharkComObjectCallBack回调,而在回调函数内则可以执行任意功能,运行如下所示。

如上所示只是演示基本的回调申请流程,回调函数通常需要包含两个值,其一RegistrationContext用于标注上下文,其二POB_PRE_OPERATION_INFORMATION则用于标注进程或者线程创建的信息结构体。

OB_PREOP_CALLBACK_STATUS MyLySharkComObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)

那么如何实现拦截进程启动这个功能呢,我们可以在回调函数中写入以下代码进行拦截。

  • CreateHandleInformation.DesiredAccess 将打开句柄的权限清零
  • CreateHandleInformation.OriginalDesiredAccess 判断是否终止
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
    DbgPrint("lyshark.exe 进程打开 \n");
    pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
    if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
    {
        pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
    }
}

拦截进程创建核心代码如下所示。

#include <ntddk.h>
#include <ntstrsafe.h>

#define PROCESS_TERMINATE 0x1

// 导出两个API
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);

// 全局句柄
PVOID Globle_Object_Handle = NULL;

// 绕过签名检测
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
    typedef struct _LDR_DATA
    {
        struct _LIST_ENTRY InLoadOrderLinks;
        struct _LIST_ENTRY InMemoryOrderLinks;
        struct _LIST_ENTRY InInitializationOrderLinks;
        VOID*        DllBase;
        VOID*        EntryPoint;
        ULONG32      SizeOfImage;
        UINT8        _PADDING0_[0x4];
        struct _UNICODE_STRING FullDllName;
        struct _UNICODE_STRING BaseDllName;
        ULONG32      Flags;
    }LDR_DATA, *PLDR_DATA;

    PLDR_DATA ldr;
    ldr = (PLDR_DATA)(pDriverObj->DriverSection);
    ldr->Flags |= 0x20;
}

// 判断是否是需要保护的进程
BOOLEAN CheckProcess(PEPROCESS eprocess)
{
    char *Name = PsGetProcessImageFileName(eprocess);
    if (!_stricmp("lyshark.exe", Name))
        return TRUE;
    else
        return FALSE;
}

// 进程回调
OB_PREOP_CALLBACK_STATUS MyLySharkProcessObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
    HANDLE pid;

    // 只取出进程回调
    if (pOperationInformation->ObjectType != *PsProcessType)
    {
        return OB_PREOP_SUCCESS;
    }

    // 得到所有进程的ID
    pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
    // DbgPrint("进程PID= %ld \n", pid);

    UNREFERENCED_PARAMETER(RegistrationContext);

    // 验证是否是需要的进程
    if (CheckProcess((PEPROCESS)pOperationInformation->Object))
    {
        // 创建句柄
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
        {
            DbgPrint("lyshark.exe 进程打开事件 \n");
            pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
            {
                DbgPrint("[LyShark Message] 拦截进程打开 \n");
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
            }
        }
        // 复制句柄
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
        {
            DbgPrint("lyshark.exe 进程被关闭 \n");
            pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
            {
                pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
            }
        }
    }
    return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    ObUnRegisterCallbacks(Globle_Object_Handle);
    DbgPrint("回调卸载完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    BypassCheckSign(Driver);

    OB_OPERATION_REGISTRATION ob_process_callback;
    OB_CALLBACK_REGISTRATION op_process_operation;

    memset(&ob_process_callback, 0, sizeof(ob_process_callback));
    ob_process_callback.ObjectType = PsProcessType;
    ob_process_callback.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    ob_process_callback.PreOperation = MyLySharkProcessObjectCallBack;
    ob_process_callback.PostOperation = NULL;

    RtlUnicodeStringInit(&op_process_operation.Altitude, L"600000");
    op_process_operation.RegistrationContext = NULL;
    op_process_operation.Version = OB_FLT_REGISTRATION_VERSION;
    op_process_operation.OperationRegistration = &ob_process_callback;
    op_process_operation.OperationRegistrationCount = 1;

    // 注册进程回调
    if (ObRegisterCallbacks(&op_process_operation, &Globle_Object_Handle))
    {
        DbgPrint("进程回调注册成功...");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

加载这个驱动,当有进程被创建时,则首先判断是否是lyshark.exe如果是则直接禁止打开,也就是终止掉。

同理进程可以被拦截,那么如果增加更多的过滤条件,则线程同样可以被拦截,拦截线程代码如下所示。

#include <ntddk.h>
#include <ntstrsafe.h>

#define THREAD_TERMINATE2 0x1

// 导出两个API
NTKERNELAPI PEPROCESS IoThreadToProcess(PETHREAD Thread);
NTKERNELAPI char* PsGetProcessImageFileName(PEPROCESS Process);

// 全局句柄
PVOID Globle_Object_Handle = NULL;

// 绕过签名检测
void BypassCheckSign(PDRIVER_OBJECT pDriverObj)
{
    typedef struct _LDR_DATA
    {
        struct _LIST_ENTRY InLoadOrderLinks;
        struct _LIST_ENTRY InMemoryOrderLinks;
        struct _LIST_ENTRY InInitializationOrderLinks;
        VOID*        DllBase;
        VOID*        EntryPoint;
        ULONG32      SizeOfImage;
        UINT8        _PADDING0_[0x4];
        struct _UNICODE_STRING FullDllName;
        struct _UNICODE_STRING BaseDllName;
        ULONG32      Flags;
    }LDR_DATA, *PLDR_DATA;

    PLDR_DATA ldr;
    ldr = (PLDR_DATA)(pDriverObj->DriverSection);
    ldr->Flags |= 0x20;
}

// 判断是否是需要保护的进程
BOOLEAN CheckProcess(PEPROCESS eprocess)
{
    char *Name = PsGetProcessImageFileName(eprocess);
    if (!_stricmp("lyshark.exe", Name))
        return TRUE;
    else
        return FALSE;
}

// 线程回调
OB_PREOP_CALLBACK_STATUS MyThreadObjectCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
    PEPROCESS ep;
    PETHREAD et;
    HANDLE pid;

    // 线程过滤
    if (pOperationInformation->ObjectType != *PsThreadType)
    {
        return OB_PREOP_SUCCESS;
    }

    et = (PETHREAD)pOperationInformation->Object;
    ep = IoThreadToProcess(et);
    pid = PsGetProcessId(ep);

    // DbgPrint("线程PID= %ld | TID= %ld \n", pid, PsGetThreadId(et));
    UNREFERENCED_PARAMETER(RegistrationContext);

    if (CheckProcess(ep))
    {
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
        {
            pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
            {
                DbgPrint("[LyShark] 拦截lyshark.exe进程内 %d 线程创建 \n", PsGetThreadId(et));
                pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
            }
        }
        if (pOperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE)
        {
            pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess=0;
            if ((pOperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess & THREAD_TERMINATE2) == THREAD_TERMINATE2)
            {
                pOperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~THREAD_TERMINATE2;
            }
        }
    }
    return OB_PREOP_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    ObUnRegisterCallbacks(Globle_Object_Handle);
    DbgPrint("回调卸载完成... \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    BypassCheckSign(Driver);

    OB_OPERATION_REGISTRATION ob_thread_callback;
    OB_CALLBACK_REGISTRATION op_thread_operation;

    memset(&ob_thread_callback, 0, sizeof(ob_thread_callback));
    ob_thread_callback.ObjectType = PsThreadType;
    ob_thread_callback.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    ob_thread_callback.PreOperation = MyThreadObjectCallBack;
    ob_thread_callback.PostOperation = NULL;

    RtlUnicodeStringInit(&op_thread_operation.Altitude, L"600001");
    op_thread_operation.RegistrationContext = NULL;
    op_thread_operation.Version = OB_FLT_REGISTRATION_VERSION;
    op_thread_operation.OperationRegistration = &ob_thread_callback;
    op_thread_operation.OperationRegistrationCount = 1;

    // 注册进程回调
    if (ObRegisterCallbacks(&op_thread_operation, &Globle_Object_Handle))
    {
        DbgPrint("进程回调注册成功...");
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

这段驱动加载后,如果有新线程被创建,则会被拦截并打印输出,效果图如下。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TZOJ 1405 An easy problem

翻译有些出错&#xff0c;但大概是那个意思 答案&#xff1a; #include <stdio.h> #include <ctype.h> //引用库函数isupper的头文件int main() {int T 0, i 0;scanf("%d", &T); //要输入的行数while (T--) //循环T次{char c;int y 0…

滴滴面试:什么是数据库事务?InnoDB一次事务的执行过程?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如滴滴、阿里、极兔、有赞、shein 希音、百度、网易的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 什么是数据库事务&#xff1f; 说说 InnoDB一次事务的执行过…

Matlab 点云线性指数计算(加权)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 思路其实很简单,即对每个邻近点集中的点,根据其到点集中心的距离进行加权处理(权重函数),之后再基于加权之后的点获取其协方差矩阵,最后再求取其相关的特征值,以此来获取该点的线性指数。相关公式如下所示:…

pytorch中Conv1d、Conv2d与Conv3d详解

1 卷积介绍 1.1 什么是卷积 卷积&#xff08;convolution&#xff09;&#xff0c;是一种运算&#xff0c;你可以类比于加&#xff0c;减&#xff0c;乘&#xff0c;除&#xff0c;矩阵的点乘与叉乘等等&#xff0c;它有自己的运算规则&#xff0c;卷积的符号是星号*。表达式…

TCP简介及特性

1. TCP协议简介 TCP是Transmission Control Protocol的简称&#xff0c;中文名是传输控制协议。它是一种面向连接的、可靠的、基于IP的传输层协议。两个TCP应用之间在传输数据的之前必须建立一个TCP连接&#xff0c;TCP采用数据流的形式在网络中传输数据。TCP为了保证报文传输的…

Python+Requests模拟发送post请求

模拟发送post请求 发送post请求的基础知识dumps和loads 代码示例&#xff1a; # 发送post请求 import requests,json # 发送post请求的基础知识dumps和loads str_dict {name:xiaoming,age:20,sex:男} print(type(str_dict)) str1 json.dumps(str_dict) # 1,json.dumps 是把…

windows判断端口是否在使用的bat脚本

脚本 REM 查询端口是否占用 netstat -ano|findstr 3306 >nul &&echo y >1.log ||echo n >1.log REM 读取文本内容赋值给变量 set /P resu<1.log if %resu% y (echo port in use ) else (echo port not in use ) mysql服务不运行的时候检测效果 mysql服…

【Excel】WPS快速按某列查重数据

查重值 excel列几条数据肉眼可见&#xff0c;如何千万级别数据查验呢&#xff1f;平时很少用&#xff0c;记录一下: 先框选列要验证的数据&#xff0c;然后&#xff1a;开始->条件格式->突出显示单元格规则->重复值 效果

成都理工大学校园《我想假如在这里度过大学生活》火了

近日&#xff0c;网上一篇关于成都理工大学校园环境的《我想假如在这里度过大学生活》火了。文章中的提到的大学环境优美&#xff0c;诗意盎然。一则则假如&#xff0c;带我们领略了校园风光&#xff0c;同时也感受到了大学时代的美好。 美丽的图书馆、阳光明媚的操场&#xff…

HNU-计算机网络-讨论课2

第二次 有关网络智能、安全以及未来网络的讨论 一、必选问题&#xff08;每组自由选择N个&#xff0c;保证组内每人负责1个&#xff09; 网络的发展促进信息的传播&#xff0c;极大提高了人类的感知能力&#xff0c;整个世界都被纳入人类的感知范围。但人们对信息系统以及数据…

Jenkins UI 自动化持续化集成测试

一&#xff1a;安装jenkins 环境 在官网下载msi 直接安装即可 二&#xff1a;设置全局变量 设置allure 路径 三&#xff1a;创建项目 1、创建自由风格项目 2、如果项目在本地&#xff0c;且本地服务器是windows &#xff0c;找到Jenkins安装根目录&#xff0c;寻找config…

Serilog .net下的新兴的日志框架

Serilog .net下的新兴的日志框架 1.Serilog简介 Serilog 是针对 .NET 应用程序的流行日志记录框架。它以其灵活性、易用性和可扩展性而闻名。借助 Serilog&#xff0c;开发人员可以轻松记录应用程序中的事件、错误和消息。它支持结构化日志记录&#xff0c;能够以结构化格式存…

B.牛牛排队伍——模拟双链表

当前位置: 首页 > news >正文 B.牛牛排队伍——模拟双链表 news 2023/12/1 15:14:37 分析 题目其实很简单,就是双链表的增删查,但是刚开始,直接vis标记删除元素,查找一个位置的前一个用的while不断向前找,但是TLE;毕竟O(n*k)的复杂度,一开始没有考虑时间复杂度…

【spring】bean的后处理器

目录 一、作用二、常见的bean后处理器2.1 AutowiredAnnotationBeanPostProcessor2.1.1 说明2.1.2 代码示例2.1.3 截图示例 2.2 CommonAnnotationBeanPostProcessor2.2.1 说明2.2.2 代码示例2.2.3 截图示例 2.3 ConfigurationPropertiesBindingPostProcessor2.3.1 说明2.3.2 代码…

uniapp uni-popup组件在微信小程序中滚动穿透问题

起因 在微信小程序中使用uni-popup组件时&#xff0c;出现滚动穿透&#xff0c;并且uni-popup内部内容不会滚动问题。 解决 滚动穿透 查阅官方文档&#xff0c;发现滚动穿透是由于平台差异性造成的&#xff0c;具体解决可以参照文档禁止滚动穿透 <template><page-…

【动手学深度学习】(七)丢弃法

文章目录 一、理论知识二、代码实现2.1从零开始实现Dropout 【相关总结】np.random.uniform(low&#xff0c;high&#xff0c;size)astypetorch.rand() 一、理论知识 1.动机 一个好的模型需要对输入数据的扰动鲁棒 使用有噪音的数据等价于Tikhonov正则丢弃法&#xff1a;在层…

PVE系列-LVM安装MacOS的各个版本

PVE系列-LVM安装MacOS的各个版本 环境配置大概过程&#xff1a;详细步骤&#xff1a;1.建立安装环境和下载安装工具2. 重启后&#xff0c;执行osx-setup配置虚拟机3. 安装到硬盘&#xff0c;4.设定引导盘&#xff0c;以方便自动开机启动5.打开屏幕共享和系统VNC最后的结果 引子…

软件工程 - 第8章 面向对象建模 - 2 静态建模

静态建模&#xff08;类和对象建模&#xff09; 类和对象模型的基本模型元素有类、对象以及它们之间的关系。系统中的类和对象模型描述了系统的静态结构&#xff0c;在UML中用类图和对象图来表示。 类图由系统中使用的类以及它们之间的关系组成。类之间的关系有关联、依赖、泛…

基于A*的网格地图最短路径问题求解

基于A*的网格地图最短路径问题求解 一、A*算法介绍、原理及步骤二、Dijkstra算法和A*的区别三、A*算法应用场景四、启发函数五、距离六、基于A*的网格地图最短路径问题求解实例分析完整代码 七、A*算法的改进思路 一、A*算法介绍、原理及步骤 A*搜索算法&#xff08;A star al…

Python 批量修改文件名

主要步骤 通过os.listdir查看该文件夹下所有的文件&#xff08;包括文件夹&#xff09;遍历所有文件&#xff0c;如果是文件夹则跳过&#xff0c;或指定跳过指定文件获取文件扩展名按照需求生成新的文件路径文件名进行重命名 代码示例 # -*- coding: utf-8 -*- import osdef…