NDIS的NDIS_PROTOCOL_BLOCK和NDIS_OPEN_BLOCK的介绍

2023-11-03

转载自:http://blog.sina.com.cn/s/blog_4de78d5901000bfd.html

 

本人简单的介绍一种更有效的基于NDIS包拦截技术

大家都知道,NDIS协议驱动程序是通过填写一张NDIS_PROTOCOL_CHARACTERISTICS的表,并调用NDIS API
函数NdisRegisterProtocol进行注册。现在我们来关注一下NDIS_PROTOCOL_CHARACTERISTICS这张表,
这张表中存有所有协议驱动程序与底层的派发函数的入口

如SendHandler,ReceiveHandler,BindAdapterHandler等,当网卡有数据包进入时,会通过表中ReceiveHandle 或ReceivePacketHandler通知协议驱动程序有一个该协议
的数据包进入,反之协议驱动程序是通过SendHandler或SendPacketsHandler函数向网卡驱动发送数据包到网络上去的,有人会奇怪程序中明明不是调用NdisSend或NdisSendPackets函数发送的吗?没错,是这样的,但是你可以看一下NDIS.H的头文件里对这两个函数的定义就知道了,他们都是一个宏定义实际还是通过这表中SendHandler或SendPacketsHandler发送的

#define NdisSend(Status, NdisBindingHandle, Packet)                                     \
{                                                                                       \
    *(Status) =                                                                         \
        (((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.SendHandler)(     \
            ((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.BindingHandle, \
        (Packet));                                                                      \
}

#define NdisSendPackets(NdisBindingHandle, PacketArray, NumberOfPackets)                \
{                                                                                       \
    (((PNDIS_OPEN_BLOCK)(NdisBindingHandle))->NdisCommonOpenBlock.SendPacketsHandler)(  \
        (PNDIS_OPEN_BLOCK)(NdisBindingHandle),                                          \
        (PacketArray),                                                                  \
        (NumberOfPackets));                                                             \
}



现在我们所要做的事情应该很清楚了,只要我们能够将每一个协议程序所填写的NDIS_PROTOCOL_CHARACTERISTICS表里的派发函数指向自己的函数,

我们就能成功的对数据包进行拦截。那么每个协议驱动程序的这张表到底存放在那里呢?太简单了,看一下下面的我对NdisRegisterProtocol重新给出的原型就很明白了。

struct _NDIS_PROTOCOL_BLOCK
{
	PNDIS_OPEN_BLOCK OpenQueue; // queue of opens for this protocol
	REFERENCE Ref;				// contains spinlock for OpenQueue
	UINT Length;				// of this NDIS_PROTOCOL_BLOCK struct
	NDIS50_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics;// handler addresses

	struct _NDIS_PROTOCOL_BLOCK * NextProtocol; // Link to next
	ULONG MaxPatternSize;
	#if defined(NDIS_WRAPPER)
	//
	// Protocol filters
	//
	struct _NDIS_PROTOCOL_FILTER * ProtocolFilter[NdisMediumMax+1];
	WORK_QUEUE_ITEM WorkItem;	// Used during NdisRegisterProtocol to
	// notify protocols of existing drivers.
	KMUTEX Mutex;				// For serialization of Bind/Unbind requests
	PKEVENT DeregEvent;			// Used by NdisDeregisterProtocol
	#endif
};


以上struct _NDIS_PROTOCOL_BLOCK的定义为网友提供,在DDK的安装包里并没有搜索到,只是搜索到了以下:(它存在于nids.h中)

typedef struct _NDIS_PROTOCOL_BLOCK     NDIS_PROTOCOL_BLOCK, *PNDIS_PROTOCOL_BLOCK;


 

EXPORT
VOID
NdisRegisterProtocol(
    __out   PNDIS_STATUS                      Status,
    __out   PNDIS_HANDLE                      NdisProtocolHandle,
    __in    PNDIS_PROTOCOL_CHARACTERISTICS    ProtocolCharacteristics,
    __in    UINT                              CharacteristicsLength
    );

/*注意NDIS_HANDLE所指向的就是PNDIS_PROTOCOL_BLOCK的结构,不要有什么怀疑。*/


NDIS_PROTOCOL_BLOCK(协议表) 是NDIS维护所有系统中已注册协义的单向链接表字段NextProtocol指向下一个协议表
庆幸的是,当我们注册一新的协议时,NDIS总是会把新注册的协义放在链表的头并返回这张表,所以只要我们注册一个新的协议,通过新协议注册返回的链表头就可以轻而易举的遍历系统中所有协议表。

eg: 以下是一个注册自己的协议的函数

NDIS把新注册的协义放在链表的头并返回这张表,新表指针为phProtocolHandle

NTSTATUS RegisterFakeProtocol( OUT PNDIS_HANDLE phProtocolHandle, IN PUCHAR pszProtocolName )
{
	NDIS40_PROTOCOL_CHARACTERISTICS		ndis40pcFakeProtCharacts;
	NDIS_STATUS							nsRegStatus;

	// Prepare the Protocol Characteristics structure.

	memset( & ndis40pcFakeProtCharacts, 0, sizeof( ndis40pcFakeProtCharacts ) );

	ndis40pcFakeProtCharacts.MajorNdisVersion = 0x4;
	ndis40pcFakeProtCharacts.MinorNdisVersion = 0;
	ndis40pcFakeProtCharacts.Reserved = 0;

	ndis40pcFakeProtCharacts.OpenAdapterCompleteHandler = & FakeProtocol_OpenAdapterComplete;
	ndis40pcFakeProtCharacts.CloseAdapterCompleteHandler = & FakeProtocol_CloseAdapterComplete;
	ndis40pcFakeProtCharacts.SendCompleteHandler = & FakeProtocol_SendComplete;
	ndis40pcFakeProtCharacts.TransferDataCompleteHandler = & FakeProtocol_TransferDataComplete;
	ndis40pcFakeProtCharacts.ResetCompleteHandler = & FakeProtocol_ResetComplete;
	ndis40pcFakeProtCharacts.RequestCompleteHandler = & FakeProtocol_RequestComplete;
	ndis40pcFakeProtCharacts.ReceiveHandler = & FakeProtocol_Receive;
	ndis40pcFakeProtCharacts.ReceiveCompleteHandler = & FakeProtocol_ReceiveComplete;
	ndis40pcFakeProtCharacts.StatusHandler = & FakeProtocol_Status;
	ndis40pcFakeProtCharacts.StatusCompleteHandler = & FakeProtocol_StatusComplete;

	NdisInitializeString( & ndis40pcFakeProtCharacts.Name, pszProtocolName );

	ndis40pcFakeProtCharacts.ReceivePacketHandler = & FakeProtocol_ReceivePacket;
	ndis40pcFakeProtCharacts.BindAdapterHandler = & FakeProtocol_BindAdapter;
	ndis40pcFakeProtCharacts.UnbindAdapterHandler = & FakeProtocol_UnbindAdapter;
	ndis40pcFakeProtCharacts.PnPEventHandler = & FakeProtocol_PnpEvent;
	ndis40pcFakeProtCharacts.UnloadHandler = & FakeProtocol_UnloadProtocol;

	// Register the Fake Protocol with the Fake Handlers.

	NdisRegisterProtocol( & nsRegStatus, phProtocolHandle, & ndis40pcFakeProtCharacts, sizeof( ndis40pcFakeProtCharacts ) );

	if ( nsRegStatus != NDIS_STATUS_SUCCESS )
	{
		* phProtocolHandle = NULL;
		return STATUS_UNSUCCESSFUL;
	}

	// Return to the caller.

	return STATUS_SUCCESS;
}

以上注册的是NDIO 4.0的协议,从D:\WINDDK\7600.16385.1\inc\ddk\ndis.h(6164)有如下定义:

typedef struct _NDIS40_PROTOCOL_CHARACTERISTICS
{
    UCHAR                           MajorNdisVersion;
    UCHAR                           MinorNdisVersion;
    USHORT                          Filler;
    union
    {
        UINT                        Reserved;
        UINT                        Flags;
    };
    OPEN_ADAPTER_COMPLETE_HANDLER   OpenAdapterCompleteHandler;
    CLOSE_ADAPTER_COMPLETE_HANDLER  CloseAdapterCompleteHandler;
    union
    {
        SEND_COMPLETE_HANDLER       SendCompleteHandler;
        WAN_SEND_COMPLETE_HANDLER   WanSendCompleteHandler;
    };
    union
    {
     TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler;
     WAN_TRANSFER_DATA_COMPLETE_HANDLER WanTransferDataCompleteHandler;
    };

    RESET_COMPLETE_HANDLER          ResetCompleteHandler;
    REQUEST_COMPLETE_HANDLER        RequestCompleteHandler;
    union
    {
        RECEIVE_HANDLER             ReceiveHandler;
        WAN_RECEIVE_HANDLER         WanReceiveHandler;
    };
    RECEIVE_COMPLETE_HANDLER        ReceiveCompleteHandler;
    STATUS_HANDLER                  StatusHandler;
    STATUS_COMPLETE_HANDLER         StatusCompleteHandler;
    NDIS_STRING                     Name;

    //
    // Start of NDIS 4.0 extensions.
    //
    RECEIVE_PACKET_HANDLER          ReceivePacketHandler;

    //
    // PnP protocol entry-points
    //
    BIND_HANDLER                    BindAdapterHandler;
    UNBIND_HANDLER                  UnbindAdapterHandler;
    PNP_EVENT_HANDLER               PnPEventHandler;
    UNLOAD_PROTOCOL_HANDLER         UnloadHandler;

} NDIS40_PROTOCOL_CHARACTERISTICS;

需要说明的在其实nids.h中都只是定义NDIS 3.0  协议:

如D:\WINDDK\3790.1830\inc\ddk\wxp\ndis.h(9370):

typedef struct _NDIS30_PROTOCOL_CHARACTERISTICS
{
    UCHAR                           MajorNdisVersion;
    UCHAR                           MinorNdisVersion;
    USHORT                          Filler;
    union
    {
        UINT                        Reserved;
        UINT                        Flags;
    };
    OPEN_ADAPTER_COMPLETE_HANDLER   OpenAdapterCompleteHandler;
    CLOSE_ADAPTER_COMPLETE_HANDLER  CloseAdapterCompleteHandler;
    union
    {
        SEND_COMPLETE_HANDLER       SendCompleteHandler;
        WAN_SEND_COMPLETE_HANDLER   WanSendCompleteHandler;
    };
    union
    {
     TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler;
     WAN_TRANSFER_DATA_COMPLETE_HANDLER WanTransferDataCompleteHandler;
    };

    RESET_COMPLETE_HANDLER          ResetCompleteHandler;
    REQUEST_COMPLETE_HANDLER        RequestCompleteHandler;
    union
    {
        RECEIVE_HANDLER             ReceiveHandler;
        WAN_RECEIVE_HANDLER         WanReceiveHandler;
    };
    RECEIVE_COMPLETE_HANDLER        ReceiveCompleteHandler;
    STATUS_HANDLER                  StatusHandler;
    STATUS_COMPLETE_HANDLER         StatusCompleteHandler;
    NDIS_STRING                     Name;
} NDIS30_PROTOCOL_CHARACTERISTICS;

 

 

顺便说一句NDISREGISTERPROTOCOL为NDIS_PROTOCOL_BLOCK所分配的内存是NonPagedPool类型的。对于核心DRIVER来说,核心区内存是一个线性的内存区,所有核心DRIVER是可以随便访问核心内存区的任意地址。所要注意的是不同IRQL级别下对分页和非分页内存。
有人会问这样就行了吗?真的拦截下来了吗?如果有那位仁兄心急现在就写程序的话,准会失望的,因为他会发现结果什么东西都没拦截到或偶而会拦截到一些数据包。为什么?
因为NDIS网卡驱动和协议驱动在发送和接收到数居时并不是调用PNDIS_OPEN_BLOCK->ProtocolCharacteristics里的派发函数。怎么办?
有必要先介绍一下NDIS网卡驱动和协议驱动之间是如何BINDING 的吧:
(1)NdisRegisterProtocol在注册完一个协议后,不久NDIS会通过调用表中BindAdapterHandler派发函数,通知协议对每一个网卡进行BINDING。或者当系统通PNP找到一块新的网卡时也会调用BindAdapterHandler对协议进行BINDING;

(2)协议在BINDING 调用里,会根据自己的需要使用NdisOpenAdapter将自身绑定到适合的网卡。并返回NdisBindingHandle.NdisBindingHandle。

(2、1)NdisBindingHandle.NdisBindingHandle是什么?NdisBindingHandl其实是指向NDIS_OPEN_BLOCK表的一根指针,那么NDIS_OPEN_BLOCK表有什么用呢?当协议顺利的绑定后,每个绑定的网卡和每一个协议之间建立了数据传输的通道,而NDIS_OPEN_BLOCK就是用来维护这一数据通道的表

struct _NDIS_OPEN_BLOCK
{
	PNDIS_MAC_BLOCK				MacHandle;			// pointer to our MAC
	NDIS_HANDLE					MacBindingHandle;	// context when calling MacXX funcs
	PNDIS_ADAPTER_BLOCK			AdapterHandle;		// pointer to our adapter
	PNDIS_PROTOCOL_BLOCK		ProtocolHandle;		// pointer to our protocol
	NDIS_HANDLE					ProtocolBindingContext;// context when calling ProtXX funcs
	PNDIS_OPEN_BLOCK			AdapterNextOpen;	// used by adapter's OpenQueue
	PNDIS_OPEN_BLOCK			ProtocolNextOpen;	// used by protocol's OpenQueue
	PNDIS_OPEN_BLOCK			NextGlobalOpen;
	BOOLEAN						Closing;			// TRUE when removing this struct
	BOOLEAN						Unbinding;			// TRUE when starting to unbind the adapter
	BOOLEAN						NoProtRsvdOnRcvPkt;	// Reflect the protocol_options
	BOOLEAN						ProcessingOpens;
	PNDIS_STRING				BindDeviceName;
	KSPIN_LOCK					SpinLock;			// guards Closing
	PNDIS_STRING				RootDeviceName;

	//
	// These are optimizations for getting to MAC routines.	They are not
	// necessary, but are here to save a dereference through the MAC block.
	//
	union
	{
		SEND_HANDLER			SendHandler;
		WAN_SEND_HANDLER		WanSendHandler;
	};
	TRANSFER_DATA_HANDLER		TransferDataHandler;

	//
	// These are optimizations for getting to PROTOCOL routines.  They are not
	// necessary, but are here to save a dereference through the PROTOCOL block.
	//
	SEND_COMPLETE_HANDLER		SendCompleteHandler;
	TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler;
	RECEIVE_HANDLER				ReceiveHandler;
	RECEIVE_COMPLETE_HANDLER	ReceiveCompleteHandler;

	//
	// Extentions to the OPEN_BLOCK since Product 1.
	//
	union
	{
		RECEIVE_HANDLER			PostNt31ReceiveHandler;
		WAN_RECEIVE_HANDLER		WanReceiveHandler;
	};
	RECEIVE_COMPLETE_HANDLER	PostNt31ReceiveCompleteHandler;

	//
	// NDIS 4.0 extensions
	//
	RECEIVE_PACKET_HANDLER		ReceivePacketHandler;
	SEND_PACKETS_HANDLER		SendPacketsHandler;

	//
	// More NDIS 3.0 Cached Handlers
	//
	RESET_HANDLER				ResetHandler;
	REQUEST_HANDLER				RequestHandler;
	RESET_COMPLETE_HANDLER		ResetCompleteHandler;
	STATUS_HANDLER		 		StatusHandler;
	STATUS_COMPLETE_HANDLER		StatusCompleteHandler;
	REQUEST_COMPLETE_HANDLER	RequestCompleteHandler;
};


上面的表结构可以很清楚的看到这张表是一个单向链接表,并且存放了和PNDIS_OPEN_BLOCK->ProtocolCharacteristics一样的数据收发派发函数。当第N块网卡发送数据包到第N个协议时,就会调用第N个协议与第N个网卡之间建立的NDIS_OPEN_BLOCK表里的SendHandler或SendPacketHandler。所以我们还需要对这张表里的派发函数进行处理(勾挂)。
那么又如何勾挂协议与网卡之间的NDIS_OPEN_BLOCK表呢。我们再回到NDIS_PROTOCOL_BLOCK这张表中,在NDIS_PROTOCOL_BLOCK表中字段PNDIS_OPEN_BLOCK OpenQueue;就是所有该协议所有NDIS_OPEN_BLOCK的表头。通过AdapterNextOpen遍历一下,再勾挂一把。就可以顺利拦截了。

值得注意的是。
1、NDIS_OPEN_BLOCK、NDIS_PROTOCOL_BLOCK这些结构不同NDIS版本是不同的


解决方法是在windows 98和windows95下(ndis 3.1)使用windows98ddk 带的NDIS.H 里的定义;
在windows me下(ndis 5.0或4。0)请使用WINDOWS 98ddk里NDIS.H里的定义nt(ndis4.0)用NTDDK里的定议,以此类推,2000(ndis5.0)。


2、不要重复勾挂同一个函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NDIS的NDIS_PROTOCOL_BLOCK和NDIS_OPEN_BLOCK的介绍 的相关文章

  • C++ 释放结构体使用的所有内存

    快速提问 我已经用谷歌搜索并找到了一些答案 但我有点偏执 所以我想确定一下 考虑这种情况 struct CoordLocation float X float Y float Z int main CoordLocation coord n
  • WPF 相对源行为

    我的理解有些问题RelativeSource绑定行为 下面是绑定的代码Label内容到StackPanel正确标记
  • 如何在Spring-MVC方法中绑定抽象类的子类?

    给定 Spring MVC 控制器中的 保存 方法 RequestMapping value save public void save ModelAttribute MY KEY final MyModel myModel 拥有位于myM
  • 您可以在不使用表单的情况下将数组从 javascript 传递到 asp.net mvc 控制器操作吗?

    我知道 如果我有一个包含多个同名元素的表单 我可以使用它绑定到一个数组 但是有什么方法可以在不使用表单的情况下将数组从 javascript 函数传递到 ASP NET MVC 控制器操作 是的 使用 XmlHttpRequests 您可以
  • 返回结构体数组还是结构体指针数组?

    如果您需要退回struct从函数中 您通常会返回一个指向struct反而 如果您想返回结构数组 建议 返回结构体数组 指向第一个元素的指针 或者返回一个结构体指针数组 我为以下两个选项画了一个图表 1 2 给出以下结构定义 struct v
  • 为什么从 UI 中删除命令源后会调用 CanExecute?

    我试图理解为什么在已从 UI 中删除的命令源上调用 CanExecute 这是一个简化的程序来演示
  • 嵌套绑定和管道转换

    为了减少冗余的 XAML 标记 我尝试获取一般填充的单选按钮类型选择控件 即我使用ItemsControl与枚举为ItemsSource并创建一个 DataTemplate 通过检查项目的枚举值是否与当前设置相同来显示选择了哪个项目 仅使用
  • 属性绑定与属性插值

    我读过一篇关于属性和属性绑定之间差异的文章 据我了解 大多数时候 Angular2 更喜欢属性绑定 因为每次数据更改后 DOM 都会更新 如果我错了 请纠正我 我有一个自定义组件并从父组件中使用它 其中 我有一个 Input named t
  • WPF 绑定 - 空字符串的默认值

    如果绑定字符串为空 是否有标准方法为 WPF 绑定设置默认值或后备值
  • 如何应用结构偏移?

    我有一个结构 typedef struct foo int lengthOfArray1 int lengthOfArray2 int array1 int array2 foo 我需要为整个结构及其数组的内容分配足够的内存 所以假设每个数
  • 如何在 hive 中创建一个空的结构数组?

    我有一个观点Hive 1 1 0 根据条件 它应该返回一个空数组或一个数组struct
  • 当 Button.CommandProperty 为 null 时如何禁用按钮

    简短说明 Button CommandProperty 绑定到 ViewModel 的 SomeObject SomeCommand 属性 当 SomeObject 的 SomeCommand 属性设置为 null 或整个 SomeObje
  • 通过其他结构体成员的偏移指针访问结构体成员是否合法?

    在这两个示例中 通过偏移其他成员的指针来访问结构体成员是否会导致未定义 未指定 实现定义的行为 struct int a int b foo1 0 0 foo1 a 1 1 printf d foo1 b struct int arr 1
  • ToProperty 和 BindTo - 无需订阅即可获取初始值

    我在 NET 4 5 中使用 RXUI 6 和 WPF 当绑定到的 ViewModel 属性由一个支持时 我一直无法获取提供给我的视图的初始值ObservableAsPropertyHelper 根据文档 https github com
  • 将二进制文件读入结构体

    我正在尝试使用 C 读取二进制数据 我拥有有关我想要读取的文件中的数据布局的所有信息 我能够 逐块 读取数据 即将前 40 个字节的数据转换为字符串 然后获取接下来的 40 个字节 由于数据至少有三个略有不同的版本 我想将数据直接读入结构中
  • 如何将异步函数存储在结构中并从结构实例调用它?

    我正在尝试用新的方法来实现这一目标async await句法 std future Futures 和最新版本的 Tokio 我正在使用东京0 2 0 alpha 4和铁锈1 39 0 nightly 我尝试过的不同事情包括 using B
  • DataGridTemplateColumn 内的绑定命令

    我使用命令将视图 包括 XAML 附加到我的 ViewModel 当单击 DataGrid 行上的按钮时 我需要调用命令 我正在为此使用行为 常规命令也有同样的问题 当我单击 DataGrid 上的按钮时 我的命令不会被触发 为了说明问题
  • MvvmCross Android 对话框以编程方式绑定

    我想使用 Android Dialog Cross UI 在我的 MvvmCross 项目中 我的第一个方法是使用 AutoViews 由于此功能还相当年轻 因此替代方案是在 touch 和 Droid 平台上实现对话框 现在我只是为 Dr
  • Marionette.View 中 UI 元素的可用性

    我只是想了解 Backbone Marionette 关于 UI 元素的观点背后的决定 在现有 DOM 元素上实例化 Marionette View 时 如下所示 view new Marionette ItemView el elemen
  • 使用 LINQ XML 绑定组合框

    请原谅我的英语不好 那不是我的母语 我是 WPF 和 LINQ 的初学者 从 3 天开始 也是 C 的临时用户 昨天 我一整天都在尝试解决我的问题并阅读了一些文档 但我的代码中的错误仍然存 在 我将 XElement 传递给绑定其内容的控件

随机推荐

  • springboot整合Redis时spring.redis.database参数不生效

    问题描述 配置配件中配置redis的database参数无论配置什么值时都是默认的0 网上查阅大量资料没有查到原因 解决 在网上找到了此网友的回答 虽然没有直接帮助我们解决问题 但给我提供了解决问题的思路 从这图阔以看出redis的data
  • PCL分割方法:区域生长分割算法(RegionGrowing)

    转载 有梦想的田园犬 https blog csdn net AmbitiousRuralDog article details 80267519
  • 数字信号处理基础----傅里叶级数

    1 傅里叶级数的余弦形式 1 1 正交的三角函数集 三角函数集 1 2 2 1 2 3 内的函数在区间 上彼此正交 也即 任意两个不同的函数的内积为0 函数和自身的内积不为零 因此 函数可以由该正交函数集唯一的表示 1 2 傅里叶级数的定义
  • vite+vue3打包部署问题

    最近使用vite vue3写了个小的demo 发现打包部署后页面出不来 如果是正常把包放在服务器的根目录中 项目页面是可以打开的 但是我要部署的是根目录dist包里面 外面多了一层文件夹 解决 vite config ts文件 base z
  • 横向手风琴效果

    html
  • 华硕a501lb5200加内存和固盘并装上win7系统并设置固盘为第一启动

    华硕a501lb5200加内存和固盘并装上win7系统并设置固盘为第一启动 最近入手一只华硕a501lb5200 然后某宝上买内存 固盘 接着拆机加内存和固盘并装上win7系统 于是想分享下自己的经验 大家多多补充 1 拆机加内存和固盘 内
  • Redis集群主从复制不生效的问题分析及解决

    一 集群信息 Redis版本 5 0 集群规模 三主三从 二 基本情况 在项目中为了达到高可用的目的 使用了Redis集群 搭建过程同Redis Cluster集群原理 三主三从交叉复制实战 故障切换 但是在实际使用中发现 集群方式比单点模
  • discuz未登录情况下首页tdk显示“首页”

    问题 discuz未登录状态下首页tdk与后台设置的不符 如图问题keywords和description变成了门户 也有部分变成了首页 解决办法 用编辑器打开 source class helper下的helper seo php文件 找
  • LeetCode#88. 合并两个有序数组(Python)

    题目 来源力扣 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2 另有两个整数 m 和 n 分别表示 nums1 和 nums2 中的元素数目 请你 合并 nums2 到 nums1 中 使合并后的数组同样按 非递减顺序
  • 很全的 Java 权限认证框架

    今天给大家推荐的这个开源项目超级棒 可能是史上功能最全的 Java 权限认证框架 这个开源项目就是 sa token Sa Token是什么 sa token是一个轻量级Java权限认证框架 主要解决 登录认证 权限认证 Session会话
  • FATFS:一个兼容windows的嵌入式文件系统API使用详解

    FATFS 一个兼容windows的嵌入式文件系统API使用详解 目录 FATFS 一个兼容windows的嵌入式文件系统API使用详解 1 API分类 2 常用API说明 2 1 挂载文件系统与解除挂载 2 2 文件操作 2 2 1 文件
  • arduino 1 读取电机编码器值

    define BAUDRATE 115200 define LEFT 0 左轮 define RIGHT 1 右轮 define FORWARDS true define BACKWARDS false 如果一个变量所在的代码段可能会意外地
  • vue3项目打开本地pdf文件实现方法

    vue3项目打开本地pdf文件实现方法 效果图 引入pdf插件 pdf页面封装 pdf存放目录 结语 效果图 引入pdf插件 注意一定要这个版本 不然会报错key split at is not a function npm install
  • 深度学习入门笔记之VggNet网络

    VGGNet是由牛津大学的视觉几何组 Visual Geometry Group 和谷歌旗下DeepMind团队的研究员共同研发提出的 获得了ILSVRC 2014 2014年ImageNet图像分类竞赛 的第二名 将 Top 5错误率降到
  • 数据库事务的四大特性以及事务的隔离级别

    本篇讲诉数据库中事务的四大特性 ACID 并且将会详细地说明事务的隔离级别 1 数据库事务的四大特性 如果一个数据库声称支持事务的操作 那么该数据库必须要具备以下四个特性 1 1 原子性 Atomicity 原子性是指事务包含的所有操作要么
  • lvgl实现动态切换横竖屏

    有两种方式 一种是通过lvgl自带的软件选择 但是这个效率很慢 而且只支持90度 180度 270度的旋转 不一定达到想要的效果 我需要实现的是这种效果 软件旋转没有办法实现 旋转后会镜像过去 而且如果你的屏幕不是等比例的 比如240 24
  • upload-labs pass02-05攻略(详细)

    pass 02 进入关卡 查看提示和源码 根据源代码我们可以发现 这一关是对文件类型验证 也就是验证MIME信息 接下来我们进行文件上传 使用burpsuit抓包 将Content Type修改为允许上传的类型 image jpeg ima
  • ERROR - Connection is read-only.

    今天在serviceImpl的查询中 调用了一样更新的操作 结果出现如下错误 ERROR Connection is read only Queries leading to data modification are not allowe
  • vi笔记3——vi之快速移动

    vi笔记3 vi之快速移动 VI快速移动主要包含以下内容 This chapter covers Movement by screens Movement by text blocks Movement by searches for pa
  • NDIS的NDIS_PROTOCOL_BLOCK和NDIS_OPEN_BLOCK的介绍

    转载自 http blog sina com cn s blog 4de78d5901000bfd html 本人简单的介绍一种更有效的基于NDIS包拦截技术 大家都知道 NDIS协议驱动程序是通过填写一张NDIS PROTOCOL CHA