zoukankan      html  css  js  c++  java
  • 开源项目之防火墙 tdifw

    tdifw是windows防火墙软件(TDI层驱动过滤),负责监控网络监听与连接、以及过滤信息。

    源码在src目录, 程序在Bin目录,执行根目录下的批处理文件也可以,
    具体步骤如下: 
    1. 运行install.bat 
    2. 根据你机器的配置情况,编辑%SystemRoot%system32driversetc difw.conf配置文件 
    3. 重新启动计算机 

    主程序源码是win32的,就9个目标文件,不包含驱动部分,项目如图:

    程序主要源码分析:

    1. int main(int argc, char **argv)  
    2. {  
    3.     static SERVICE_TABLE_ENTRY dispatch_table[] = {  
    4.         {"tdifw", service_main},  
    5.         {NULL, NULL}  
    6.     };  
    7.   
    8.     _LEAK_CHECK;  
    9.   
    10.     //模拟参数   
    11.     argc = 3;  
    12.     argv[0]="tdifw";  
    13.     argv[1]="install";  
    14.     argv[2]="tdifw_drv.sys";  
    15.   
    16.     if (argc >= 2)   
    17.     {  
    18.         const char *param = argv[1];  
    19.           
    20.         if (strcmp(param, "install") == 0)   
    21.         {  
    22.             if (argc < 3)   
    23.             {  
    24.                 fprintf(stderr, "Use: tdifw install <config> ");  
    25.                 return -1;  
    26.             }  
    27.           
    28.             //加载驱动服务   
    29.             install_service(argv[2]);  
    30.           
    31.         }   
    32.         else if (strcmp(param, "remove") == 0)   
    33.         {  
    34.             //移除驱动服务   
    35.             remove_service();  
    36.         } else if (strcmp(param, "debug") == 0)   
    37.         {  
    38.   
    39.             if (argc < 3)   
    40.             {  
    41.                 fprintf(stderr, "Use: tdifw debug <config> ");  
    42.                 return -1;  
    43.             }  
    44.   
    45.             if (start(argv[2]))   
    46.             {  
    47.                 printf("press enter to exit... ");  
    48.                 getchar();  
    49.                 printf("exiting... ");  
    50.                 //停止 释放资源   
    51.                 stop();  
    52.             }  
    53.   
    54.         } else if (strcmp(param, "listen") == 0)   
    55.         {       // tdifw specific   
    56.             //枚举监听   
    57.             enum_listen();  
    58.         } else if (strcmp(param, "conn") == 0)  
    59.         {       // tdifw specific   
    60.             //枚举连接   
    61.             enum_connect();  
    62.         } else   
    63.         {  
    64.             fprintf(stderr, "Use: tdifw install|remove|debug|listen|conn ");  
    65.         }  
    66.     }  
    67.     else   
    68.     {  
    69.         g_console = FALSE;  
    70.   
    71.         // 连接程序主线程到服务控制管理程序   
    72.         if (!StartServiceCtrlDispatcher(dispatch_table))  
    73.             winerr("main: StartServiceCtrlDispatcher");  
    74.   
    75.     }  
    76.   
    77.     return 0;  
    78. }  
    int main(int argc, char **argv)
    {
        static SERVICE_TABLE_ENTRY dispatch_table[] = {
            {"tdifw", service_main},
            {NULL, NULL}
        };
    
    	_LEAK_CHECK;
    
    	//模拟参数
    	argc = 3;
    	argv[0]="tdifw";
    	argv[1]="install";
    	argv[2]="tdifw_drv.sys";
    
    	if (argc >= 2) 
    	{
    		const char *param = argv[1];
    		
    		if (strcmp(param, "install") == 0) 
    		{
    			if (argc < 3) 
    			{
    				fprintf(stderr, "Use: tdifw install <config>
    ");
    				return -1;
    			}
    		
    			//加载驱动服务
    			install_service(argv[2]);
    		
    		} 
    		else if (strcmp(param, "remove") == 0) 
    		{
    			//移除驱动服务
    			remove_service();
    		} else if (strcmp(param, "debug") == 0) 
    		{
    
    			if (argc < 3) 
    			{
    				fprintf(stderr, "Use: tdifw debug <config>
    ");
    				return -1;
    			}
    
    			if (start(argv[2])) 
    			{
    				printf("press enter to exit...
    ");
    				getchar();
    				printf("exiting...
    ");
    				//停止 释放资源
    				stop();
    			}
    
    		} else if (strcmp(param, "listen") == 0) 
    		{		// tdifw specific
    			//枚举监听
    			enum_listen();
    		} else if (strcmp(param, "conn") == 0)
    		{		// tdifw specific
    			//枚举连接
    			enum_connect();
    		} else 
    		{
    			fprintf(stderr, "Use: tdifw install|remove|debug|listen|conn
    ");
    		}
    	}
    	else 
    	{
    		g_console = FALSE;
    
    		// 连接程序主线程到服务控制管理程序
    		if (!StartServiceCtrlDispatcher(dispatch_table))
    			winerr("main: StartServiceCtrlDispatcher");
    
    	}
    
    	return 0;
    }
    1. //获得驱动文件所在路径 则开启 否则退出   
    2. void install_service(const char *config)  
    3. {  
    4.     SC_HANDLE   schService;  
    5.     SC_HANDLE   schSCManager;  
    6.   
    7.     CHAR szPath[MAX_PATH];  
    8.   
    9.     //从注册表中获得信息   
    10.     AddEventSource("tdifw");  
    11.   
    12.     if (GetModuleFileName(NULL, szPath, sizeof(szPath)) == 0) {  
    13.         winerr("install_service: GetModuleFileName");  
    14.         return;  
    15.     }  
    16.   
    17.     //建立了一个连接到服务控制管理器,并打开指定的数据库。   
    18.     schSCManager = OpenSCManager(  
    19.                         NULL,                   // machine (NULL == local)   
    20.                         NULL,                   // database (NULL == default)   
    21.                         SC_MANAGER_ALL_ACCESS); // access required   
    22.   
    23.     if (schSCManager != NULL) {  
    24.   
    25.         //创建一个服务对象并且把它加入到服务管理数据库中   
    26.         schService = CreateService(  
    27.             schSCManager,               // SCManager database   
    28.             "tdifw",                    // name of service   
    29.             "TDI-based open source personal firewall",  // name to display   
    30.             SERVICE_ALL_ACCESS,         // desired access   
    31.             SERVICE_WIN32_OWN_PROCESS,  // service type   
    32.             SERVICE_AUTO_START,         // start type   
    33.             SERVICE_ERROR_NORMAL,       // error control type   
    34.             szPath,                     // service's binary   
    35.             NULL,                       // no load ordering group   
    36.             NULL,                       // no tag identifier   
    37.             NULL,                       // dependencies   
    38.             NULL,                       // LocalSystem account   
    39.             NULL);                      // no password   
    40.   
    41.         if (schService != NULL) {  
    42.             printf("tdifw service has been installed ");  
    43.   
    44.             if (!add_config_info(schService, config))  
    45.                 fprintf(stderr, "Can't store config info! Service will use defaults. ");  
    46.   
    47.             CloseServiceHandle(schService);  
    48.         } else  
    49.             winerr("install_service: CreateService");  
    50.   
    51.         CloseServiceHandle(schSCManager);  
    52.     }  
    53.     else  
    54.         winerr("install_service: OpenSCManager");  
    55. }  
    //获得驱动文件所在路径 则开启 否则退出
    void install_service(const char *config)
    {
    	SC_HANDLE	schService;
    	SC_HANDLE	schSCManager;
    
    	CHAR szPath[MAX_PATH];
    
    	//从注册表中获得信息
    	AddEventSource("tdifw");
    
    	if (GetModuleFileName(NULL, szPath, sizeof(szPath)) == 0) {
    		winerr("install_service: GetModuleFileName");
    		return;
    	}
    
    	//建立了一个连接到服务控制管理器,并打开指定的数据库。
    	schSCManager = OpenSCManager(
    						NULL,					// machine (NULL == local)
    						NULL,					// database (NULL == default)
    						SC_MANAGER_ALL_ACCESS);	// access required
    
    	if (schSCManager != NULL) {
    
    		//创建一个服务对象并且把它加入到服务管理数据库中
    		schService = CreateService(
    			schSCManager,				// SCManager database
    			"tdifw",				    // name of service
    			"TDI-based open source personal firewall",	// name to display
    			SERVICE_ALL_ACCESS, 		// desired access
    			SERVICE_WIN32_OWN_PROCESS,	// service type
    			SERVICE_AUTO_START,		    // start type
    			SERVICE_ERROR_NORMAL,		// error control type
    			szPath, 					// service's binary
    			NULL,						// no load ordering group
    			NULL,						// no tag identifier
    			NULL,						// dependencies
    			NULL,						// LocalSystem account
    			NULL);						// no password
    
    		if (schService != NULL) {
    			printf("tdifw service has been installed
    ");
    
    			if (!add_config_info(schService, config))
    				fprintf(stderr, "Can't store config info! Service will use defaults.
    ");
    
    			CloseServiceHandle(schService);
    		} else
    			winerr("install_service: CreateService");
    
    		CloseServiceHandle(schSCManager);
    	}
    	else
    		winerr("install_service: OpenSCManager");
    }
    1. //移除服务 关闭驱动   
    2. void remove_service(void)  
    3. {  
    4.     SC_HANDLE   schService;  
    5.     SC_HANDLE   schSCManager;  
    6.   
    7.     schSCManager = OpenSCManager(  
    8.                         NULL,                   // machine (NULL == local)   
    9.                         NULL,                   // database (NULL == default)   
    10.                         SC_MANAGER_ALL_ACCESS); // access required   
    11.       
    12.     if (schSCManager != NULL) {  
    13.         schService = OpenService(schSCManager, "tdifw", SERVICE_ALL_ACCESS);  
    14.   
    15.         if (schService != NULL) {  
    16.   
    17.             // try to stop the service   
    18.             if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus)) {  
    19.                 printf("stopping...");  
    20.                 Sleep(1000);  
    21.   
    22.                 while(QueryServiceStatus( schService, &ssStatus)) {  
    23.                     if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) {  
    24.                         printf(".");  
    25.                         Sleep( 1000 );  
    26.                     }  
    27.                     else  
    28.                         break;  
    29.                 }  
    30.   
    31.                 printf(" ");  
    32.   
    33.                 if (ssStatus.dwCurrentState == SERVICE_STOPPED)  
    34.                     printf("stopped ");  
    35.                 else  
    36.                     printf("failed to stop ");  
    37.             }  
    38.   
    39.             // now remove the service   
    40.             if (DeleteService(schService))  
    41.                 printf("service has been removed ");  
    42.             else  
    43.                 winerr("install_service: DeleteService");  
    44.   
    45.             CloseServiceHandle(schService);  
    46.         }  
    47.         else  
    48.             winerr("install_service: OpenService");  
    49.   
    50.         CloseServiceHandle(schSCManager);  
    51.     }  
    52.     else  
    53.         winerr("install_service: OpenSCManager");  
    54. }  
    //移除服务 关闭驱动
    void remove_service(void)
    {
    	SC_HANDLE	schService;
    	SC_HANDLE	schSCManager;
    
    	schSCManager = OpenSCManager(
    						NULL,					// machine (NULL == local)
    						NULL,					// database (NULL == default)
    						SC_MANAGER_ALL_ACCESS); // access required
    	
    	if (schSCManager != NULL) {
    		schService = OpenService(schSCManager, "tdifw", SERVICE_ALL_ACCESS);
    
    		if (schService != NULL) {
    
    			// try to stop the service
    			if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus)) {
    				printf("stopping...");
    				Sleep(1000);
    
    				while(QueryServiceStatus( schService, &ssStatus)) {
    					if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) {
    						printf(".");
    						Sleep( 1000 );
    					}
    					else
    						break;
    				}
    
    				printf("
    ");
    
    				if (ssStatus.dwCurrentState == SERVICE_STOPPED)
    					printf("stopped
    ");
    				else
    					printf("failed to stop
    ");
    			}
    
    			// now remove the service
    			if (DeleteService(schService))
    				printf("service has been removed
    ");
    			else
    				winerr("install_service: DeleteService");
    
    			CloseServiceHandle(schService);
    		}
    		else
    			winerr("install_service: OpenService");
    
    		CloseServiceHandle(schSCManager);
    	}
    	else
    		winerr("install_service: OpenSCManager");
    }
    1. // 从驱动程序中获得网络监听对象   
    2. void enum_listen(void)  
    3. {  
    4.     ULONG size;  
    5.     struct listen_nfo *ln = NULL;  
    6.     int i, n;  
    7.   
    8.     // 从 psapi.dll 中获得链接EnumProcesses、EnumProcessModules、GetModuleFileNameExW函数地址   
    9.     link_psapi();  
    10.   
    11.     /* connect with driver */  
    12.       
    13.     g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE,   
    14.         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);  
    15.     if (g_device == INVALID_HANDLE_VALUE) {  
    16.         winerr(g_nfo_device_name);  
    17.         goto done;  
    18.     }  
    19.   
    20.     /* get list of listening objects */  
    21.   
    22.     size = sizeof(*ln) * 0x10000 * 3;   // this size is good enough :-)   
    23.     ln = (struct listen_nfo *)malloc(size);  
    24.     if (ln == NULL) {  
    25.         perror("malloc");  
    26.         goto done;  
    27.     }  
    28.   
    29.     //与驱动交流 枚举监听操作 获取监听信息   
    30.     if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_LISTEN, NULL, 0,  
    31.         ln, size, &size, NULL)) {  
    32.         winerr("DeviceIoControl");  
    33.         goto done;  
    34.     }  
    35.   
    36.     n = size / sizeof(*ln);  
    37.   
    38.     // sort this list!    
    39.     qsort(ln, n, sizeof(*ln), compare_ln);  
    40.   
    41.     printf("IPProto Address:Port Process (pid) ");  
    42.     printf("------- ------------ --------------------------------------------- ");  
    43.   
    44.     //显示   
    45.     for (i = 0; i < n ; i++) {  
    46.         char *proto, pname[MAX_PATH];  
    47.           
    48.         if (ln[i].ipproto == IPPROTO_TCP)  
    49.             proto = "TCP";  
    50.         else if (ln[i].ipproto == IPPROTO_UDP)  
    51.             proto = "UDP";  
    52.         else if (ln[i].ipproto == IPPROTO_IP)  
    53.             proto = "RawIP";  
    54.         else  
    55.             proto = "?";  
    56.   
    57.         // resolve pid!   
    58.         if (!get_pname_by_pid(ln[i].pid, pname, sizeof(pname)))  
    59.             pname[0] = '';  
    60.   
    61.         printf("%s %d.%d.%d.%d:%d %s (%d) ",  
    62.             proto, PRINT_IP_ADDR(ln[i].addr), ntohs(ln[i].port), pname, ln[i].pid);  
    63.     }  
    64.   
    65. done:  
    66.     free(ln);  
    67.     if (g_device != INVALID_HANDLE_VALUE)  
    68.         CloseHandle(g_device);  
    69. }  
    // 从驱动程序中获得网络监听对象
    void enum_listen(void)
    {
    	ULONG size;
    	struct listen_nfo *ln = NULL;
    	int i, n;
    
    	// 从 psapi.dll 中获得链接EnumProcesses、EnumProcessModules、GetModuleFileNameExW函数地址
    	link_psapi();
    
    	/* connect with driver */
    	
    	g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE, 
    		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    	if (g_device == INVALID_HANDLE_VALUE) {
    		winerr(g_nfo_device_name);
    		goto done;
    	}
    
    	/* get list of listening objects */
    
    	size = sizeof(*ln) * 0x10000 * 3;	// this size is good enough :-)
    	ln = (struct listen_nfo *)malloc(size);
    	if (ln == NULL) {
    		perror("malloc");
    		goto done;
    	}
    
    	//与驱动交流 枚举监听操作 获取监听信息
    	if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_LISTEN, NULL, 0,
    		ln, size, &size, NULL)) {
    		winerr("DeviceIoControl");
    		goto done;
    	}
    
    	n = size / sizeof(*ln);
    
    	// sort this list! 
    	qsort(ln, n, sizeof(*ln), compare_ln);
    
    	printf("IPProto	Address:Port	Process (pid)
    ");
    	printf("-------	------------	---------------------------------------------
    ");
    
    	//显示
    	for (i = 0; i < n ; i++) {
    		char *proto, pname[MAX_PATH];
    		
    		if (ln[i].ipproto == IPPROTO_TCP)
    			proto = "TCP";
    		else if (ln[i].ipproto == IPPROTO_UDP)
    			proto = "UDP";
    		else if (ln[i].ipproto == IPPROTO_IP)
    			proto = "RawIP";
    		else
    			proto = "?";
    
    		// resolve pid!
    		if (!get_pname_by_pid(ln[i].pid, pname, sizeof(pname)))
    			pname[0] = '';
    
    		printf("%s	%d.%d.%d.%d:%d	%s (%d)
    ",
    			proto, PRINT_IP_ADDR(ln[i].addr), ntohs(ln[i].port), pname, ln[i].pid);
    	}
    
    done:
    	free(ln);
    	if (g_device != INVALID_HANDLE_VALUE)
    		CloseHandle(g_device);
    }
    1. // 从驱动程序中获得网络连接信息   
    2. void enum_connect(void)  
    3. {  
    4.     ULONG size;  
    5.     struct tcp_conn_nfo *tn = NULL;  
    6.     int i, n;  
    7.     unsigned __int64 traffic[TRAFFIC_MAX];  
    8.   
    9.     // 从 psapi.dll 中获得链接EnumProcesses、EnumProcessModules、GetModuleFileNameExW函数地址   
    10.     link_psapi();  
    11.   
    12.     /* connect with driver */  
    13.       
    14.     g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE,   
    15.         FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);  
    16.     if (g_device == INVALID_HANDLE_VALUE) {  
    17.         winerr(g_nfo_device_name);  
    18.         goto done;  
    19.     }  
    20.   
    21.     /* get list of listening objects */  
    22.   
    23.     size = sizeof(*tn) * 0x10000 * 3;   // this size is good enough :-)   
    24.     tn = (struct tcp_conn_nfo *)malloc(size);  
    25.     if (tn == NULL) {  
    26.         perror("malloc");  
    27.         goto done;  
    28.     }  
    29.   
    30.         //与驱动交流 枚举监听操作 获取连接信息   
    31.     if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_TCP_CONN, NULL, 0,  
    32.         tn, size, &size, NULL)) {  
    33.         winerr("DeviceIoControl");  
    34.         goto done;  
    35.     }  
    36.   
    37.     n = size / sizeof(*tn);  
    38.   
    39.     // sort this list!   
    40.     qsort(tn, n, sizeof(*tn), compare_tn);  
    41.   
    42.     //顺序输出   
    43.     for (i = 0; i < n ; i++) {  
    44.         char pname[MAX_PATH];  
    45.   
    46.         if (tn[i].state >= TCP_STATE_MAX)  
    47.             tn[i].state = 0;  
    48.           
    49.         // resolve pid!   
    50.         if (!get_pname_by_pid(tn[i].pid, pname, sizeof(pname)))  
    51.             pname[0] = '';  
    52.   
    53.         printf("%s %d.%d.%d.%d:%d %d.%d.%d.%d:%d %s (%d) %u/%u ",  
    54.             g_tcp_states[tn[i].state],  
    55.             PRINT_IP_ADDR(tn[i].laddr), ntohs(tn[i].lport),  
    56.             PRINT_IP_ADDR(tn[i].raddr), ntohs(tn[i].rport),  
    57.             pname, tn[i].pid,  
    58.             tn[i].bytes_out, tn[i].bytes_in);  
    59.     }  
    60.   
    61.     // output traffic counters   
    62.     get_traffic_stats(traffic);  
    63.   
    64.     printf(  
    65.         " "  
    66.         "Traffic counters (out/in): "  
    67.         "    Total:   %I64u/%I64u "  
    68.         "    Counted: %I64u/%I64u ",  
    69.         traffic[TRAFFIC_TOTAL_OUT], traffic[TRAFFIC_TOTAL_IN],  
    70.         traffic[TRAFFIC_COUNTED_OUT], traffic[TRAFFIC_COUNTED_IN]);  
    71.   
    72. done:  
    73.     free(tn);  
    74.     if (g_device != INVALID_HANDLE_VALUE)  
    75.         CloseHandle(g_device);  
    76. }  
    // 从驱动程序中获得网络连接信息
    void enum_connect(void)
    {
    	ULONG size;
    	struct tcp_conn_nfo *tn = NULL;
    	int i, n;
    	unsigned __int64 traffic[TRAFFIC_MAX];
    
    	// 从 psapi.dll 中获得链接EnumProcesses、EnumProcessModules、GetModuleFileNameExW函数地址
    	link_psapi();
    
    	/* connect with driver */
    	
    	g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE, 
    		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    	if (g_device == INVALID_HANDLE_VALUE) {
    		winerr(g_nfo_device_name);
    		goto done;
    	}
    
    	/* get list of listening objects */
    
    	size = sizeof(*tn) * 0x10000 * 3;	// this size is good enough :-)
    	tn = (struct tcp_conn_nfo *)malloc(size);
    	if (tn == NULL) {
    		perror("malloc");
    		goto done;
    	}
    
    		//与驱动交流 枚举监听操作 获取连接信息
    	if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_TCP_CONN, NULL, 0,
    		tn, size, &size, NULL)) {
    		winerr("DeviceIoControl");
    		goto done;
    	}
    
    	n = size / sizeof(*tn);
    
    	// sort this list!
    	qsort(tn, n, sizeof(*tn), compare_tn);
    
    	//顺序输出
    	for (i = 0; i < n ; i++) {
    		char pname[MAX_PATH];
    
    		if (tn[i].state >= TCP_STATE_MAX)
    			tn[i].state = 0;
    		
    		// resolve pid!
    		if (!get_pname_by_pid(tn[i].pid, pname, sizeof(pname)))
    			pname[0] = '';
    
    		printf("%s	%d.%d.%d.%d:%d	%d.%d.%d.%d:%d	%s (%d)	%u/%u
    ",
    			g_tcp_states[tn[i].state],
    			PRINT_IP_ADDR(tn[i].laddr), ntohs(tn[i].lport),
    			PRINT_IP_ADDR(tn[i].raddr), ntohs(tn[i].rport),
    			pname, tn[i].pid,
    			tn[i].bytes_out, tn[i].bytes_in);
    	}
    
    	// output traffic counters
    	get_traffic_stats(traffic);
    
    	printf(
    		"
    "
    		"Traffic counters (out/in):
    "
    		"    Total:   %I64u/%I64u
    "
    		"    Counted: %I64u/%I64u
    ",
    		traffic[TRAFFIC_TOTAL_OUT], traffic[TRAFFIC_TOTAL_IN],
    		traffic[TRAFFIC_COUNTED_OUT], traffic[TRAFFIC_COUNTED_IN]);
    
    done:
    	free(tn);
    	if (g_device != INVALID_HANDLE_VALUE)
    		CloseHandle(g_device);
    }
    


    以上是主要程序的源码,驱动部分共有32个目标文件,如图:

    1. /* 驱动入口 */  
    2. NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,  
    3.             IN PUNICODE_STRING theRegistryPath)  
    4. {  
    5.     NTSTATUS status = STATUS_SUCCESS;  
    6.     int i;  
    7.     UNICODE_STRING name, linkname;  
    8.   
    9.     //内存跟踪初始化   调用了KeInitializeSpinLock(&guard);   
    10.     memtrack_init();  
    11.   
    12.     //初始化锁   
    13.     KeInitializeSpinLock(&g_traffic_guard);  
    14.   
    15. #ifdef USE_TDI_HOOKING   
    16.     KdPrint(("[tdi_fw] WARNING! Using unstable working mode: TDI hooking! "));  
    17. #endif   
    18.   
    19.     status = ot_init();  
    20.     if (status != STATUS_SUCCESS) {  
    21.         KdPrint(("[tdi_fw] DriverEntry: ot_init: 0x%x ", status));  
    22.         goto done;  
    23.     }  
    24.   
    25.     //过滤器初始化   
    26.     status = filter_init();  
    27.     if (status != STATUS_SUCCESS) {  
    28.         KdPrint(("[tdi_fw] DriverEntry: filter_init: 0x%x ", status));  
    29.         goto done;  
    30.     }  
    31.   
    32.     //连接状态初始化   
    33.     status = conn_state_init();  
    34.     if (status != STATUS_SUCCESS) {  
    35.         KdPrint(("[tdi_fw] DriverEntry: conn_state_init: 0x%x ", status));  
    36.         goto done;  
    37.     }  
    38.       
    39.     //分发函数   
    40.     for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)  
    41.         theDriverObject->MajorFunction[i] = DeviceDispatch;  
    42.   
    43. #if DBG   
    44.     // register UnLoad procedure   
    45.     theDriverObject->DriverUnload = OnUnload;  
    46. #endif   
    47.   
    48.     /* create control device and symbolic link */  
    49.   
    50.     RtlInitUnicodeString(&name, L"\Device\tdifw");  
    51.   
    52.     status = IoCreateDevice(theDriverObject,  
    53.                             0,  
    54.                             &name,  
    55.                             0,  
    56.                             0,  
    57.                             TRUE,       // exclusive!   
    58.                             &g_devcontrol);  
    59.     if (status != STATUS_SUCCESS) {  
    60.         KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(control): 0x%x! ", status));  
    61.         goto done;  
    62.     }  
    63.   
    64.     RtlInitUnicodeString(&linkname, L"\??\tdifw");  
    65.   
    66.     //创建了一个符号连接   
    67.     status = IoCreateSymbolicLink(&linkname, &name);  
    68.     if (status != STATUS_SUCCESS) {  
    69.         KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x! ", status));  
    70.         goto done;  
    71.     }  
    72.   
    73.     RtlInitUnicodeString(&name, L"\Device\tdifw_nfo");  
    74.   
    75.     //创建设备对象   
    76.     status = IoCreateDevice(theDriverObject,  
    77.                             0,  
    78.                             &name,  
    79.                             0,  
    80.                             0,  
    81.                             FALSE,      // not exclusive!   
    82.                             &g_devnfo);  
    83.     if (status != STATUS_SUCCESS) {  
    84.         KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(nfo): 0x%x! ", status));  
    85.         goto done;  
    86.     }  
    87.   
    88.     RtlInitUnicodeString(&linkname, L"\??\tdifw_nfo");  
    89.   
    90.     //创建了一个符号连接   
    91.     status = IoCreateSymbolicLink(&linkname, &name);  
    92.     if (status != STATUS_SUCCESS) {  
    93.         KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x! ", status));  
    94.         goto done;  
    95.     }  
    96.   
    97. #ifndef USE_TDI_HOOKING   
    98.   
    99.     //绑定设备   
    100.     status = c_n_a_device(theDriverObject, &g_tcpfltobj, &g_tcpoldobj, L"\Device\Tcp");  
    101.     if (status != STATUS_SUCCESS) {  
    102.         KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x ", status));  
    103.         goto done;  
    104.     }  
    105.   
    106.         //绑定设备   
    107.     status = c_n_a_device(theDriverObject, &g_udpfltobj, &g_udpoldobj, L"\Device\Udp");  
    108.     if (status != STATUS_SUCCESS) {  
    109.         KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x ", status));  
    110.         goto done;  
    111.     }  
    112.   
    113.         //绑定设备   
    114.     status = c_n_a_device(theDriverObject, &g_ipfltobj, &g_ipoldobj, L"\Device\RawIp");  
    115.     if (status != STATUS_SUCCESS) {  
    116.         KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x ", status));  
    117.         goto done;  
    118.     }  
    119.   
    120. #else   /* USE_TDI_HOOKING */   
    121.   
    122.     /* get device objects for tcp/udp/ip */  
    123.     //获得tcp设备对象   
    124.     status = get_device_object(L"\Device\Tcp", &g_tcpfltobj);  
    125.     if (status != STATUS_SUCCESS) {  
    126.         KdPrint(("[tdi_fw] DriverEntry: get_device_object(tcp): 0x%x ", status));  
    127.         goto done;  
    128.     }  
    129.     //获得Udp设备对象   
    130.     status = get_device_object(L"\Device\Udp", &g_udpfltobj);  
    131.     if (status != STATUS_SUCCESS) {  
    132.         KdPrint(("[tdi_fw] DriverEntry: get_device_object(udp): 0x%x ", status));  
    133.         goto done;  
    134.     }  
    135.     //获得RawIp设备对象   
    136.     status = get_device_object(L"\Device\RawIp", &g_ipfltobj);  
    137.     if (status != STATUS_SUCCESS) {  
    138.         KdPrint(("[tdi_fw] DriverEntry: get_device_object(ip): 0x%x ", status));  
    139.         goto done;  
    140.     }  
    141.   
    142.     /* hook tcpip */  
    143.     //针对tcp下钩子   
    144.     status = hook_tcpip(&g_old_DriverObject, TRUE);  
    145.     if (status != STATUS_SUCCESS) {  
    146.         KdPrint(("[tdi_fw] DriverEntry: hook_driver: 0x%x ", status));  
    147.         goto done;  
    148.     }  
    149.     g_hooked = TRUE;  
    150.   
    151. #endif  /* USE_TDI_HOOKING */   
    152.   
    153.     status = STATUS_SUCCESS;  
    154.   
    155. done:  
    156.     if (status != STATUS_SUCCESS) {  
    157.         // cleanup   
    158.         OnUnload(theDriverObject);  
    159.     }  
    160.   
    161.     return status;  
    162. }  
    /* 驱动入口 */
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
                IN PUNICODE_STRING theRegistryPath)
    {
        NTSTATUS status = STATUS_SUCCESS;
    	int i;
    	UNICODE_STRING name, linkname;
    
    	//内存跟踪初始化	调用了KeInitializeSpinLock(&guard);
    	memtrack_init();
    
    	//初始化锁
    	KeInitializeSpinLock(&g_traffic_guard);
    
    #ifdef USE_TDI_HOOKING
    	KdPrint(("[tdi_fw] WARNING! Using unstable working mode: TDI hooking!
    "));
    #endif
    
    	status = ot_init();
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: ot_init: 0x%x
    ", status));
    		goto done;
    	}
    
    	//过滤器初始化
    	status = filter_init();
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: filter_init: 0x%x
    ", status));
    		goto done;
    	}
    
    	//连接状态初始化
    	status = conn_state_init();
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: conn_state_init: 0x%x
    ", status));
    		goto done;
    	}
    	
    	//分发函数
    	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
    		theDriverObject->MajorFunction[i] = DeviceDispatch;
    
    #if DBG
    	// register UnLoad procedure
    	theDriverObject->DriverUnload = OnUnload;
    #endif
    
    	/* create control device and symbolic link */
    
    	RtlInitUnicodeString(&name, L"\Device\tdifw");
    
    	status = IoCreateDevice(theDriverObject,
    							0,
    							&name,
    							0,
    							0,
    							TRUE,		// exclusive!
    							&g_devcontrol);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(control): 0x%x!
    ", status));
    		goto done;
    	}
    
    	RtlInitUnicodeString(&linkname, L"\??\tdifw");
    
    	//创建了一个符号连接
    	status = IoCreateSymbolicLink(&linkname, &name);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!
    ", status));
    		goto done;
    	}
    
    	RtlInitUnicodeString(&name, L"\Device\tdifw_nfo");
    
    	//创建设备对象
    	status = IoCreateDevice(theDriverObject,
    							0,
    							&name,
    							0,
    							0,
    							FALSE,		// not exclusive!
    							&g_devnfo);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(nfo): 0x%x!
    ", status));
    		goto done;
    	}
    
    	RtlInitUnicodeString(&linkname, L"\??\tdifw_nfo");
    
    	//创建了一个符号连接
    	status = IoCreateSymbolicLink(&linkname, &name);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!
    ", status));
    		goto done;
    	}
    
    #ifndef USE_TDI_HOOKING
    
    	//绑定设备
    	status = c_n_a_device(theDriverObject, &g_tcpfltobj, &g_tcpoldobj, L"\Device\Tcp");
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x
    ", status));
    		goto done;
    	}
    
    		//绑定设备
    	status = c_n_a_device(theDriverObject, &g_udpfltobj, &g_udpoldobj, L"\Device\Udp");
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x
    ", status));
    		goto done;
    	}
    
    		//绑定设备
    	status = c_n_a_device(theDriverObject, &g_ipfltobj, &g_ipoldobj, L"\Device\RawIp");
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x
    ", status));
    		goto done;
    	}
    
    #else	/* USE_TDI_HOOKING */
    
    	/* get device objects for tcp/udp/ip */
    	//获得tcp设备对象
    	status = get_device_object(L"\Device\Tcp", &g_tcpfltobj);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: get_device_object(tcp): 0x%x
    ", status));
    		goto done;
    	}
    	//获得Udp设备对象
    	status = get_device_object(L"\Device\Udp", &g_udpfltobj);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: get_device_object(udp): 0x%x
    ", status));
    		goto done;
    	}
    	//获得RawIp设备对象
    	status = get_device_object(L"\Device\RawIp", &g_ipfltobj);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: get_device_object(ip): 0x%x
    ", status));
    		goto done;
    	}
    
    	/* hook tcpip */
    	//针对tcp下钩子
    	status = hook_tcpip(&g_old_DriverObject, TRUE);
    	if (status != STATUS_SUCCESS) {
    		KdPrint(("[tdi_fw] DriverEntry: hook_driver: 0x%x
    ", status));
    		goto done;
    	}
    	g_hooked = TRUE;
    
    #endif	/* USE_TDI_HOOKING */
    
    	status = STATUS_SUCCESS;
    
    done:
    	if (status != STATUS_SUCCESS) {
    		// cleanup
    		OnUnload(theDriverObject);
    	}
    
        return status;
    }


    针对tcp或udp、ip等设备对象时的操作,操作分两种:设备过滤模式 和 TDI Hook模式。源码如下:

    1.         //TDI Hook过滤模式   
    2.         case IRP_MJ_CREATE:     /* create fileobject */  
    3.               
    4.             result = tdi_create(irp, irps, &completion);  
    5.   
    6.             status = tdi_dispatch_complete(DeviceObject, irp, result,  
    7.                 completion.routine, completion.context);  
    8.               
    9.             break;  
    10.   
    11.         case IRP_MJ_DEVICE_CONTROL:  
    12.               
    13.             KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_DEVICE_CONTROL, control 0x%x for 0x%08X ",  
    14.                 irps->Parameters.DeviceIoControl.IoControlCode, irps->FileObject));  
    15.   
    16.             if (KeGetCurrentIrql() == PASSIVE_LEVEL) {  
    17.                 /* 
    18.                  * try to convert it to IRP_MJ_INTERNAL_DEVICE_CONTROL 
    19.                  * (works on PASSIVE_LEVEL only!) 
    20.                  */  
    21.                 status = TdiMapUserRequest(DeviceObject, irp, irps);  
    22.               
    23.             } else  
    24.                 status = STATUS_NOT_IMPLEMENTED; // set fake status   
    25.   
    26.             if (status != STATUS_SUCCESS) {  
    27.                 void *buf = (irps->Parameters.DeviceIoControl.IoControlCode == IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) ?  
    28.                     irps->Parameters.DeviceIoControl.Type3InputBuffer : NULL;  
    29.   
    30.                 // send IRP to original driver   
    31.                 status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL);  
    32.   
    33.                 if (buf != NULL && status == STATUS_SUCCESS) {  
    34.   
    35.                     g_TCPSendData = *(TCPSendData_t **)buf;  
    36.   
    37.                     KdPrint(("[tdi_fw] DeviceDispatch: IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER: TCPSendData = 0x%x ",  
    38.                         g_TCPSendData));  
    39.   
    40.                     *(TCPSendData_t **)buf = new_TCPSendData;  
    41.                 }  
    42.   
    43.                 break;  
    44.             }  
    45.   
    46.             // don't break! go to internal device control!   
    47.           
    48.         case IRP_MJ_INTERNAL_DEVICE_CONTROL: {  
    49.             /* 
    50.              * Analyze ioctl for TDI driver 
    51.              */  
    52.             int i;  
    53.   
    54.             for (i = 0; g_tdi_ioctls[i].MinorFunction != 0; i++)  
    55.                 if (g_tdi_ioctls[i].MinorFunction == irps->MinorFunction) {  
    56.                       
    57. #if DBG   
    58.                     // print description   
    59.                     KdPrint(("[tdi_fw] DeviceDispatch: %s (0x%x) for 0x%x ",  
    60.                         g_tdi_ioctls[i].desc,  
    61.                         irps->MinorFunction,  
    62.                         irps->FileObject));  
    63. #endif   
    64.   
    65.                     if (g_tdi_ioctls[i].fn == NULL) {  
    66.                         // send IRP to original driver   
    67.                         status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW,  
    68.                             NULL, NULL);  
    69.                         break;  
    70.                     }  
    71.   
    72.                     // call dispatch function   
    73.   
    74.                     result = g_tdi_ioctls[i].fn(irp, irps, &completion);  
    75.   
    76.                     // complete request   
    77.                     status = tdi_dispatch_complete(DeviceObject, irp, result,  
    78.                         completion.routine, completion.context);  
    79.   
    80.                     break;  
    81.                 }  
    82.       
    83.             // if dispatch function hasn't been found   
    84.             if (g_tdi_ioctls[i].MinorFunction == 0) {  
    85.                 // send IRP to original driver   
    86.                 status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL);  
    87.             }  
    88.   
    89.             break;  
    90.         }  
    91.   
    92.         case IRP_MJ_CLEANUP:        /* cleanup fileobject */  
    93.   
    94.             result = tdi_cleanup(irp, irps, &completion);  
    95.   
    96.             status = tdi_dispatch_complete(DeviceObject, irp, result,  
    97.                 completion.routine, completion.context);  
    98.             break;  
    99.   
    100.         case IRP_MJ_CLOSE:  
    101.             KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_CLOSE fileobj 0x%x ", irps->FileObject));  
    102.   
    103.             // passthrough IRP   
    104.             status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW,  
    105.                 completion.routine, completion.context);  
    106.   
    107.             break;  
    		//TDI Hook过滤模式
    		case IRP_MJ_CREATE:		/* create fileobject */
    			
    			result = tdi_create(irp, irps, &completion);
    
    			status = tdi_dispatch_complete(DeviceObject, irp, result,
    				completion.routine, completion.context);
    			
    			break;
    
    		case IRP_MJ_DEVICE_CONTROL:
    			
    			KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_DEVICE_CONTROL, control 0x%x for 0x%08X
    ",
    				irps->Parameters.DeviceIoControl.IoControlCode, irps->FileObject));
    
    			if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
    				/*
    				 * try to convert it to IRP_MJ_INTERNAL_DEVICE_CONTROL
    				 * (works on PASSIVE_LEVEL only!)
    				 */
    				status = TdiMapUserRequest(DeviceObject, irp, irps);
    			
    			} else
    				status = STATUS_NOT_IMPLEMENTED; // set fake status
    
    			if (status != STATUS_SUCCESS) {
    				void *buf = (irps->Parameters.DeviceIoControl.IoControlCode == IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) ?
    					irps->Parameters.DeviceIoControl.Type3InputBuffer : NULL;
    
    				// send IRP to original driver
    				status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL);
    
    				if (buf != NULL && status == STATUS_SUCCESS) {
    
    					g_TCPSendData = *(TCPSendData_t **)buf;
    
    					KdPrint(("[tdi_fw] DeviceDispatch: IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER: TCPSendData = 0x%x
    ",
    						g_TCPSendData));
    
    					*(TCPSendData_t **)buf = new_TCPSendData;
    				}
    
    				break;
    			}
    
    			// don't break! go to internal device control!
    		
    		case IRP_MJ_INTERNAL_DEVICE_CONTROL: {
    			/*
    			 * Analyze ioctl for TDI driver
    			 */
    			int i;
    
    			for (i = 0; g_tdi_ioctls[i].MinorFunction != 0; i++)
    				if (g_tdi_ioctls[i].MinorFunction == irps->MinorFunction) {
    					
    #if DBG
    					// print description
    					KdPrint(("[tdi_fw] DeviceDispatch: %s (0x%x) for 0x%x
    ",
    						g_tdi_ioctls[i].desc,
    						irps->MinorFunction,
    						irps->FileObject));
    #endif
    
    					if (g_tdi_ioctls[i].fn == NULL) {
    						// send IRP to original driver
    						status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW,
    							NULL, NULL);
    						break;
    					}
    
    					// call dispatch function
    
    					result = g_tdi_ioctls[i].fn(irp, irps, &completion);
    
    					// complete request
    					status = tdi_dispatch_complete(DeviceObject, irp, result,
    						completion.routine, completion.context);
    
    					break;
    				}
    	
    			// if dispatch function hasn't been found
    			if (g_tdi_ioctls[i].MinorFunction == 0) {
    				// send IRP to original driver
    				status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL);
    			}
    
    			break;
    		}
    
    		case IRP_MJ_CLEANUP:		/* cleanup fileobject */
    
    			result = tdi_cleanup(irp, irps, &completion);
    
    			status = tdi_dispatch_complete(DeviceObject, irp, result,
    				completion.routine, completion.context);
    			break;
    
    		case IRP_MJ_CLOSE:
    			KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_CLOSE fileobj 0x%x
    ", irps->FileObject));
    
    			// passthrough IRP
    			status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW,
    				completion.routine, completion.context);
    
    			break;
    1. //设备过滤操作模式   
    2. if (irps->MajorFunction == IRP_MJ_CREATE) {  
    3.   
    4.     // initialize for user-mode part (exclusive access - 1 user-mode logging part)   
    5.     filter_init_2();  
    6.   
    7.     g_got_log = TRUE;  
    8.   
    9. else if (irps->MajorFunction == IRP_MJ_CLOSE) {  
    10.   
    11.     // cleanup for user-mode logging part   
    12.     filter_free_2();  
    13.   
    14.     g_got_log = FALSE;  
    15.   
    16. if (irps->MajorFunction == IRP_MJ_DEVICE_CONTROL) {  
    17.   
    18.     /* 
    19.      * control request 
    20.      */  
    21.   
    22.     ULONG ioctl = irps->Parameters.DeviceIoControl.IoControlCode,  
    23.         len = irps->Parameters.DeviceIoControl.InputBufferLength,  
    24.         size = irps->Parameters.DeviceIoControl.OutputBufferLength;  
    25.     char *out_buf;  
    26.   
    27.     if (IOCTL_TRANSFER_TYPE(ioctl) == METHOD_NEITHER) {  
    28.         // this type of transfer unsupported   
    29.         out_buf = NULL;  
    30.     } else  
    31.         out_buf = (char *)irp->AssociatedIrp.SystemBuffer;  
    32.   
    33.     // process control request   
    34.     status = process_request(ioctl, out_buf, &len, size);  
    35.   
    36.     irp->IoStatus.Information = len;  
    37.   
    38. }  
    39.   
    40. irp->IoStatus.Status = status;  
    41.   
    42. IoCompleteRequest(irp, IO_NO_INCREMENT);  
    		//设备过滤操作模式
    		if (irps->MajorFunction == IRP_MJ_CREATE) {
    
    			// initialize for user-mode part (exclusive access - 1 user-mode logging part)
    			filter_init_2();
    
    			g_got_log = TRUE;
    
    		} else if (irps->MajorFunction == IRP_MJ_CLOSE) {
    
    			// cleanup for user-mode logging part
    			filter_free_2();
    
    			g_got_log = FALSE;
    
    		} if (irps->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
    
    			/*
    			 * control request
    			 */
    
    			ULONG ioctl = irps->Parameters.DeviceIoControl.IoControlCode,
    				len = irps->Parameters.DeviceIoControl.InputBufferLength,
    				size = irps->Parameters.DeviceIoControl.OutputBufferLength;
    			char *out_buf;
    
    			if (IOCTL_TRANSFER_TYPE(ioctl) == METHOD_NEITHER) {
    				// this type of transfer unsupported
    				out_buf = NULL;
    			} else
    				out_buf = (char *)irp->AssociatedIrp.SystemBuffer;
    
    			// process control request
    			status = process_request(ioctl, out_buf, &len, size);
    
    			irp->IoStatus.Information = len;
    
    		}
    
    		irp->IoStatus.Status = status;
    
    		IoCompleteRequest(irp, IO_NO_INCREMENT);


    学习的目的是成熟!~

    源码包下载

  • 相关阅读:
    使用VS进入源码调试
    Nlog配置
    一个极简的爬虫
    简单的调用图灵机器人
    docker部署netcore项目 nginx负载均衡
    windows nginx负载均衡
    windows服务器环境配置redis sentinel部署
    ASP.NET资源大全-知识分享
    ABP动态生成WebAPI
    windows服务器环境下安装redis
  • 原文地址:https://www.cnblogs.com/youlechang123/p/6821999.html
Copyright © 2011-2022 走看看