21.1 使用PEfile分析PE文件
PeFile模块是Python
中一个强大的便携式第三方PE
格式分析工具,用于解析和处理Windows
可执行文件。该模块提供了一系列的API接口,使得用户可以通过Python
脚本来读取和分析PE文件的结构,包括文件头、节表、导入表、导出表、资源表、重定位表等等。此外,PEfile模块还可以帮助用户进行一些恶意代码分析,比如提取样本中的字符串、获取函数列表、重构导入表、反混淆等等。PEfile模块是Python中处理PE文件的重要工具之一,广泛应用于二进制分析、安全研究和软件逆向工程等领域。
由于该模块为第三方模块,在使用之前读者需要在命令行下执行pip install pefile
命令安装第三方库,当安装成功后即可正常使用,如下所示则是该模块的基本使用方法,读者可自行学习理解。
21.1.1 打开并加载PE文件
如下这段代码封装并实现了OpenPeFile
函数,可用于打开一个PE文件,在其内部首先判断了可执行文件是否被压缩如果被压缩则会通过zipfile
模块将压缩包读入内存并调用C2BIP3
函数将数据集转换为2字节,接着再执行pefile.PE()
函数,该函数可用于将可执行文件载入,至此读者可在主函数内通过pe.dump_dict()
的方式输出该PE文件的所有参数,由于输出的是字典,读者可以使用字典与列表的方式灵活的提取出该程序的所有参数信息。
import sys
import zipfile
import pefile
# 如果是Python3则转换为2字节
def C2BIP3(string):
if sys.version_info[0] > 2:
return bytes([ord(x) for x in string])
else:
return string
# 打开文件
def OpenPeFile(filename):
# 判断是否是ZIP压缩包
if filename.lower().endswith('.zip'):
try:
oZipfile = zipfile.ZipFile(filename, 'r')
file = oZipfile.open(oZipfile.infolist()[0], 'r', C2BIP3('infected'))
except Exception:
print(sys.exc_info()[1])
sys.exit()
oPE = pefile.PE(data=file.read())
file.close()
oZipfile.close()
# 如果是空则
elif filename == '':
oPE = False
return oPE
# 否则直接打开文件
else:
oPE = pefile.PE(filename)
return oPE
if __name__ == "__main__":
pe = OpenPeFile("d://lyshark.exe")
print(pe.FILE_HEADER.dump())
print(pe.dump_dict())
21.1.2 解析PE头部数据
如下代码实现了解析PE结构中头部基本数据,在GetHeader
函数内,我们首先通过pe.FILE_HEADER.Machine
成员判断当前读入的文件的位数信息,通过pe.FILE_HEADER.Characteristics
可判断PE文件的类型,通常为EXE可执行文件或DLL动态链接库文件,通过AddressOfEntryPoint
加上ImageBase
则可获取到程序的实际装载地址,压缩数据的计算可通过hashlib
模块对PE文件字节数据进行计算摘要获取,最后是附加数据,通过get_overlay_data_start_offset
则可获取到,并依次循环即可输出所有附加数据。
import hashlib
import pefile
# 计算得到数据长度,自动使用推荐大小
def NumberOfBytesHumanRepresentation(value):
if value <= 1024:
return '%s bytes' % value
elif value < 1024 * 1024:
return '%.1f KB' % (float(value) / 1024.0)
elif value < 1024 * 1024 * 1024:
return '%.1f MB' % (float(value) / 1024.0 / 1024.0)
else:
return '%.1f GB' % (float(value) / 1024.0 / 1024.0 / 1024.0)
# 获取PE头部基本信息
def GetHeader(pe):
raw = pe.write()
# 扫描基本信息
print("-" * 50)
print("程序基本信息")
print("-" * 50)
if (hex(pe.FILE_HEADER.Machine) == "0x14c"):
print("程序位数: {}".format("x86"))
if (hex(pe.FILE_HEADER.Machine) == "0x8664"):
print("程序位数: {}".format("x64"))
if (hex(pe.FILE_HEADER.Characteristics) == "0x102"):
print("程序类型: Executable")
elif (hex(pe.FILE_HEADER.Characteristics) == "0x2102"):
print("程序类型: Dynamic link library")
if pe.OPTIONAL_HEADER.AddressOfEntryPoint:
oep = pe.OPTIONAL_HEADER.AddressOfEntryPoint + pe.OPTIONAL_HEADER.ImageBase
print("实际入口: {}".format(hex(oep)))
print("映像基址: {}".format(hex(pe.OPTIONAL_HEADER.ImageBase)))
print("虚拟入口: {}".format(hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint)))
print("映像大小: {}".format(hex(pe.OPTIONAL_HEADER.SizeOfImage)))
print("区段对齐: {}".format(hex(pe.OPTIONAL_HEADER.SectionAlignment)))
print("文件对齐: {}".format(hex(pe.OPTIONAL_HEADER.FileAlignment)))
print("区块数量: {}".format(int(pe.FILE_HEADER.NumberOfSections + 1)))
print('熵值比例: %f (Min=0.0, Max=8.0)' % pe.sections[0].entropy_H(raw))
# 计算压缩数据
print("-" * 50)
print("计算压缩数据")
print("-" * 50)
print('MD5 : %s' % hashlib.md5(raw).hexdigest())
print('SHA-1 : %s' % hashlib.sha1(raw).hexdigest())
print('SHA-256 : %s' % hashlib.sha256(raw).hexdigest())
print('SHA-512 : %s' % hashlib.sha512(raw).hexdigest())
# 扫描文件末尾是否存在附加数据
print("-" * 50)
print("扫描附加数据")
print("-" * 50)
overlayOffset = pe.get_overlay_data_start_offset()
if overlayOffset != None:
print("起始文件位置: 0x%08x"%overlayOffset)
overlaySize = len(raw[overlayOffset:])
print("长度: 0x%08x %s %.2f%%"%(overlaySize, NumberOfBytesHumanRepresentation(overlaySize), float(overlaySize) / float(len(raw)) * 100.0))
print("MD5: %s" %hashlib.md5(raw[overlayOffset:]).hexdigest())
print("SHA-256: %s" %hashlib.sha256(raw[overlayOffset:]).hexdigest())
if __name__ == "__main__":
pe = pefile.PE("d://lyshark.exe")
GetHeader(pe)
21.1.3 解析节表数据
运用PEFile
模块解析节表也很容易,如下代码中分别实现了两个功能函数,函数ScanSection()
用于输出当前文件的所有节表数据,其中通过pe.FILE_HEADER.NumberOfSections
得到节表数量,并通过循环的方式依次解析pe.sections
中的每一个节中元素,函数CheckSection()
则可用于计算PE
文件节大小以及节MD5
值,完整代码如下所示;
import hashlib
import pefile
# 计算得到数据长度,自动使用推荐大小
def NumberOfBytesHumanRepresentation(value):
if value <= 1024:
return '%s bytes' % value
elif value < 1024 * 1024:
return '%.1f KB' % (float(value) / 1024.0)
elif value < 1024 * 1024 * 1024:
return '%.1f MB' % (float(value) / 1024.0 / 1024.0)
else:
return '%.1f GB' % (float(value) / 1024.0 / 1024.0 / 1024.0)
# 输出所有的节
def ScanSection(pe):
print("-" * 100)
print("{:10s}{:10s}{:10s}{:10s}{:10s}{:10s}{:10s}{:10s}".
format("序号","节区名称","虚拟偏移","虚拟大小","实际偏移","实际大小","节区属性","熵值"))
print("-" * 100)
section_count = int(pe.FILE_HEADER.NumberOfSections + 1)
for count,item in zip(range(1,section_count),pe.sections):
print("%d\t\t\t%-10s\t0x%.8X\t0x%.8X\t0x%.8X\t0x%.8X\t0x%.8X\t%f"
%(count,(item.Name).decode("utf-8"),item.VirtualAddress,item.Misc_VirtualSize,item.PointerToRawData,item.SizeOfRawData,item.Characteristics,item.get_entropy()))
print("-" * 100)
# 计算所有节的MD5
def CheckSection(pe):
print("-" * 100)
print("序号\t\t节名称\t\t文件偏移\t\t大小\t\tMD5\t\t\t\t\t\t\t\t\t\t节大小")
print("-" * 100)
# 读取PE文件到内存
image_data = pe.get_memory_mapped_image()
section_count = int(pe.FILE_HEADER.NumberOfSections + 1)
for count,item in zip(range(1,section_count),pe.sections):
section_data = image_data[item.PointerToRawData: item.PointerToRawData + item.SizeOfRawData - 1]
data_size = NumberOfBytesHumanRepresentation(len(section_data))
hash_value = hashlib.md5(section_data).hexdigest()
print("{}\t{:10s}\t{:10X}\t{:10X}\t{:30s}\t{}".format(count,(item.Name).decode("utf-8"),item.PointerToRawData,item.SizeOfRawData,hash_value,data_size))
print("-" * 100)
if __name__ == "__main__":
pe = pefile.PE("d://lyshark.exe")
ScanSection(pe)
CheckSection(pe)
21.1.4 节区RVA与FOA互转
此处计算节偏移地址,相信读者能理解,在之前的文章中我们详细的介绍了PE文件如何进行RVA
与FOA
以及VA
之间的转换的,如果是在平时的恶意代码分析中需要快速实现转换那么使用Python将是一个不错的选择,如下代码中RVAToFOA
可将一个RVA
相对地址转换为FOA
文件偏移,FOAToRVA
则可实现将一个FOA文件偏移转换为RVA先对地址,当然PeFile模块内也提供了get_rva_from_offset
实现从FOA转RVA,get_offset_from_rva
则是从RVA到FOA,读者可自行选择不同的转换方式。
import pefile
# 将RVA转换为FOA的函数
def RVAToFOA(pe,rva):
for item in pe.sections:
Section_Start = item.VirtualAddress
Section_Ends = item.VirtualAddress + item.SizeOfRawData
if rva >= Section_Start and rva < Section_Ends:
return rva - item.VirtualAddress + item.PointerToRawData
return -1
# 将FOA文件偏移转换为RVA相对地址
def FOAToRVA(pe,foa):
ImageBase = pe.OPTIONAL_HEADER.ImageBase
NumberOfSectionsCount = pe.FILE_HEADER.NumberOfSections
for index in range(0,NumberOfSectionsCount):
PointerRawStart = pe.sections[index].PointerToRawData
PointerRawEnds = pe.sections[index].PointerToRawData + pe.sections[index].SizeOfRawData
if foa >= PointerRawStart and foa <= PointerRawEnds:
rva = pe.sections[index].VirtualAddress + (foa - pe.sections[index].PointerToRawData)
return rva
return -1
# 内部功能实现FOA->RVA互转
def inside(pe):
# 从FOA获取RVA 传入十进制
rva = pe.get_rva_from_offset(3952)
print("对应内存RVA: {}".format(hex(rva)))
# 从RVA获取FOA 传入十进制
foa = pe.get_offset_from_rva(rva)
print("对应文件FOA: {}".format(foa))
if __name__ == "__main__":
pe = pefile.PE("d://lyshark.exe")
ref = RVAToFOA(pe,4128)
print("RVA转FOA => 输出十进制: {}".format(ref))
ref = FOAToRVA(pe,1056)
print("FOA转RVA => 输出十进制: {}".format(ref))
21.1.5 解析数据为Hex格式
如下代码片段实现了对PE文件的各种十六进制操作功能,封装cDump()
类,该类内由多个类函数可以使用,其中HexDump()
可用于将读入的PE文件以16进制方式输出,HexAsciiDump()
则可用于输出十六进制以及所对应的ASCII格式,GetSectionHex()
用于找到PE文件的.text
节,并将此节内的数据读入到内存中,这段代码可以很好的实现对PE文件的十六进制输出与解析,读者可在实际开发中使用。
import pefile
from io import StringIO
import sys
import re
dumplinelength = 16
def CIC(expression):
if callable(expression):
return expression()
else:
return expression
def IFF(expression, valueTrue, valueFalse):
if expression:
return CIC(valueTrue)
else:
return CIC(valueFalse)
class cDump():
def __init__(self, data, prefix='', offset=0, dumplinelength=16):
self.data = data
self.prefix = prefix
self.offset = offset
self.dumplinelength = dumplinelength
# 输出指定位置的十六进制格式
def HexDump(self):
oDumpStream = self.cDumpStream(self.prefix)
hexDump = ''
for i, b in enumerate(self.data):
if i % self.dumplinelength == 0 and hexDump != '':
oDumpStream.Addline(hexDump)
hexDump = ''
hexDump += IFF(hexDump == '', '', ' ') + '%02X' % self.C2IIP2(b)
oDumpStream.Addline(hexDump)
return oDumpStream.Content()
def CombineHexAscii(self, hexDump, asciiDump):
if hexDump == '':
return ''
countSpaces = 3 * (self.dumplinelength - len(asciiDump))
if len(asciiDump) <= self.dumplinelength / 2:
countSpaces += 1
return hexDump + ' ' + (' ' * countSpaces) + asciiDump
# 输出指定位置的十六进制格式以及ASCII字符串
def HexAsciiDump(self):
oDumpStream = self.cDumpStream(self.prefix)
hexDump = ''
asciiDump = ''
for i, b in enumerate(self.data):
b = self.C2IIP2(b)
if i % self.dumplinelength == 0:
if hexDump != '':
oDumpStream.Addline(self.CombineHexAscii(hexDump, asciiDump))
hexDump = '%08X:' % (i + self.offset)
asciiDump = ''
if i % self.dumplinelength == self.dumplinelength / 2:
hexDump += ' '
hexDump += ' %02X' % b
asciiDump += IFF(b >= 32 and b <= 128, chr(b), '.')
oDumpStream.Addline(self.CombineHexAscii(hexDump, asciiDump))
return oDumpStream.Content()
class cDumpStream():
def __init__(self, prefix=''):
self.oStringIO = StringIO()
self.prefix = prefix
def Addline(self, line):
if line != '':
self.oStringIO.write(self.prefix + line + '\n')
def Content(self):
return self.oStringIO.getvalue()
@staticmethod
def C2IIP2(data):
if sys.version_info[0] > 2:
return data
else:
return ord(data)
# 只输出十六进制数据
def HexDump(data):
return cDump(data, dumplinelength=dumplinelength).HexDump()
# 输出十六进制与ASCII字符串
def HexAsciiDump(data):
return cDump(data, dumplinelength=dumplinelength).HexAsciiDump()
# 找到指定节并读取hex数据
def GetSectionHex(pe):
ImageBase = pe.OPTIONAL_HEADER.ImageBase
for item in pe.sections:
# 判断是否是.text节
if str(item.Name.decode('UTF-8').strip(b'\x00'.decode())) == ".text":
# print("虚拟地址: 0x%.8X 虚拟大小: 0x%.8X" %(item.VirtualAddress,item.Misc_VirtualSize))
VirtualAddress = item.VirtualAddress
VirtualSize = item.Misc_VirtualSize
ActualOffset = item.PointerToRawData
StartVA = hex(ImageBase + VirtualAddress)
StopVA = hex(ImageBase + VirtualAddress + VirtualSize)
print("[+] 代码段起始地址: {} 结束: {} 实际偏移:{} 长度: {}".format(StartVA, StopVA, ActualOffset, VirtualSize))
# 获取到.text节区间内的数据
hex_code = pe.write()[ActualOffset: VirtualSize]
return hex_code
else:
print("程序中不存在.text节")
return 0
return 0
REGEX_STANDARD = '[\x09\x20-\x7E]'
def ExtractStringsASCII(data):
regex = REGEX_STANDARD + '{%d,}'
return re.findall(regex % 4, data)
def ExtractStringsUNICODE(data):
regex = '((' + REGEX_STANDARD + '\x00){%d,})'
return [foundunicodestring.replace('\x00', '') for foundunicodestring, dummy in re.findall(regex % 4, data)]
# 将传入Hex字符串以每16字符分割在一个列表内
def ExtractStrings(data):
return ExtractStringsASCII(data) + ExtractStringsUNICODE(data)
if __name__ == "__main__":
pe = pefile.PE("d://lyshark.exe")
# 得到.text节内数据
ref = GetSectionHex(pe)
# 转为十六进制格式
dump_hex = HexDump(ref)
print(dump_hex)
# 打包为每16字符一个列表
dump_list = ExtractStrings(dump_hex)
print(dump_list)
21.1.6 解析数据目录表
数据目录表用于记录可执行文件的数据目录项在文件中的位置和大小。数据目录表共有16个条目,每个条目都对应着一个数据目录项,每个数据目录项都描述了可执行文件中某一部分的位置和大小。
数据目录表的解析可以使用pe.OPTIONAL_HEADER.NumberOfRvaAndSizes
首先获取到数据目录表的个数,接着二通过循环个数依次解包OPTIONAL_HEADER.DATA_DIRECTORY
里面的每一个列表,在循环列表时依次解包输出即可。
import pefile
# 将RVA转换为FOA的函数
def RVAToFOA(pe,rva):
for item in pe.sections:
Section_Start = item.VirtualAddress
Section_Ends = item.VirtualAddress + item.SizeOfRawData
if rva >= Section_Start and rva < Section_Ends:
return rva - item.VirtualAddress + item.PointerToRawData
return -1
# 扫描数据目录表
def ScanOptional(pe):
optional_size = pe.OPTIONAL_HEADER.NumberOfRvaAndSizes
print("数据目录表个数: {}".format(optional_size))
print("-" * 100)
print("编号 \t\t\t 目录RVA\t\t 目录FOA\t\t\t 长度\t\t 描述信息")
print("-" * 100)
for index in range(0,optional_size):
va = int(pe.OPTIONAL_HEADER.DATA_DIRECTORY[index].VirtualAddress)
print("%03d \t\t 0x%08X\t\t 0x%08X\t\t %08d \t\t"
%(index,
pe.OPTIONAL_HEADER.DATA_DIRECTORY[index].VirtualAddress,
RVAToFOA(pe,va),
pe.OPTIONAL_HEADER.DATA_DIRECTORY[index].Size
),end="")
if index == 0:
print("Export symbols")
if index == 1:
print("Import symbols")
if index == 2:
print("Resources")
if index == 3:
print("Exception")
if index == 4:
print("Security")
if index == 5:
print("Base relocation")
if index == 6:
print("Debug")
if index == 7:
print("Copyright string")
if index == 8:
print("Globalptr")
if index == 9:
print("Thread local storage (TLS)")
if index == 10:
print("Load configuration")
if index == 11:
print("Bound Import")
if index == 12:
print("Import Address Table")
if index == 13:
print("Delay Import")
if index == 14:
print("COM descriptor")
if index == 15:
print("NoUse")
if __name__ == "__main__":
pe = pefile.PE("d://lyshark.exe")
ScanOptional(pe)
21.1.7 解析导入导出表
导入表和导出表都是PE文件中的重要数据结构,分别记录着一个模块所导入和导出的函数和数据,如下所示则是使用PeFile
模块实现对导入表与导出表的解析工作,对于导入表ScanImport
的解析需要通过pe.DIRECTORY_ENTRY_IMPORT
获取到完整的导入目录,并通过循环的方式输出x.imports
中的数据即可,而对于导出表ScanExport
则需要在pe.DIRECTORY_ENTRY_EXPORT.symbols
导出符号中解析获取。
import pefile
# 输出所有导入表模块
def ScanImport(pe):
print("-" * 100)
try:
for x in pe.DIRECTORY_ENTRY_IMPORT:
for y in x.imports:
print("[*] 模块名称: %-20s 导入函数: %-14s" %((x.dll).decode("utf-8"),(y.name).decode("utf-8")))
except Exception:
pass
print("-" * 100)
# 输出所有导出表模块
def ScanExport(pe):
print("-" * 100)
try:
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
print("[*] 导出序号: %-5s 模块地址: %-20s 模块名称: %-15s"
%(exp.ordinal,hex(pe.OPTIONAL_HEADER.ImageBase + exp.address),(exp.name).decode("utf-8")))
except:
pass
print("-" * 100)
if __name__ == "__main__":
pe = pefile.PE("d://lyshark.exe")
ScanImport(pe)
ScanExport(pe)
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/92a3370c.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
21.1 使用PEfile分析PE文件的更多相关文章
- PE文件详解(七)
本文转载自小甲鱼PE文件讲解系列原文传送门 这次主要说明导出表,导出表一般记录着文件中函数的地址等相关信息,供其他程序调用,常见的.exe文件中一般不存在导出表,导出表更多的是存在于dll文件中.一般 ...
- 分析PE
PE文件是Windows平台下可执行文件标准格式,浓缩了微软工程师的设计精华,历经40年依旧保持着原有的设计.分析PE文件对于研究Windows操作系统有着重要的实践意义,对于逆向分析有着重要的指导作 ...
- PE文件学习系列笔记四-C++实现PE文件的分析
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q Q:408365330 E-Mail:egojit@qq.com 综述: 首 ...
- PE文件学习系列二 DOS头分析
合肥程序员群:49313181. 合肥实名程序员群 :128131462 (不愿透露姓名和信息者勿加入)Q Q:408365330 E-Mail:egojit@qq.com PE文件结 ...
- 一个PE文件的逆向分析
一个PE文件的逆向分析 idf-ctf上有个题,是PE文件的逆向,反正对我来说做出来就是有意思的题,做不出来就没劲.言归正传,下面看一下吧 大家想玩可以去这个地方去拿题http://pan.baidu ...
- PE文件附加数据感染之Worm.Win32.Agent.ayd病毒分析
一.基本信息 样本名称:1q8JRgwDeGMofs.exe 病毒名称:Worm.Win32.Agent.ayd 文件大小:165384 字节 文件MD5:7EF5D0028997CB7DD3484A ...
- PE文件加节感染之Win32.Loader.bx.V病毒分析
一.病毒名称:Win32.Loader.bx.V 二.分析工具:IDA 5.5.OllyDebug.StudPE 三.PE文件加节感染病毒简介 PE病毒感染的方式比较多,也比较复杂也比较难分析,下面就 ...
- 手写PE文件(二)
[文章标题]: 纯手工编写的PE可执行程序 [文章作者]: Kinney [作者邮箱]: mohen_ng@sina.cn [下载地址]: 自己搜索下载 [使用工具]: C32 [操作平台]: win ...
- 获取pe文件的文件类型
工程文件petype.cpp通过调用pefile类中的函数获取文件类型. 文件类型的判断通过5个监测点完成. 监测点1:dos头的e_magic 监测点2:nt头的Signature 监测点3:文件头 ...
- 浅析MSIL中间语言——PE文件结构篇
一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...
随机推荐
- python使用docxtpl生成word模板
python-docxtpl包简单使用和实战,Python处理word,docx文件. 最近需要处理一些爬虫得到的数据来进行一些自动化报告的操作,因为需要生成的是word的报告,所以估选用docxtp ...
- stl-----map去重,排序,计数
一.map erase()删除函数:可以迭代器删除,关键字删除,成片删除. 例:1.iter=mapStu.find(1); mapStu.erase(iter); 2.int n = mapStu. ...
- 文心一言 VS chatgpt (12)-- 算法导论3.1 6~7题
六.证明:一个算法的运行时间为θ(g(n))当且仅当其最坏情况运行时间为O(g(n)),且其最好情况运行时间为Ω(g(n)) . 文心一言: chatgpt: 要证明「一个算法的运行时间为θ(g(n) ...
- go for range的坑
package main import "fmt" func main() { ParseStudent() } type student struct { Name string ...
- Java 网络编程 —— 创建非阻塞的 HTTP 服务器
HTTP 概述 HTTP 客户程序必须先发出一个 HTTP 请求,然后才能接收到来自 HTTP 服器的响应,浏览器就是最常见的 HTTP 客户程序.HTTP 客户程序和 HTTP 服务器分别由不同的软 ...
- Oracle Linux切换uek内核到rhck内核解决ACFS兼容问题
背景:协助客户做验证,客户使用的是RHEL7.6环境,我这边是OEL7.6环境,开始以为区别不大,结果acfs兼容还是遇到问题,特此记录下. 现象:asmca图形没有acfs相关内容,无法使用acfs ...
- 暑期实习开始啦「GitHub 热点速览」
无巧不成书,刚好最近有小伙伴在找实习,而 GitHub 热榜又有收录实习信息的项目在榜.所以,无意外本周特推就收录了这个实习项目,当然还有国内版本.除了应景的实习 repo 之外,还有帮你管理文件的 ...
- 【C#/.NET】Dapper使用QueryMultipleAsync执行多条SQL
目录 背景 解决方案 总结 背景 对于查询数据列表的功能,需要分页已经查询总数.这里涉及两句SQL,一个是查询分页对应的数据,第二个是Count(*); 会导致部分重复代码和两次的数据库查询. ...
- 解密Prompt系列8. 无需训练让LLM支持超长输入:知识库 & unlimiformer & PCW & NBCE
这一章我们聊聊有哪些方案可以不用微调直接让大模型支持超长文本输入,注意这里主要针对无限输入场景.之前在BERT系列中我们就介绍过稀疏注意力和片段递归的一些长文本建模方案长文本建模 BigBird &a ...
- xpoc漏洞使用与编写 浅尝
下载地址 https://github.com/chaitin/xpoc/releases 目前最新版本是 0.0.4 可能是我还是不太习惯yaml这种结构的,感觉就很反人类,所以我以前一般都还是po ...