驱动开发:通过应用堆实现多次通信

在前面的文章《驱动开发:运用MDL映射实现多次通信》LyShark教大家使用MDL的方式灵活的实现了内核态多次输出结构体的效果,但是此种方法并不推荐大家使用原因很简单首先内核空间比较宝贵,其次内核里面不能分配太大且每次传出的结构体最大不能超过1024个,而最终这些内存由于无法得到更好的释放从而导致坏堆的产生,这样的程序显然是无法在生产环境中使用的,如下LyShark将教大家通过在应用层申请空间来实现同等效果,此类传递方式也是多数ARK反内核工具中最常采用的一种。

与MDL映射相反,MDL多数处理流程在内核代码中,而应用层开堆复杂代码则在应用层,但内核层中同样还是需要使用指针,只是这里的指针仅仅只是保留基本要素即可,通过EnumProcess()模拟枚举进程操作,传入的是PPROCESS_INFO进程指针转换,将数据传入到PPROCESS_INFO直接返回进程计数器即可。

// -------------------------------------------------
// R3传输结构体
// -------------------------------------------------

// 进程指针转换
typedef struct
{
  DWORD PID;
  DWORD PPID;
}PROCESS_INFO, *PPROCESS_INFO;

// 数据存储指针
typedef struct
{
  ULONG_PTR nSize;
  PVOID BufferPtr;
}BufferPointer, *pBufferPointer;

// 模拟进程枚举
ULONG EnumProcess(PPROCESS_INFO pBuffer)
{
  ULONG nCount = 0;

  for (size_t i = 0; i < 10; i++)
  {
    pBuffer[i].PID = nCount * 2;
    pBuffer[i].PPID = nCount * 4;

    nCount = nCount + 1;
  }
  return nCount;
}

内核层核心代码: 内核代码中是如何通信的,首先从用户态接收pIoBuffer到分配的缓冲区数据,并转换为pBufferPointer结构,ProbeForWrite用于检查地址是否可写入,接着会调用EnumProcess()注意传入的其实是应用层的指针,枚举进程结束后,将进程数量nCount通过*(PULONG)pIrp->AssociatedIrp.SystemBuffer = (ULONG)nCount回传给应用层,至此内核中仅仅回传了一个长度,其他的都写入到了应用层中。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

pBufferPointer pinp = (pBufferPointer)pIoBuffer;

__try
{
  DbgPrint("缓冲区长度: %d \n", pinp->nSize);
  DbgPrint("缓冲区基地址: %p \n", pinp->BufferPtr);

  // 检查地址是否可写入
  ProbeForWrite(pinp->BufferPtr, pinp->nSize, 1);

  ULONG nCount = EnumProcess((PPROCESS_INFO)pinp->BufferPtr);
  DbgPrint("进程计数 = %d \n", nCount);
  if (nCount > 0)
  {
    // 将进程数返回给用户
    *(PULONG)pIrp->AssociatedIrp.SystemBuffer = (ULONG)nCount;
    status = STATUS_SUCCESS;
  }
}
__except (1)
{
  status = GetExceptionCode();
  DbgPrint("IOCTL_GET_EPROCESS %x \n", status);
}

// 返回通信状态
status = STATUS_SUCCESS;
break;

应用层核心代码: 通信的重点在于应用层,首先定义BufferPointer用于存放缓冲区头部指针,定义PPROCESS_INFO则是用于后期将数据放入该容器内,函数HeapAlloc分配一段堆空间,并HEAP_ZERO_MEMORY将该堆空间全部填空,将这一段初始化后的空间放入到pInput.BufferPtr缓冲区内,并计算出长度放入到pInput.nSize缓冲区内,一切准备就绪之后,再通过DriveControl.IoControlBufferPointer结构传输至内核中,而bRet则是用于接收返回长度的变量。

当收到数据后,通过(PPROCESS_INFO)pInput.BufferPtr强制转换为指针类型,并依次pProcessInfo[i]读出每一个节点的元素,最后是调用HeapFree释放掉这段堆空间。至于输出就很简单了vectorProcess[x].PID循环容器元素即可。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

// 应用层数据结构体数据
BOOL bRet = FALSE;
BufferPointer pInput = { 0 };
PPROCESS_INFO pProcessInfo = NULL;

// 分配堆空间
pInput.BufferPtr = (PVOID)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PROCESS_INFO) * 1000);
pInput.nSize = sizeof(PROCESS_INFO) * 1000;

ULONG nRet = 0;

if (pInput.BufferPtr)
{
  bRet = DriveControl.IoControl(IOCTL_IO_R3StructAll, &pInput, sizeof(BufferPointer), &nRet, sizeof(ULONG), 0);
}

std::cout << "返回结构体数量: " << nRet << std::endl;

if (bRet && nRet > 0)
{
  pProcessInfo = (PPROCESS_INFO)pInput.BufferPtr;
  std::vector<PROCESS_INFO> vectorProcess;

  for (ULONG i = 0; i < nRet; i++)
  {
    vectorProcess.push_back(pProcessInfo[i]);
  }

  // 释放空间
  bRet = HeapFree(GetProcessHeap(), 0, pInput.BufferPtr);
  std::cout << "释放状态: " << bRet << std::endl;

  // 输出容器内的进程ID列表
  for (int x = 0; x < nRet; x++)
  {
    std::cout << "PID: " << vectorProcess[x].PID << " PPID: " << vectorProcess[x].PPID << std::endl;
  }
}

// 关闭符号链接句柄
CloseHandle(DriveControl.m_hDriver);

如上就是内核层与应用层的部分代码功能分析,接下来我将完整代码分享出来,大家可以自行测试效果。

驱动程序WinDDK.sys完整代码;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#define _CRT_SECURE_NO_WARNINGS
#include <ntifs.h>
#include <windef.h>

// 定义符号链接,一般来说修改为驱动的名字即可
#define DEVICE_NAME        L"\\Device\\WinDDK"
#define LINK_NAME          L"\\DosDevices\\WinDDK"
#define LINK_GLOBAL_NAME   L"\\DosDevices\\Global\\WinDDK"

// 定义驱动功能号和名字,提供接口给应用程序调用
#define IOCTL_IO_R3StructAll    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)

// 保存一段非分页内存,用于给全局变量使用
#define FILE_DEVICE_EXTENSION 4096

// -------------------------------------------------
// R3传输结构体
// -------------------------------------------------

// 进程指针转换
typedef struct
{
	DWORD PID;
	DWORD PPID;
}PROCESS_INFO, *PPROCESS_INFO;

// 数据存储指针
typedef struct
{
	ULONG_PTR nSize;
	PVOID BufferPtr;
}BufferPointer, *pBufferPointer;

// 模拟进程枚举
ULONG EnumProcess(PPROCESS_INFO pBuffer)
{
	ULONG nCount = 0;

	for (size_t i = 0; i < 10; i++)
	{
		pBuffer[i].PID = nCount * 2;
		pBuffer[i].PPID = nCount * 4;

		nCount = nCount + 1;
	}
	return nCount;
}

// 驱动绑定默认派遣函数
NTSTATUS DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp)
{
	_pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
	_pIrp->IoStatus.Information = 0;
	IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
	return _pIrp->IoStatus.Status;
}

// 驱动卸载的处理例程
VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
	if (pDriverObj->DeviceObject)
	{
		UNICODE_STRING strLink;

		// 删除符号连接和设备
		RtlInitUnicodeString(&strLink, LINK_NAME);
		IoDeleteSymbolicLink(&strLink);
		IoDeleteDevice(pDriverObj->DeviceObject);
		DbgPrint("[kernel] # 驱动已卸载 \n");
	}
}

// IRP_MJ_CREATE 对应的处理例程,一般不用管它
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	DbgPrint("[kernel] # 驱动处理例程载入 \n");
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// IRP_MJ_CLOSE 对应的处理例程,一般不用管它
NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	DbgPrint("[kernel] # 关闭派遣 \n");
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// IRP_MJ_DEVICE_CONTROL 对应的处理例程,驱动最重要的函数
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
	NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
	PIO_STACK_LOCATION pIrpStack;
	ULONG uIoControlCode;
	PVOID pIoBuffer;
	ULONG uInSize;
	ULONG uOutSize;

	// 获得IRP里的关键数据
	pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

	// 获取控制码
	uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;

	// 输入和输出的缓冲区(DeviceIoControl的InBuffer和OutBuffer都是它)
	pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;

	// EXE发送传入数据的BUFFER长度(DeviceIoControl的nInBufferSize)
	uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;

	// EXE接收传出数据的BUFFER长度(DeviceIoControl的nOutBufferSize)
	uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

	// 对不同控制信号的处理流程
	switch (uIoControlCode)
	{
	// 测试R3传输多次结构体
	case IOCTL_IO_R3StructAll:
	{
		pBufferPointer pinp = (pBufferPointer)pIoBuffer;

		__try
		{
			DbgPrint("[lyshark] 缓冲区长度: %d \n", pinp->nSize);
			DbgPrint("[lyshark] 缓冲区基地址: %p \n", pinp->BufferPtr);

			// 检查地址是否可写入
			ProbeForWrite(pinp->BufferPtr, pinp->nSize, 1);

			ULONG nCount = EnumProcess((PPROCESS_INFO)pinp->BufferPtr);
			DbgPrint("[lyshark.com] 进程计数 = %d \n", nCount);
			if (nCount > 0)
			{
				// 将进程数返回给用户
				*(PULONG)pIrp->AssociatedIrp.SystemBuffer = (ULONG)nCount;
				status = STATUS_SUCCESS;
			}
		}
		__except (1)
		{
			status = GetExceptionCode();
			DbgPrint("IOCTL_GET_EPROCESS %x \n", status);
		}

		// 返回通信状态
		status = STATUS_SUCCESS;
		break;
	}
	}

	// 设定DeviceIoControl的*lpBytesReturned的值(如果通信失败则返回0长度)
	if (status == STATUS_SUCCESS)
	{
		pIrp->IoStatus.Information = uOutSize;
	}
	else
	{
		pIrp->IoStatus.Information = 0;
	}

	// 设定DeviceIoControl的返回值是成功还是失败
	pIrp->IoStatus.Status = status;
	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	return status;
}

// 驱动的初始化工作
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
	NTSTATUS status = STATUS_SUCCESS;
	UNICODE_STRING ustrLinkName;
	UNICODE_STRING ustrDevName;
	PDEVICE_OBJECT pDevObj;

	// 初始化其他派遣
	for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		// DbgPrint("初始化派遣: %d \n", i);
		pDriverObj->MajorFunction[i] = DefaultDispatch;
	}

	// 设置分发函数和卸载例程
	pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
	pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
	pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
	pDriverObj->DriverUnload = DriverUnload;

	// 创建一个设备
	RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);

	// FILE_DEVICE_EXTENSION 创建设备时,指定设备扩展内存的大小,传一个值进去,就会给设备分配一块非页面内存。
	status = IoCreateDevice(pDriverObj, sizeof(FILE_DEVICE_EXTENSION), &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	// 判断支持的WDM版本,其实这个已经不需要了,纯属WIN9X和WINNT并存时代的残留物
	if (IoIsWdmVersionAvailable(1, 0x10))
	{
		RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
	}
	else
	{
		RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
	}

	// 创建符号连接
	status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("创建符号链接失败 \n");
		IoDeleteDevice(pDevObj);
		return status;
	}
	DbgPrint("[hello LyShark.com] # 驱动初始化完毕 \n");

	// 返回加载驱动的状态(如果返回失败,驱动讲被清除出内核空间)
	return STATUS_SUCCESS;
}

应用层客户端程序lyshark.exe完整代码;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include <iostream>
#include <Windows.h>
#include <vector>

#pragma comment(lib,"user32.lib")
#pragma comment(lib,"advapi32.lib")

// 定义驱动功能号和名字,提供接口给应用程序调用
#define IOCTL_IO_R3StructAll    0x806

class cDrvCtrl
{
public:
	cDrvCtrl()
	{
		m_pSysPath = NULL;
		m_pServiceName = NULL;
		m_pDisplayName = NULL;
		m_hSCManager = NULL;
		m_hService = NULL;
		m_hDriver = INVALID_HANDLE_VALUE;
	}
	~cDrvCtrl()
	{
		CloseServiceHandle(m_hService);
		CloseServiceHandle(m_hSCManager);
		CloseHandle(m_hDriver);
	}

	// 安装驱动
	BOOL Install(PCHAR pSysPath, PCHAR pServiceName, PCHAR pDisplayName)
	{
		m_pSysPath = pSysPath;
		m_pServiceName = pServiceName;
		m_pDisplayName = pDisplayName;
		m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
		if (NULL == m_hSCManager)
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		m_hService = CreateServiceA(m_hSCManager, m_pServiceName, m_pDisplayName,
			SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
			m_pSysPath, NULL, NULL, NULL, NULL, NULL);
		if (NULL == m_hService)
		{
			m_dwLastError = GetLastError();
			if (ERROR_SERVICE_EXISTS == m_dwLastError)
			{
				m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
				if (NULL == m_hService)
				{
					CloseServiceHandle(m_hSCManager);
					return FALSE;
				}
			}
			else
			{
				CloseServiceHandle(m_hSCManager);
				return FALSE;
			}
		}
		return TRUE;
	}

	// 启动驱动
	BOOL Start()
	{
		if (!StartServiceA(m_hService, NULL, NULL))
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		return TRUE;
	}

	// 关闭驱动
	BOOL Stop()
	{
		SERVICE_STATUS ss;
		GetSvcHandle(m_pServiceName);
		if (!ControlService(m_hService, SERVICE_CONTROL_STOP, &ss))
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		return TRUE;
	}

	// 移除驱动
	BOOL Remove()
	{
		GetSvcHandle(m_pServiceName);
		if (!DeleteService(m_hService))
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		return TRUE;
	}

	// 打开驱动
	BOOL Open(PCHAR pLinkName)
	{
		if (m_hDriver != INVALID_HANDLE_VALUE)
			return TRUE;
		m_hDriver = CreateFileA(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
		if (m_hDriver != INVALID_HANDLE_VALUE)
			return TRUE;
		else
			return FALSE;
	}

	// 发送控制信号
	BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes)
	{
		DWORD dw;
		BOOL b = DeviceIoControl(m_hDriver, CTL_CODE_GEN(dwIoCode), InBuff, InBuffLen, OutBuff, OutBuffLen, &dw, NULL);
		if (RealRetBytes)
			*RealRetBytes = dw;
		return b;
	}
private:

	// 获取服务句柄
	BOOL GetSvcHandle(PCHAR pServiceName)
	{
		m_pServiceName = pServiceName;
		m_hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
		if (NULL == m_hSCManager)
		{
			m_dwLastError = GetLastError();
			return FALSE;
		}
		m_hService = OpenServiceA(m_hSCManager, m_pServiceName, SERVICE_ALL_ACCESS);
		if (NULL == m_hService)
		{
			CloseServiceHandle(m_hSCManager);
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}

	// 获取控制信号对应字符串
	DWORD CTL_CODE_GEN(DWORD lngFunction)
	{
		return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;
	}

public:
	DWORD m_dwLastError;
	PCHAR m_pSysPath;
	PCHAR m_pServiceName;
	PCHAR m_pDisplayName;
	HANDLE m_hDriver;
	SC_HANDLE m_hSCManager;
	SC_HANDLE m_hService;
};

void GetAppPath(char *szCurFile)
{
	GetModuleFileNameA(0, szCurFile, MAX_PATH);
	for (SIZE_T i = strlen(szCurFile) - 1; i >= 0; i--)
	{
		if (szCurFile[i] == '\\')
		{
			szCurFile[i + 1] = '\0';
			break;
		}
	}
}

// -------------------------------------------------
// R3数据传递变量
// -------------------------------------------------
// 进程指针转换
typedef struct
{
	DWORD PID;
	DWORD PPID;
}PROCESS_INFO, *PPROCESS_INFO;

// 数据存储指针
typedef struct
{
	ULONG_PTR nSize;
	PVOID BufferPtr;
}BufferPointer, *pBufferPointer;

int main(int argc, char *argv[])
{
	cDrvCtrl DriveControl;

	// 设置驱动名称
	char szSysFile[MAX_PATH] = { 0 };
	char szSvcLnkName[] = "WinDDK";;
	GetAppPath(szSysFile);
	strcat(szSysFile, "WinDDK.sys");

	// 安装并启动驱动
	DriveControl.Install(szSysFile, szSvcLnkName, szSvcLnkName);
	DriveControl.Start();

	// 打开驱动的符号链接
	DriveControl.Open("\\\\.\\WinDDK");

	// 应用层数据结构体数据
	BOOL bRet = FALSE;
	BufferPointer pInput = { 0 };
	PPROCESS_INFO pProcessInfo = NULL;

	// 分配堆空间
	pInput.BufferPtr = (PVOID)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PROCESS_INFO) * 1000);
	pInput.nSize = sizeof(PROCESS_INFO) * 1000;

	ULONG nRet = 0;

	if (pInput.BufferPtr)
	{
		bRet = DriveControl.IoControl(IOCTL_IO_R3StructAll, &pInput, sizeof(BufferPointer), &nRet, sizeof(ULONG), 0);
	}

	std::cout << "[LyShark.com] 返回结构体数量: " << nRet << std::endl;

	if (bRet && nRet > 0)
	{
		pProcessInfo = (PPROCESS_INFO)pInput.BufferPtr;
		std::vector<PROCESS_INFO> vectorProcess;

		for (ULONG i = 0; i < nRet; i++)
		{
			vectorProcess.push_back(pProcessInfo[i]);
		}

		// 释放空间
		bRet = HeapFree(GetProcessHeap(), 0, pInput.BufferPtr);
		std::cout << "释放状态: " << bRet << std::endl;

		// 输出容器内的进程ID列表
		for (int x = 0; x < nRet; x++)
		{
			std::cout << "PID: " << vectorProcess[x].PID << " PPID: " << vectorProcess[x].PPID << std::endl;
		}
	}

	// 关闭符号链接句柄
	CloseHandle(DriveControl.m_hDriver);

	// 停止并卸载驱动
	DriveControl.Stop();
	DriveControl.Remove();

	system("pause");
	return 0;
}

手动编译这两个程序,将驱动签名后以管理员身份运行lyshark.exe客户端,此时屏幕中即可看到滚动输出效果,如此一来就实现了循环传递参数的目的。

文章作者:lyshark (王瑞)
文章出处:http://www.cnblogs.com/LyShark/p/17134596.html
版权声明:本博客文章,除去特殊声明 [转载标注/参考文献] 部分, [均为原创] 作品,禁止任何形式的转载!
本文转载于网络 如有侵权请联系删除

相关文章

  • JS封深入了解

    大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。1.javascript语言理解闭包js变量的范围分成两个:全局变量、局部变量。在全局变量的函数外声明变量,内部功能可以直接调用全局变量。声明变量里面的函数必须使用var命令,否则,它里面的函数声明一个全局变量。闭包的产生是为了从外部读取函数的局部变量,即在函数内部再定义一个函数f2,把f2作为返回值,在上层函数中返回就能够使上层函数读取其它函数的局部变量了。functionf1(){    n=999;    functionf2(){       alert(n);     }    returnf2;  }  varresult=f1();  result();//999闭包(closure)就是可以读取其它函数内部变量的函数。也可以说是定义在一个函数内部的函数。本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。闭包的用途:1.能够读取函数内部的变量2.让这些变量的值始终保持在内存中,由于f2依赖于f1的存在而存在。缺点:函数中的变量都被保存在内存中,使内存消耗非常大,所以不能滥用闭包,不然网页性能会减少,在IE

  • Python 条件语句中的elif

    条件语句中的elif什么是elifelif(或者如果)对于命题的非第一次的多种判断,每一种判断条件对应一组业务代码条件语句的说明对于首次if判断不满足后,其他条件的判断语句用法ifbool_result: do elifbool_result: elifdo#当前elif语句对应的语法块 elifbool_result: elifdo#缩进等级与do语法块一致 else: elsedo复制参数elifdo:当前elif语句对应的python代码返回值elif属于语法,没有返回值说明条件语句中满足一个条件后,将退出当前条件语句每个条件语句中仅有且必须有一个if语句可以有0个或多个elif语句可以有0个或1个else语句每个条件语句if必须是第一个条件语句练习有一个班级,班级有很多同学,每个同学有如下信息:名字年龄分数,现在来了一个插班生,将这个小明放到成绩单里,这里要做判断,如果班级里有小明,就说明重名了,那么要给新的小明的后面加个新字并存入用列表与字典两种类型,用两种方法做题代码#coding:utf-8 number=10 ifnumber>10: print(&#

  • python内置函数open(读写文件)

    使用方法file_name="/%s/hello.txt/" date="2022-05-12" a=dict() withopen(file_name%date,'r')asfin: forlineinfin: (c,d,e)=line.strip().split('\t') a[c]=d data={} withopen(file_name,'w')asfout: forlineindata: oid=str(line['oid']) freq_list=str(line['freq_list']) fout.write("%s\t%s\n"%(oid,freq_list))复制相关参数1r:以只读方式打开文件。文件的指针将会放在文件的开头。这是**默认模式**。 2rb:以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 3r+:打开一个文件用于读写。文件指针将会放在文件的开头。 4rb+:以二

  • Python的socket编程

    目前处在学习python的阶段,昨天看到了python的socket模块,分别实现TCP、UDP时间戳回显。1、tcp通信server和client代码# tcpServer.py #!/usr/bin/python # -*- coding: utf-8 -*- from socket import * from time import ctime HOST = '' PORT = 21156 BUFSIZE = 1024 ADDR = (HOST,PORT) tcpServerSock = socket(AF_INET,SOCK_STREAM) tcpServerSock.bind(ADDR) tcpServerSock.listen(5) while True: print 'waiting for connection...' tcpClientSock ,addr = tcpServerSock.accept() print '...connected from:',addr while True: dat

  • 一种新的滥用缓存密钥规范化的缓存投毒技术分享

    众所周知,如今的网站会包含大量的JavaScript文件/代码,而这些代码一般都取自于TypeScript、SCSS和Webpack等复杂的实现栈。为了减少标准网页的加载时间,开发人员会利用缓存来减少服务器上的负载并减少用户的延迟。虽然缓存通常是为了帮助提高服务的可靠性,使其更易于用户访问,但一些自定义缓存配置可能会引入拒绝服务漏洞,导致服务易受攻击。缓存投毒DoS基础知识当攻击者利用目标设备中的缓存来向每一个请求资源的其他用户发送更改响应时,便有可能触发缓存投毒漏洞,下面给出的是缓存投毒拒绝服务攻击的演示样例:背景内容大家可以看到,实现DoS攻击所需的只是一个未缓存的Header,它将强制源服务器发送格式错误的请求。因此,我决定通过应用以下方法,在一些私人应用程序中寻找潜在的DoS漏洞:通过识别特定的缓存Header(X-Cache和cf-cache-status等)来检测使用了缓存服务的所有子域名;使用ParamMiner来爆破潜在的未缓存的Header;没花多少时间,我就在assests.redacted.com中找到了一个缓存投毒DoS漏洞,而这个子域名负责托管其中一个私人应用

  • 小程序 | 2-小程序初体验

    1.小程序的数据绑定xx.js文件中放置页面的逻辑和变量内容。小程序数据绑定使用的是{{}}双大括号语法,标准叫法为Mustache语法。在xx.js的Page-data节点中定义变量 Page({/** *页面的初始数据 */ data:{ name:'张三' } } 复制然后在xx.wxml页面中通过{{}}引用:<text>{{name}}</text>复制 复制此时,小程序的预览界面就会显示张三,如下图:{{}}还支持如下写法:<text>我的名字:{{name}}</text> <!--表示引用在js中定义的变量age,并将其值+10--> <text>我的年龄:{{age+10}}</text>复制 复制2.列表展示wx:forxx.js文件内容:Page({ /** *页面的初始数据 */ data:{ students:[ {id:1,name:'张三',age:13}, {id:2,name:'李四',age:24}

  • 英伟达新增一代Ada Lovelace显卡,将采用5nm架构

    AdaLovelace拥有18432个流处理器。作者|来自镁客星球的刘爽日前,外媒报料英伟达将新增一代采用5nm制程工艺的AdaLovelace显卡。继英伟达上一代Ampere安培架构之后,英伟达的下一代产品原本是Hopper(霍珀),此次AdaLovelace属于中途插队。AdaLovelace的代号来源于著名英国诗人拜伦之女,也是大名鼎鼎的数学家、计算机程序创始人——阿达·洛芙莱斯。她建立了循环、子程序的概念,并于1843年公布了世界上第一套算法,被视为“第一位给计算机写程序的人”。英伟达用她的名字为新一代产品命名,或许是因为该显卡在性能提升上有着开创性的意义。据爆料,AdaLovelace架构的大核心编号为AD102,将有多达12个GPC(图形处理集群)、72个TPC(纹理处理集群)和144个SM(流式多处理器)。同时,每个SM都拥有128个流处理器(CUDA核心),整个GPU包含的流处理器多达18432个,相较于目前安培架构GA102核心的10752个,增长幅度超过70%。虽然这个幅度比不上安培GA102相对于图灵TU1021.3倍的提升,但依然十分可观,接近两万的流处理器总数

  • 阅读器多种翻页的设计与实现

    前言前文介绍的是小说阅读器的设计和实现,本文作为补充对多种翻页模式做详细剖析。正文常见的阅读器翻页模式包括:平移、仿真、滑页和上下: 平移:左右滑动; 仿真:左右滑动;(纸质书翻页效果) 滑页:左右滑动;(覆盖效果) 上下:上下滑动; 1、平移UIKit提供UIPageViewController可以很方便实现平移的页面切换效果,使用流程: 1、创建UIPageViewController;self.pageVC=[[UIPageViewControlleralloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options: @{ UIPageViewControllerOptionSpineLocationKey:@(UIPageViewControllerSpineLocationMin) }]; self.pageVC.dele

  • 使用vim打造go语言IDE

    在网上看到了一篇不错的关于vim的文章,参考网上给出的资料,花了些时间,将自己的vim打造成一块go语言的IDE。中间因为网路的问题,踩了很多的坑,就在此简单的分享下自己的爬坑经验吧,其实也没经验,主要还是网络。以前的安装经验来自mac,linux下没有尝试。 安装go语言环境 这一步就不做详细的展开,唯一需要注意的地方就是设置GOPATH环境变量以及将$GOPATH/bin目录加入到环境变量中。 exportGOPATH=$HOME/go exportPATH=$PATH:$GOPATH/bin:$GOROOT/bin复制 将其加入到bash或zsh配置文件中,为了环境变量生效一定要source一下配置 安装vim mac及linux都会自带vim,因为一直用mac自带的,在安装YMC插件时踩了很多的坑。最后,将vim重新安装了一遍才解决。所以在此先将vim升级一把,避免因为版本问题,带来不必要的麻烦。mac下升级 brewinstallvim复制 为了使新安装的版本起作用,需要重新启动下命令行工具,这个坑,爬了好久,才出来。 vim的基本配置 在写程序中,我们常把一些系统需要灵活更

  • 神经网络图灵机(Neural Turing Machines, NTM)论文完整翻译

    神经网络图灵机AlexGravesgravesa@google.com GregWaynegregwayne@google.com IvoDanihelkadanihelka@google.comGoogleDeepMind,London,UK摘要本文通过引入一个使用注意力程序进行交互的外部存储器(externalmemory)来增强神经网络的能力。新系统可以与图灵机或者冯·诺依曼体系相类比,但每个组成部分都是可微的,可以使用梯度下降进行高效训练。初步的结果显示神经网络图灵机能够从输入和输出样本中推理出(infer)简单的算法,如复制、排序和回忆。1.简介计算机程序在执行计算任务的过程中(VonNeumann,1945)使用了三个基本机制:初等运算(如算术操作),逻辑控制流(分支循环)和可读写的存储器。虽然在建模复杂数据方面取得了广泛的成功,现代机器学习理论却普遍忽略了对控制流和存储器的使用。由于其对带有时间属性的数据的进行学习和复杂转换的能力,递归神经网络脱颖而出。进一步,RNN又被证明是图灵完全等价的(SiegelmannandSontag,1995),因而只要合理建模,它就可以模

  • 为什么分布式一定要有redis?

    作者:孤独烟来自:http://rjzheng.cnblogs.com/ 分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。 回答:首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。 分析:这两个问题,说句实在话,一般中小型传统软件企业,很难碰到这个问题。如果有大并发的项目,流量有几百万左右。这两个问题一定要深刻考虑。 回答:如下所示 缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。 解决方案: (一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试 (二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失

  • 老码农的技术理想

    老码农的技术理想 小时候,老师问我,你的理想是什么?我不假思索说是工程师,于是长大之后果然成了工程师。   工作这么多年,一直在思考工程师这三个字的意义,终于有一天恍然大悟,原来就是:用技术手段改进世界。   那么,在软件方面,目前的世界有哪些问题需要解决呢?有这么一些问题可以思考: 现在整个世界的信息化程度是偏高还是偏低?程序员的人数够用吗?软件行业的生产力是偏高还是偏低?大部分软件系统都可靠吗?   我想说说自己对这几个问题的理解。   虽然现在我们的生活与十年前相比,已经发生了巨大变化,比如智能手持设备已经非常普及,可穿戴设备也在蓬勃发展。十年前我们用手机收发短信或者邮件,浏览非常简单而老土的wap页面,但现在,绝大部分人的手机已经取代了电脑,成为日常生活中不可缺少的工具。   我们用手机交流,购物,欣赏影视,阅读书籍,玩各类游戏,尤其是飞速发展的移动购物和支付体系,使得我们能在任意场合购买心仪的物品,订购旅游服务和宾馆,叫快餐,打车等等,生活非常美好,那么,整个世界的信息化程度处于什么级别呢?   我觉得,才刚刚相当于小学二年级,整个世界的信息化程度仍然严重偏低。从现在算起,往

  • 【Python】解决使用 plt.savefig 保存图片时一片空白

    更新这里我会列出对本文的更新。2017年9月28日:修正几处错字,优化排版。问题当使用如下代码保存使用plt.savefig保存生成的图片时,结果打开生成的图片却是一片空白。importmatplotlib.pyplotasplt """一些画图代码""" plt.show() plt.savefig("filename.png")复制原因其实产生这个现象的原因很简单:在plt.show()后调用了plt.savefig(),在plt.show()后实际上已经创建了一个新的空白的图片(坐标轴),这时候你再plt.savefig()就会保存这个新生成的空白图片。解决知道了原因,就不难知道解决办法了,解决办法有两种:在plt.show()之前调用plt.savefig(); importmatplotlib.pyplotasplt"""一些画图代码"""plt.savefig("filename.png")plt.show()画

  • 腾讯云DDoS高防IP源站IP暴露的解决方法

    由于部分攻击者会记录源站使用过的IP,因此在使用DDoS高防IP后,如果还存在绕过高防直接攻击源站IP的情况,建议更换源站IP。如不想更换源站IP或已经更换过IP但仍存在IP暴露情况,为防止出现攻击绕过高防直接攻击源站IP的情况,强烈参考下面方法以保护源站IP: 不使用与旧源站IP相同或相近网段的IP作为新的源站IP,避免攻击者对C段或相近网段进行猜测和扫描。 提前准备备份链路和备份IP。 设置访问来源范围,避免攻击者的恶意扫描。 参考 与源站结合的防护调度方案,结合实际情况进行应用。 说明: 更换源站IP之前,请务必确认已消除所有可能暴露源站IP的因素。 在更换源站IP前可参考下列检查方法,对暴露源站IP的可能因素进行逐一排查,避免新更换的源站IP继续暴露。 检查方法DNS解析记录检查检查该遭到攻击的旧源站IP上所有DNS解析记录,如子域名的解析记录、邮件服务器MX(MailExchanger)记录以及NS(NameServer)记录等,确保全部配置到高防IP,避免部分解析记录直

  • Delphi创建ActiveX控件,实现安全接口及无界面代码

    Delphi创建OCX控件非常的方便,但IE调用时弹出的安全认证非常麻烦,有时OCX也不需要界面,IE调用时需要隐藏,非常不方便。在DELPHI中创建OCX实现安全接口和创建事件中修改部分代码   实现安全接口 继承:IObjectSafety 重载方法: functionGetInterfaceSafetyOptions(constIID:TIID;pdwSupportedOptions,//安全接口 pdwEnabledOptions:PDWORD):HResult;stdcall; functionSetInterfaceSafetyOptions(constIID:TIID;dwOptionSetMask, dwEnabledOptions:DWORD):HResult;stdcall; functionTZhddMsg.GetInterfaceSafetyOptions(constIID:TIID; pdwSupportedOptions,pdwEnabledOptions:PDWORD):HResult; var Unk:IUnknown; beg

  • 【分步详解】两个有序数组中的中位数和Top K问题

    (这也是一道leetcode的经典题目:《LeetCode》解题笔记:004.MedianofTwoSortedArrays[H] 问题介绍 这是个超级超级经典的分治算法!!这个问题大致是说,如何在给定的两个有序数组里面找其中的中值,或者变形问题,如何在2个有序数组数组中查找TopK的值(TopK的问题可以转换成求第k个元素的问题)。这个算法在很多实际应用中都会用到,特别是在当前大数据的背景下。 我觉得下面的这个思路特别好,特别容易理解!!请按顺序看。是来自leetcode上的stellari英文答案,我整理并自己修改了一下。 预备知识 先解释下“割” 我们通过切一刀,能够把有序数组分成左右两个部分,切的那一刀就被称为割(Cut),割的左右会有两个元素,分别是左边最大值和右边最小值。 我们定义L=Max(LeftPart),R=Min(RightPart) Ps.割可以割在两个数中间,也可以割在1个数上,如果割在一个数上,那么这个数即属于左边,也属于右边。(后面讲单数组中值问题的时候会说) 比如说[2357]这个序列,割就在3和5之间 [23/57] 中值就是(3+5)/2=4 如

  • 2016/06/16 phpexcel

      程序部分   require_once './phpexcel/PHPExcel.php';   //首先创建一个新的对象  PHPExcelobject $objPHPExcel= new PHPExcel();   //设置文件的一些属性,在xls文件——>属性——>详细信息里可以看到这些值,xml表格里是没有这些值的 $objPHPExcel       ->getProperties()  //获得文件属性对象,给下文提供设置资源       ->setCreator( "MaartenBalliauw")                //设置文件的创建者

  • malloc 实现原理

    1.Reference: 如何实现一个malloc http://blog.codinglabs.org/articles/a-malloc-tutorial.html   2.  作者:loverszhaokai 出处:http://www.cnblogs.com/lovers 本文采用知识共享署名-非商业性使用-相同方式共享2.5中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 时间格式处理

    Java时间格式转换大全 importjava.text.*; importjava.util.Calendar; publicclassVeDate{ /** *获取现在时间 * *@return返回时间类型yyyy-MM-ddHH:mm:ss */ publicstaticDategetNowDate(){ DatecurrentTime=newDate(); SimpleDateFormatformatter=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss"); StringdateString=formatter.format(currentTime); ParsePositionpos=newParsePosition(8); DatecurrentTime_2=formatter.parse(dateString,pos); returncurrentTime_2; } /** *获取现在时间 * *@return返回短时间格式yyyy-MM-dd */ DateFormatformat1=newSimpleDateFormat("yyyy

  • Angularjs 动态添加指令并绑定事件

    先说使用场景,动态生成DOM元素并绑定事件,非常常见的一种场景,用jq实现效果: http://jsbin.com/gajizuyuju/edit?html,js,output varcount=0; $("#test").on("click",function(event){ if(event.target.tagName.toLowerCase()=="input")return; count++; varhtml="<inputtype='text'class='newEle'value='"+count+"'/>"; $(this).html(html); $(".newEle").focus(); }); $("body").on("blur",".newEle",function(){ alert($(this).val()); })复制 如果用angularjs应该怎么实现呢?想当然的情况是这样的: varmyApp=angular.module('myApp',[]); myApp.controller('MainCtrl',['$scope','$c

  • 朴素贝叶斯分类算法

    朴素贝叶斯分类算法 现有的分类算法很多,主要分为两种,一是单一分类器,包括决策树、贝叶斯、人工神经网络、支持向量机等;二是集成学习算法,如Bagging和Boosting等。贝叶斯(Bayes)分类算法是一类利用概率统计知识进行分类的算法,如朴素贝叶斯(NaiveBayes)算法。这些算法主要利用Bayes定理来预测一个未知类别的样本属于各个类别的可能性,选择其中可能性最大的一个类别作为该样本的最终类别。为了计算简单,朴素贝叶斯算法假设条件概率相互独立。 设样本的类别有\(K\)种,记\(Y={c_1,c_2,...,c_N}\),则基于后验概率\(P(c_i|x)\)可获得将样本\(x\)分类成为\(c_i\)所产生的期望损失(条件风险)为, \[R(c_i|x)=\sum_{j=1}^K\lambda_{ij}P(c_i|x) \]其中,\(\lambda_{ij}\)是将一个真实标记为\(c_j\)标记为\(c_i\)产生的损失。因为我们目标是最小化分类错误率,此时\(\lambda_{ij}\)为0/1损失函数,即分类正确时\(\lambda_{ij}=1\),否则为\(0\

相关推荐

推荐阅读