(转)TDI FILTER 网络过滤驱动完全解析

http://blog.csdn.net/charlesprince/article/details/5924376

  TDI FILTER 过滤驱动的功能一般用来进行整个系统中的所有网络流量的分析,记录和管理,可以实现非常强大的管理功能,这里就将讨论它的设计架构,和具体实现的方法。

  进行系统级网络数据包的过滤,很明显,第一步需要在系统内核中截取到网络数据包,那么在WINDOWS平台下,应该如何实现这样的功能?
  在WINDOWS内核中,数据的通信载体是IRP包,如果希望截取到IRP数据包,当然必须生成核模块以驱动的方式加载至内核之中。如果只是需要用来进行IRP数据包的截取,进而进行数据的分析,及下一步工作的控制。比较合适的方式就是使用TDI FILTER驱动的方式。


它在内核中的结构如图所示:
TDI FILTER ( 你的DRIVER )
TDI DRIVER ( AFD.SYS )

附加至TDI设备的方法:
  在DriverEntry时,生成两个设备,将其附加至(Attach)至Tdi驱动的Udp和Tcp设备,实现IRP包过滤功能,具体代码如下:

#define UDP_DEVICE_NAME   L"//Device//Udp"
#define TCP_DEVICE_NAME   L"//Device//Tcp"
#define TDI_FILTER_DEVICE_NAME   L"//Device//TdiFilter"typedef struct __TDI_FILTER_DEVICE_EXTENSION
{//过滤设备至少需要记录下真正Tdi网络设备的指针,来调用真正的TDI设备功能。
    PDEVICE_OBJECT pTdiDeviceObject; 
} TDI_FILTER_DEVICE_EXTENSION, *PTDI_FILTER_DEVICE_EXTENSION;DriverEntry( PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath )
{UNICODE_STRING TdiDeviceName;......RtlInitUnicodeString( ( PUNICODE_STRING )&TdiDeviceName, UDP_DEVICE_NAME );ntStatus = IoCreateDevice( DriverObject, sizeof( TDI_FILTER_DEVICE_EXTENSION ), //指定设备扩展长度
        NULL, FILE_DEVICE_NETWORK, //网络类型设备0, 0, &DeviceObject ); //生成一个无名、网络类型设备,附加至TDI TCP/UDP设备,实现过滤功能。if( NT_SUCCESS( ntStatus ) ){DeviceObject->Flags |= DO_DIRECT_IO; //生成新的页表将同样的用户内存空间映射至系统虚拟内存空间来进行通信ntStatus = IoAttachDevice( DeviceObject, TdiDeviceName, ( PDEVICE_OBJECT* )DeviceObject->DeviceExtension //附加至的设备的指针将会输出至此参数中,这样就将真正的TDI设备的指针记录在过滤设备的扩展中
        ); }
}

 

TDI驱动的组织结构分为两个部分:
1.庞大的INTERNAL IO CONTROL子功能,包括以下功能:
  TDI_ASSOCIATE_ADDRESS 可以通过它截取出自己和对端的套接字信息,一般就是IP地址+端口号,可以在此IRP功能响应中进行套接字信息的记录。
  TDI_DISASSOCIATE_ADDRESS 它的IRP包是在closesocket函数时发生的,所以如果我们在     TDI_ASSOCIATE_ADDRESS中记录了信息,需要在此IRP的功能响应中取消之前的记录。
  TDI_CONNECT:主动连接
  TDI_LISTEN
  TDI_ACCEPT
  TDI_DISCONNECT
  TDI_SEND 它的IRP包是在调用send函数时发生的,必然,对它的响应将会实现对基于TCP协议的网络上传流量的截取。
  TDI_RECEIVE 它的IRP包是在调用recv函数时发生的,必然,对它的响应将会实现对基于TCP协议的网络下载流量的截取。
  TDI_SEND_DATAGRAM 它的IRP包是在调用sendto函数时发生的,必然,对它的响应将会实现对基于UDP协议的网络上传流量的截取。
  TDI_RECEIVE_DATAGRAM 它的IRP包是在调用recvfrom函数时发生的,必然,对它的响应将会实现对基于UDP协议的网络下载流量的截取。
  TDI_SET_EVENT_HANDLER 它的IRP包是在TDI驱动中注册一些回调用函数,当接收到数据包时,将会首先执行它们,它的具体功能将会在下一步讲述。
  TDI_QUERY_INFORMATION
  TDI_SET_INFORMATION
  TDI_ACTION
  TDI_DIRECT_SEND
  TDI_DIRECT_SEND_DATAGRAM

在TDI_SET_EVENT_HANDLER子功能中,可以注册以下回调涵数:
  TDI_EVENT_CONNECT:被动连接
  TDI_EVENT_DISCONNECT
  TDI_EVENT_ERROR
  TDI_EVENT_RECEIVE 对应于recv函数有返回数据时,将会调用此回调函数。
  TDI_EVENT_RECEIVE_DATAGRAM 对应于recvfrom函数接收到数据时,将会调用此回调函数。
  TDI_EVENT_RECEIVE_EXPEDITED 对应于函数接收到带外数据时,将会调用此回调函数。( 带外数也就是OOB数据, 在全部IRP数据包中会优先进行发送或接收,TCP协议功能 )
  TDI_EVENT_SEND_POSSIBLE

  以下将讲述数据具体传输回调功能的过滤方法


4.实现事件回调函数挂钩的方法:
  响应IRP_MJ_INTERNAL_DEVICE_CONTROL中的TDI_SET_EVENT_HANDLER子功能,记录下原始的注册事件回调函数和参数,但真正注册的是自己的回调函数,来截取所有的事件回调函数调用,实现过滤功能。
具体代码如下:

typedef struct __TDI_EVENT_CONTEXT_WRAP
{DWORD dwEventContextMark; //对自己生成的结构实例加一个四字节的标志,可以不使用。DWORD dwEventType; //记录事件回调函数的类型PVOID pOrgEventHandler; //记录原始的事件回调函数PVOID pOrgEventContext; //记录原始的事件回调函数参数PFILE_OBJECT pAssocAddr; //记录事件回调函数所绑定的本机套接字PDEVICE_OBJEXT pDeviceObjext; //记录注册事件IRP所发送至的TDI设备
} TDI_EVENT_HANDLER_WRAP, *PTDI_EVENT_HANDLER_WRAP;typedef struct __TDI_EVENT_HANDLER_LINK
{LIST_ENTRY List; //将事件回调钩子记录以链表形式进行管理
    PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;
} TDI_EVENT_HANDLER_LIST, *PTDI_EVENT_HANDLER_LIST;LIST_ENTRY g_TdiEventHandlerInfoList;NTSTATUS DeviceInternalIoControl( PDEVICE_OBJECT DeviceObject, PIRP Irp )
{PKIRQL OldIrql;PLIST_ENTRY pListEntry;PIO_STACK_LOCATION IrpSp;PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList;PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap_;PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList_;PTDI_FILTER_DEVICE_EXTENSION pTdiDeviceExtension; pTdiDeviceExtension = ( PTDI_FILTER_DEVICE_EXTENSION )DeviceObject->DeviceExtension;switch( IrpSp->MinorFunction ){case TDI_SET_EVENT_HANDLER:pTdiSetEvent = ( PTDI_REQUEST_KERNEL_SET_EVENT )&pIrpSp->Parameters;if( TDI_EVENT_RECEIVE == pTdiSetEvent->EventType )//|| TDI_EVENT_RECEIVE_EXPEDITED == pTdiSetEvent->EventType || TDI_EVENT_CHAINED_RECEIVE == pTdiSetEvent->EventType || TDI_EVENT_CHAINED_RECEIVE_EXPEDITED == pTdiSetEvent->EventType || TDI_EVENT_RECEIVE_DATAGRAM == pTdiSetEvent->EventType ){pTdiEventHandlerList = NULL;pTdiEventHandlerWrap = NULL;pProcessNetWorkTrafficInfo = NULL;if( NULL == pTdiSetEvent->EventHandler ){//注意!如果注册的事件回调函数是NULL的话,它表示的取消之前曾经注册过的事件回调函数, 这里当然不能挂钩,可以加入释放钩子资源的操作。goto CALL_PDO_DRIVER;}KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql ); //对事件回调函数钩子列表写操作加锁保护
            pListEntry = g_TdiEventHandlerInfoList.Flink;for( ; ; ){if( pListEntry == &g_TdiEventHandlerInfoList ){pTdiEventHandlerWrap_ = NULL;break;}pTdiEventHandlerList_ = ( PTDI_EVENT_HANDLER_LIST )pListEntry;pTdiEventHandlerWrap_ = pTdiEventHandlerList_->pTdiEventHandlerWrap;if( pTdiEventHandlerWrap_->pAssocAddr == pFileObject &&pTdiEventHandlerWrap_->dwEventType == dwEventType ) //如果此本机套接字对象的相应事件回调函数已经存在,则直接对其进行修改就可以了,而不是不断的新建事件件回调钩子
                {pTdiEventHandlerWrap_->pOrgEventHandler = pEventHandler;pTdiEventHandlerWrap_->pOrgEventContext = pEventContext;break;}}if( NULL == pTdiEventHandlerWrap_ ) //没有找到,加入新的事件回调函数钩子
            {pTdiEventHandlerWrap = ( PTDI_EVENT_HANDLER_WRAP )ExAllocatePoolWithTag( NonPagedPool, sizeof( TDI_EVENT_HANDLER_WRAP ), 0 );if( NULL == pTdiEventHandlerWrap ){goto RELEASE_RESOURCE;}pTdiEventHandlerList = ( PTDI_EVENT_HANDLER_LIST )ExAllocatePoolWithTag( NonPagedPool, sizeof( TDI_EVENT_HANDLER_LIST ), 0 );if( NULL == pTdiEventHandlerList ){goto RELEASE_RESOURCE;}pTdiEventHandlerWrap->dwEventContextMark = TDI_EVENT_CONTEXT_MARK;pTdiEventHandlerWrap->dwEventType = dwEventType;pTdiEventHandlerWrap->pOrgEventHandler = pEventHandler;pTdiEventHandlerWrap->pOrgEventContext = pEventContext;pTdiEventHandlerWrap->pAssocAddr = pFileObject;pTdiEventHandlerWrap->pDeviceObject = pTdiDeviceExtension->pTdiDeviceObject;pTdiEventHandlerList->pTdiEventHandlerWrap = pTdiEventHandlerWrap;InsertTailList( &g_TdiEventHandlerInfoList, pTdiEventHandlerList );}else{pTdiEventHandlerWrap = pTdiEventHandlerWrap_;pTdiEventHandlerList = pTdiEventHandlerList_;}KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql ); //释放事件回调钩子列表锁if( TDI_EVENT_RECEIVE == pTdiSetEvent->EventType || TDI_EVENT_RECEIVE_EXPEDITED == pTdiSetEvent->EventType ){pTdiSetEvent->EventHandler = TdiFilterRecvEventHandler; //加入自己的事件过滤回调函数
            }else if( TDI_EVENT_CHAINED_RECEIVE == pTdiSetEvent->EventType ||TDI_EVENT_CHAINED_RECEIVE_EXPEDITED == pTdiSetEvent->EventType ){pTdiSetEvent->EventHandler = TdiFilterChainedRecvHandler;}else{pTdiSetEvent->EventHandler = TdiFilterRecvDatagramEventHandler;}pTdiSetEvent->EventContext = pTdiEventHandlerWrap;IoSkipCurrentIrpStackLocation( pIrp );ntStatus = IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );if( !NT_SUCCESS( ntStatus ) ){if( NULL == pTdiEventHandlerWrap_ ){//如果是新加入的事件回调函数钩子,可以在出错时将其释放, 也可以保留至套接字关闭时,再进行释放KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql ); //对事件回调函数钩子列表写操作加锁保护
                    RemoveEntryList( ( PLIST_ENTRY )pTdiEventHandlerList );ExFreePoolWithTag( pTdiEventHandlerWrap );ExFreePoolWithTag( pTdiEventHandlerList );KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql ); //释放事件回调钩子列表锁
                }}return ntStatus;}break;default:goto CALL_PDO_DRIVER;break;}RELEASE_RESOURCE:if( NULL != pTdiEventHandlerWrap ){ExFreePoolWithTag( pTdiEventHandlerWrap, NonPagedPool );}if( NULL != pTdiEventHandlerList ){ExFreePoolWithTag( pTdiEventHandlerList, NonPagedPool );}CALL_PDO_DRIVER:IoSkipCurrentIrpStackLocation( pIrp );return IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
}

 

  以上对事件回调函数加入了钩子,下一步,必须考虑对其释放的问题,否则,当原始回调函数对应的套接字释放后,你的系统将会崩溃,以下为具体代码:

在套接字关闭后,要在IRP_MJ_CLEANUP功能函数中将相关的事件回调钩子释放掉:

NTSTATUS TdiFilterCleanUp(PDEVICE_OBJECT DeviceObject, PIRP pIrp )
{NTSTATUS ntStatus;KIRQL OldIrql;PTDI_EVENT_HANDLER_LIST pTdiEventHandlerList;PTDI_EVENT_HANDLER_WRAP pTdiEventHandlerWrap;PFILE_OBJECT pFileObject;TDI_FILTER_DEVICE_EXTENSION *pDeviceExtension;PIO_STACK_LOCATION pIrpSp;pDeviceExtension = ( TDI_FILTER_DEVICE_EXTENSION* )DeviceObject->DeviceExtension;pIrpSp = IoGetCurrentIrpStackLocation( pIrp );pFileObject = pIrpSp->FileObject;...//如果是主控制设备,要将调用IoCompleteIrp完成Irp, 如果是过滤设备,调用PDO设备驱动
IoSkipCurrentIrpStackLocation( pIrp );ntStatus = IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );if( !NT_SUCCESS( ntStatus ) ){DebugPrintEx( CLEANUP_INFO,"netmon TdiFilterCleanUp IoCallDriver return ERROR/n" );return ntStatus;}//下一步,释放套接字对应的事件回调钩子KeAcquireSpinLock( &g_SpLockTdiEventHandlerInfo, &OldIrql );FIND_LIST_AGAIN:pListEntry = g_TdiEventHandlerInfoList.Flink;for( ; ; ){if( pListEntry == &g_TdiEventHandlerInfoList ){break;}pTdiEventHandlerList = ( PTDI_EVENT_HANDLER_LIST )pListEntry;pTdiEventHandlerWrap = pTdiEventHandlerList->pTdiEventHandlerWrap;if( pTdiEventHandlerWrap->pAssocAddr == pFileObject ){RemoveEntryList( pListEntry );ExFreePoolWithTag( pTdiEventHandlerWrap, 0 );ExFreePoolWithTag( pTdiEventHandlerList, 0 );goto FIND_LIST_AGAIN;}pListEntry = pListEntry->Flink;}KeReleaseSpinLock( &g_SpLockTdiEventHandlerInfo, OldIrql );return ntStatus;
}

 

那么,可以在事件回调过滤钩子函数对数据进行处理了

NTSTATUS TdiFilterRecvEventHandler( IN PVOID TdiEventContext,IN CONNECTION_CONTEXT ConnectionContext,IN ULONG ReceiveFlags,IN ULONG BytesIndicated,IN ULONG BytesAvailable,OUT ULONG *BytesTaken,IN PVOID Tsdu,OUT PIRP *IoRequestPacket)
{NTSTATUS ntStatus;PIO_STACK_LOCATION pIrpSp;PTDI_EVENT_HANDLER_WRAP pEventHandlerWrap;PTDI_COMPLETION_WRAP pCompletionWrap;LARGE_INTEGER RecvedDataSize;pEventHandlerWrap = ( PTDI_EVENT_HANDLER_WRAP )TdiEventContext;if( FALSE == g_bFiltering ) //是否进行过滤
    {goto CALL_ORIGINAL_EVENT_HANDLER;}if( FALSE != bStopRecv ){ ntStatus = STATUS_DATA_NOT_ACCEPTED;goto RELEASE_PROCESS_IO_INFO_RETURN;}ntStatus = ( ( ClientEventReceive )pEventHandlerWrap->pOrgEventHandler )( pEventHandlerWrap->pOrgEventContext, ConnectionContext, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket );if( NULL != BytesTaken && 0 != *BytesTaken ){//这里对数据进行处理, 比如可以进行通信数据量的统计
    }if( STATUS_MORE_PROCESSING_REQUIRED != ntStatus ){goto RELEASE_PROCESS_IO_INFO_RETURN;}if( NULL == *IoRequestPacket ){goto RELEASE_PROCESS_IO_INFO_RETURN;}//IoRequestPacket表示当前接收IRP中的数据如果并不完整, 并且认为接下来的数据是有价值,需要接收的话,那么需要自己新建一个IRP包,将其指针传入此参数中,并返回STATUS_MORE_PROCESSING_REQUIRED,通知IO管理不终止此IRP,TDI驱动将继续接收接下来的数据。//所以如果此IRP包存在,可以截取它的信息,具体方法下一步讲述。return ntStatus;CALL_ORIGINAL_EVENT_HANDLER:return ( ( ClientEventReceive )pEventHandlerWrap->pOrgEventHandler )( //直接调用原始的IRP钩子函数,不进行处理pEventHandlerWrap->pOrgEventContext, ConnectionContext, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket);
}

  上面讲述了使用事件回调函数钩子的方式进行通信数据的截取方法,下面讲述直接IRP包数据传输方式,也就是以下4个子功能的截取方法:

  TDI_SEND
  TDI_RECEIVE
  TDI_SEND_DATAGRAM
  TDI_RECEIVE_DATAGRAM

  在IRP_MJ_INTERNAL_DEVICE_CONTROL函数中响应以上子功能时,确认参数DeviceObject为TDI过滤设备,对所有截取到的IRP加入自己的完成函数,在此IRP被完成时( 调用IoCompleteRequest )此完成函数被调用,取得IRP处理的返回结果,进行处理。具体数据的处理对应于TDI_SEND子功能可以在IoCallDriver之前得到,因为它是应用程序传给你的,而TDI_RECEIVE子功能,应该在TDI事件回调函数或Completion回调函数中取得。
相关代码如下:

typedef struct __TDI_COMPLETION_WRAP
{...//可以加入用来记录/处理数据的成员, 比如通信标志, 流量统计等
  PIO_COMPLETION_ROUTINE pCompletionRoutine;LPVOID pContext;
} TDI_COMPLETION_WRAP, *PTDI_COMPLETION_WRAP;

加入自定义的CompletionRoutine的方法:

if( TDI_SEND == MinorFunction ||TDI_SEND_DATAGRAM == MinorFunction || TDI_RECEIVE == MinorFunction || TDI_RECEIVE_DATAGRAM == MinorFunction )
{if( TDI_RECEIVE == MinorFunction && TDI_RECEIVE_PEEK == ( ULONG )pIrpSp->Parameters.Others.Argument2 ){//TDI_RECEIVE_PEEK不会真正接收数据,可以不需要对其进行过滤。goto SKIP_CURRENT_STACK_LOCATION;}pCompletionWrap = ( PTDI_COMPLETION_WRAP )ExAllocateFromNPagedLookasideList( &g_CompletionWrapList ); //可以使用链表或HASH等数据结构来管理所有的CompletionRoutine包装信息,这里使用了NPAGED_LOOKASIDE_LIST,它的优势在于系统中所有的NPAGED_LOOKASIDE_LIST资源的最大占用量将会被内存管理器动态管理if( NULL == pCompletionWrap ){goto SKIP_CURRENT_STACK_LOCATION;}//这里可以设置CompletionRoutine的具体工作参数,比如具体操作的类型,原始的Completion函数等,在用户层传送至的IRP中是不会设置CompletionRoutine函数的,但其它驱动传送至的IRP中可能会进行设置,如在Receive事件回调函数中的IoRequestPacket参数IoCopyCurrentIrpStackLocationToNext( pIrp ); //设置下一个设备栈工作参数
IoSetCompletionRoutine( pIrp, TdiFilterCompletion, pCompletionWrap, TRUE, TRUE, TRUE);//这里就为这个IRP加入自己的CompletionRoutine函数goto CALL_PDO_DRIVER;SKIP_CURRENT_STACK_LOCATION:IoSkipCurrentIrpStackLocation( pIrp );CALL_PDO_DRIVER:return IoCallDriver( pDeviceExtension->pTdiDeviceObject, pIrp );
}

具体的Completion函数的工作:

NTSTATUS TdiFilterCompletion( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, LPVOID pContext )
{NTSTATUS ntStatus;PTDI_COMPLETION_WRAP pCompletionWrap;LARGE_INTEGER TransferredDataSize;PIRP pMasterIrp;PIO_STACK_LOCATION pIrpSp;ntStatus = pIrp->IoStatus.Status;pCompletionWrap = ( PTDI_COMPLETION_WRAP )pContext;if( NT_SUCCESS( ntStatus ) ){//可以在这里对成功传输的数据进行处理
    }//这里可以调用原始的Completion函数
    RETURN_SUCCESS:return ntStatus;
}

 

  需要注意的是,如果为IRP包加入了CompletionRoutine之后,那么在驱动卸载( Unload )之前,必须保证所有IRP已经执行过此Completion函数, 如果在驱动被从内存中卸载后才执行, 将会使系统崩溃。
处理方法为:
  1.不实现DriverUnload函数,使驱动只有在系统关闭,底层设备被卸载时,才能完成真正的卸载。这是的一般    FILTER驱动的工作方式,
  2.使用线程同步的方法保证Completion函数的执行,Windows XP或之后的系统也提供了一个API,         SetCompletionRoutineEx来保证驱动在Completion函数完成前不被卸载。

  至此,讲述TDI过滤驱动组织框架,可以为它添加一些更加完善的功能。

转载于:https://www.cnblogs.com/himessage/archive/2013/01/15/2860834.html

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

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

相关文章

云服务器 怎样修改地域,云服务器 怎样修改地域

云服务器 怎样修改地域 内容精选换一换华为云帮助中心,为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档,帮助您快速上手使用华为云服务。华为云帮助中心,为用户提供产品简介、价格说明、…

python 除数总是提示为0_python负数求余不正确?——取模 VS 取余

前天小王同学正在leetcode兴致勃勃的刷题,用java写了一版后又习惯性的用python写了一版,代码逻辑完全一样,但提交答案后居然提示【解答错误】!经过反复调试,发现问题出在涉及求余的地方,python和java得出的…

14天学会安卓开发(附PDF文档和全部示例代码)

前言: 本人也是菜鸟,老鸟看了此文有哪里不好之处敬请指点,本书是根据<<Android应用开发揭秘>>攒写的,如何把一本书读薄,是一件值得思考的问题.相信看过那本书的都知道有500页,哪里才是重点呢?我来告诉你!本书针对有JAVA基础的孩纸们,基本JAVA基础都没的,赶紧去找2…

scanf的用法

今天上论坛看到有人问的关于C语言代码中的问题&#xff0c;发现出现了在scanf函数中带有\n&#xff0c;记得当年刚开始学得时候&#xff0c;也出现过类似的问题&#xff0c;然后看到有人归纳了scanf的用法&#xff0c;就转载到自己的blog以方便以后可以复习。 ***************…

qldump 备份所有表_MySQL中的备份和恢复是怎样执行的?

- 点击上方“中国统计网”订阅我吧&#xff01;-MySQL备份MySQL中的逻辑备份是将数据库中的数据备份为一个文本文件&#xff0c;备份的文件可以被查看和编辑。在MySQL中&#xff0c;使用mysaldump工具来完成备份。有以下3种来调用mysqldump&#xff1a;1. 备份指定的数据库&…

将系统默认记事本替换成自己喜欢的文本编辑器

找寻了这么长时间的编辑器&#xff0c;感觉还是Notepad2最适合自己了。打开效率快 界面简洁 并且还能直接按Esc进行关闭这个最喜欢了。只是它的图标有点丑。。 下面就记录一下 将Notepad2替换成系统记事本。 首先&#xff1a;我们将notepad2.exe重命名为notepad.exe并复制一份名…

linux系统管理与服务器配置高志君_如何在 Linux 上安装、配置 NTP 服务器和客户端?...

你也许听说过这个词很多次或者你可能已经在使用它了。在这篇文章中我将会清晰的告诉你 NTP 服务器和客户端的安装。-- Magesh Maruthamuthu你也许听说过这个词很多次或者你可能已经在使用它了。在这篇文章中我将会清晰的告诉你 NTP 服务器和客户端的安装。之后我们将会了解 Chr…

latex使用

目录 工具支持中文方法eps图片转换给目录加上超链接修改文字颜色C代码式样超链接工具 首先&#xff0c;推荐使用ubuntu下的图形界面tex编辑工具Kile&#xff0c;同时该工具支持界面的编辑&#xff0c;与pdf文件的生成。 由于我只需要简单的使用tex文件生成pdf&#xff0c;就可以…

8位可控加减法器_行测高分技巧-资料分析之有效数字加减法取舍

资料分析是我们行测试卷中得分率较高的一个部分&#xff0c;所以对资料分析这一部分的题目我们必须把握。但是做题过程中&#xff0c;考生碰到一些数字较大&#xff0c;列式复杂的题目&#xff0c;就无从下手&#xff0c;不知如何应对&#xff0c;今天陕西京佳教育就和各位考生…

《linux c编程指南》学习手记4

7.1 文件系统简介 7.1.1 文件 文件类型&#xff1a;普通文件、目录文件、链接文件、设备文件、管道文件 文件权限&#xff1a;r w x 访问权限&#xff1a;文件所有者、文件所有者同组用户、其他用户 7.1.2 索引节点 7.1.3 文件系统 树形层次结构系统&#xff0c;文件最终都归结…

3层vni vxlan_方便业务迁移,大型企业数据中心VXLAN大二层基础,一分钟了解下

一、VXLAN 简介(1)定义RFC7348 定义了 VLAN 扩展方案 VXLAN(Virtual eXtensible Local Area Network)。VXLAN 采用 MAC in UDP(User Datagram Protocol)封装方式&#xff0c;是 NVO3(Network Virtualization overLayer 3)中的一种网络虚拟化技术。(2)目的作为云计算的核心技术之…

android shape的用法总结

<?xml version"1.0" encoding"utf-8"?><shape xmlns:android"http://schemas.android.com/apk/res/android"> <gradient android:startColor"#c0000000" android:endColor"#c0000000" …

设置dns_网络速度缓慢怎么办?轻松一键修改DNS设置让网速提升五倍

不知道大家有没有这种情况的发生&#xff0c;在家上网或者看智能电视&#xff0c;打开一个普通网页&#xff0c;明明自己办的是100M的宽带&#xff0c;结果慢得要死&#xff0c;还动不动弹出各种各样的广告&#xff0c;然后这时你又杀毒&#xff0c;又去清内存&#xff0c;结果…