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主题,我要讲述一个关于 ...
随机推荐
- 超实用的Go语言基础教程,让你快速上手刷题!!
背景 工欲善其事,必先利其器.掌握Go的基础语法还不够,还需要勤加练习,修习"外功",才能达到出奇制胜的效果. 在大致了解Go语言的基本语法后,我就迫不得已地想使用这门语言.可是我 ...
- 2021-08-13:给定一个每一行有序、每一列也有序,整体可能无序的二维数组 ,在给定一个正数k,返回二维数组中,最小的第k个数。
2021-08-13:给定一个每一行有序.每一列也有序,整体可能无序的二维数组 ,在给定一个正数k,返回二维数组中,最小的第k个数. 福大大 答案2021-08-13: 二分法. 代码用golang编 ...
- 用go设计开发一个自己的轻量级登录库/框架吧(业务篇)
用go设计开发一个自己的轻量级登录库/框架吧(业务篇) 本篇会讲讲框架的登录业务的实现.实现三种登录模式: 同一用户只能登录一次 同一用户多次登录多token 同一用户多次登录共享一个token 源码 ...
- Prompt learning 教学[技巧篇]:通过增加示例、引导词、特殊符号指令等方式让chatgpt输出更好的答案
Prompt learning 教学[技巧篇]:通过增加示例.引导词.特殊符号指令等方式让chatgpt输出更好的答案 技巧1:To Do and Not To Do 在问答场景里,为了让 AI 回答 ...
- 谷歌语法Github及利用方式
0x01简介 GoogleHack(谷歌语法)是指使用Google等搜索引擎对某些特定的网络主机漏洞(通常是服务器上的脚本漏洞)进行搜索,以达到快速找到漏洞主机或特定主机的漏洞的目的.比如使用搜索包含 ...
- Spring Boot 3.1中如何整合Spring Security和Keycloak
在今年2月14日的时候,Keycloak 团队宣布他们正在弃用大多数 Keycloak 适配器.其中包括Spring Security和Spring Boot的适配器,这意味着今后Keycloak团队 ...
- MAIXIII(爱芯派)的一种配网并安装nmtui的实现方法
关于一种MAIXIII(爱芯派)的一种配网并安装nmtui的实现方法 特别感谢sipped的大佬鼠以及多位群友这几天提供的帮助与支持! 0.目录 一,MAIXIII简介 二,到手图展示 三,具体操作方 ...
- 今天在内部 Galaxy 分析平台操作探针引物设计小工具程序,调用 Ensembl API 获取相关序列和信息时,发现官网 MySQL server 异常,报告问题后当天晚上就收到了回复,并且修......
本文分享自微信公众号 - 生信科技爱好者(bioitee).如有侵权,请联系 support@oschina.cn 删除.本文参与"OSC源创计划",欢迎正在阅读的你也加入,一起分 ...
- tryhackem_wonderland
涉及,解密,扫描,横向移动,纵向移动 仙境 掉进兔子洞,进入仙境. 获得shell 解法一: 目录扫描 ffuf -u http://10.10.134.189/FUZZ -w /usr/share/ ...
- Adobe 构建 IDP 之路的经验与教训
在过去的25年多时间里,我创建了软件组件和分布式框架,建立并领导了相关团队.近几年我致力于推动 Adobe 服务开发.部署和管理系统的开发人员生产力. 抽象陷阱 在云时代早期,Adobe 的每个团队都 ...