版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第七章Python逆向7.1PE文件结构7.2静态分析7.3反汇编技术7.4Hook技术习题
7.1PE文件结构
7.1.1概述编程语言大致分为三类:机器语言、汇编语言和高级语言。比如高级语言C语言的源代码经过编译链接后(参见图7-1),在Windows系统下生成的可执行程序都是以PE文件形式存储。图7-1编译链接过程
PE文件全称为PortableExecutable,意为可移植可执行文件。常见的EXE、DLL、OCX、SYS、COM文件都属于PE文件。PE文件采用段的形式存储代码和一些相关资源数据,其中数据段和代码段是每个PE文件所必需的。研究PE文件格式的主要目的,一是给应用程序添加代码功能,比如注册机代码注入(KeygenInjection);二是手动给EXE脱壳。
由于所有Win32可执行程序(除了VxD和16位的DLL)都使用PE文件格式,包括NT的内核模式驱动程序(KernelModeDrivers),因而了解PE文件格式是学习Windows系统结构的有效途径。
PE文件的格式如图7-2所示,其组成包括:
图7-2PE结构模型
(1)
DOS文件头。所有PE文件(甚至32位的DLL)都必须以一个简单的DOSMZheader开始。一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZheader之后的DOSstub。DOSstub实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它简单显示一个错误提示,类似于字符串“ThisprogramrequiresWindows”或者程序员可根据自己的意图实现完整的DOS代码。大多数情况下,它是由汇编器/编译器自动生成。DOS文件头有两个字段比较重要,分别是e_magic和e.lfanew。其中e.lfanew字段是真正PE文件头的相对偏移地址(RVA)。DOS文件头如图7-3所示。
图7-3DOS文件头
可执行程序在支持PE文件结构的操作系统中执行时,PE装载器将从DOSMZheader中找到PEheader的起始偏移量(0000003Ch),从而可以跳过DOSstub定位到真正的文件头即PE文件头,如图7-4所示。
图7-4定位真正的PE文件头
(2) PE文件头。它是有效PE文件的签名(Signature),其值为00004550h,ASCII码字符串是“PE..”。
(3) IMAGE_FILE_HEADER。它包含PE文件的基本信息,包括运行的平台、文件的区块数目、文件创建时间和IMAGE_OPTIONAL_HEADER的结构大小,如图7-5所示。
图7-5PE文件头结构
(4) IMAGE_OPTIONAL_HEADER。这是一个可选结构,用于更详细定义PE文件属性,与IMAGE_FILE_HEADER功能类似,其主要字段如图7-6所示。
图7-6PE可选头结构
(5)块表。块表是关于PE文件头与原始数据的一个映射表。其中Name是相对应的块名,VirtualAddress指明了块的RVA地址,详细信息参见图7-7。
图7-7PE块表信息
(6)(区)块。PE预定义了多个区块,.text是代码区块,.data、.rdata、.bss是数据区块,.rsrc是资源区块,.edata与.idata分别是输出和输入表区块,.debug是调试信息区块。例如:
块的相关信息参见表7-1。
在后续对PE文件的操作过程中,经常会对文件内容以及内存中的文件内容进行读取,此时将涉及多种形式的地址,因此,有必要对相关术语进行说明,具体信息参见表7-2。
表7-2中各地址之间的关系如下:
当要对一个PE文件插入代码时,有一种方法是把代码加入到一个存在的区块的未用空间里。首先需要找到一个被映射到一个块中且有执行权限的区块,最简单的方法就是直接利用.text;然后需要查找这块内的多余空间(也就是填00h的个数)。具体操作如图7-8所示。
图7-8.text起始地址 = 400000+1000=401000h
一个区块(如.text)可以通过两个变量的数值来计算其大小,这两个变量分别是VirtualSize和SizeOfRawData,如图7-9所示。
图7-9
.text区块的参数
图7-9中,Misc_VirtualSize代表Section中代码实际所占用的磁盘空间;SizeOfRawData代表根据磁盘对齐后所占的空间。通常SizeofRawData所占磁盘空间都会比VirtualSize的要大,而大出来的那部分就可以注入我们的代码了。
FileAlignment定义磁盘区块的对齐值,每个区块从对齐值的倍数偏移位置开始。
eg:当FileAlignment=200h,第一个区块在400h处开始,长度为90h,这一区块对齐后就是400h~599h,其中490h~599h用0填充。而下一区块就以600h开始。
SectionAlignment定义内存区块的对齐值,当其映射到内存中时,每个区块从页边界处开始。在X86系列CPU中,页是按照4KB(1000h)来排列的。
eg:在磁盘中开始地址是400h的块,映射到内存中的装入地址就是1000h字节处的。
地址映射关系如图7-10所示,假设add1和add2的差值为∆k,每个区块的∆k是不一样的,有
某一虚拟地址(VA) = 401112h,计算它的文件偏移地址(.text区块中的差值为0C00h)如下:
图7-10地址映射关系
7.1.2pefile
pefile是一个多平台Python模块,能够对DOS_Header、OPTIONAL_HEADER和PESECTIONS进行全面的解析,也可以对PE结构进行修改。利用pefile模块可以得到PE文件关键的数据结构,从而进行静态分析和二次开发。
pefile模块的一些常用方法主要有:
(1)
set_bytes_at_offset():用给定的字符串覆盖文件偏移量处的字节。
(2)
set_bytes_at_rva():用给定的字符串覆盖RVA对应的文件偏移量处字节。
(3)
parse_export_directory():解析导出目录。
(4)
parse_import_directory:遍历并解析导入目录。
(5)
parse_resources_directory:解析resources目录。
(6)
write():写入PE文件。
下面介绍pefile模块的基本用法。
(1)获取PE结构信息。导入模块并解析文件,将fast_load参数设置为True,将阻止解析目录。代码如下:
(2)获取各个区块的信息,代码如下:
以上代码运行结果如图7-11所示。
图7-11运行结果
(3)修改信息写入磁盘。PE文件成功解析后,可以修改PE结构里面的数据,并作为PE实例属性进行修改。代码如下:
pe.OPTIONAL_HEADER.AddressOfEntryPoint=0xdeadbeef
pe.write(filename='file_to_write.exe')
(4)获取导入的dll和表,代码如下:
forentryinpe.DIRECTORY_ENTRY_IMPORT:
printentry.dll
forimpinentry.imports:
print'\t',hex(imp.address),
7.1.3脚本实例
[例7-1]
标识出hash值与函数的匹配关系。
shellcode和恶意软件经常使用hash算法混淆加载函数和链接库,想要标识出hash值与函数的匹配关系,这就需要通过爬取Windows操作系统常用的链接库列表,然后遍历里面的函数。下述代码以kernel32.dll为例。
[例7-2]
结合pydasm进行反汇编。
将pydasm和pefile模块结合,先利用pefile模块对PE文件解析,提取原始数据;之后利用pydasm将其反汇编为汇编语句。
第03行对程序进行解析;第04行访问PE结构的AddressOfEntryPoint,得到程序执行入口RVA(相对虚拟地址),再通过pe.OPTIONAL_HEADER.ImageBase得到程序的基地址,两个地址的和就是VA(内存虚拟地址);第06行返回PE文件的内存布局相对应的数据。接下来,对数据进行反汇编,第09行pydasm.get_instruction创建一个指令对象;第10行将指令对象转换为字符串表示。
上述代码运行结果如图7-12所示。
图7-12代码运行结果
[例7-3]
利用pefile对指定程序添加一个MessageBox弹窗。
要求:目标程序和执行结果如图7-13所示。
图7-13目标程序和执行结果
首先构造一段弹窗的shellcode,将其插入到原程序中;然后修改程序入口地址为shellcode;再利用jmp指令跳转到原程序入口,以保证原程序的正确执行。
(1)获取MessageBox地址(get_messagebox_address.c):
运行C语言程序,得到MessageBoxA的地址为0x751ffd1e。注意,这个地址对于每台计算机是不一样的。
(2)查询call和jmp的机器指令:
call->E8
jmp->E9
(3)构造shellcode
函数调用前4个参数入栈如图7-14所示。
图7-14参数入栈
在上述代码中,0x00是待填充字段;E8的反汇编指令是call;E9的反汇编指令是jmp,后面也留空,等后续计算完成后再填充。
E8后边的值 =真正要跳转地址 -
E8当前指令的地址 + E8当前指令的大小
=真正要跳转地址 -
E8当前指令的下一行地址
(4)代码区添加shellcode。首先,需要分析目标程序的PE结构,其主要字段如表7-3所示。
程序运行时,PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为ImageBase + AddressOfEntryPoint。第一区块部分成员信息如表7-4所示。
然后,计算call后面的地址值。
①
MessageBox地址:0x751FFD1E(根据get_messagebox_address.c得到)。
②
call的机器指令:E8。
③ E8的下一指令在文件中的地址:X。
④ E8的下一指令映射到内存中的地址:X - PointerToRawData+ImageBase+VirtualAddress。
⑤ E8(call)后面的值:MessageBox - E8的下一指令映射到内存中的地址。
接着,计算jmp后面的地址值。
要保证所创建的MessageBox关闭后,程序能够正常运行,可用jmp指令跳到原来的OEP入口地址。
①真正要跳转的地址:ImageBase+AddressOfEntryPoint。
② jmp的机器指令:E9。
③ E9的下一指令在文件中的地址:Y。
④ E9的下一指令映射到内存中的地址:Y - PointerToRawData+ImageBase+VirtualAddress。
⑤ E9(jmp)后面的值:真正要跳转的地址 -E9的下一指令映射到内存中地址。
最后,计算并修改AddressOfEntryPoint。
文件中shellcode起始地址:Z。
AddressOfEntryPoint:Z-PointerToRawData+VirtualAddress。
接下来,可通过一个脚本来计算修改值:
以上代码运行结果如图7-15所示。
图7-15运行结果
7.2
静态分析
7.2.1概述静态分析是指在程序尚未运行时进行的逆向分析行为,但也不是直接在硬盘上去分析,而是需要通过逆向软件加载入内存进行分析。当我们遇到软件的某一模块无法单独运行或者病毒程序等情况时,就需要将目标软件的二进制指令反汇编为汇编代码,实现程序流程的静态分析。
7.2.2IDAPython函数
IDAPython常用到的一些函数如表7-5所示。
7.2.3脚本实例
1.搜寻危险函数的交叉代码
有一些函数被安全审计人员定义为危险函数,对这类函数的使用不当将会产生一些安全问题,比如字符串拷贝例程(strcpy、sprintf)和缺乏长度检查机制的拷贝函数(memcpy)。利用IDA可以查看程序使用的函数模块名字,如图7-16所示。
图7-16调用的模块名
下面将编写一个脚本,用于搜寻程序中存在问题(或危险)的函数(如strcpy、sprintf和memcpy)以及所有调用这些危险函数的代码引用。
将危险函数调用指令背景色设置为红色,如图7-17中灰色横条所示。危险函数的地址如图7-18所示。
图7-17将危险函数背景色设置为红色图7-18危险函数地址
上述代码的第02行列出危险函数;第03、04行遍历这些危险函数,并通过LocByName(
)函数返回危险函数的内存地址;第06行CodeRefsTo()得到该危险函数地址为目标地址的交叉引用;最后输出地址,在第11行把这些危险函数的调用指令标注为红色。
2.对重复函数进行高亮
对于许多复杂的代码,不仅能静态标识指令,并且能统计出对应的指令被使用了多少次。注意:因为要统计指令使用了多少次,所以要建立debugger并运行。
get_new_color(
)函数检索出执行的每一行并标识颜色,代码会获取给定行数代码的当前颜色,决定将代码设置成什么颜色并进行设定。将无颜色的代码行数设置为高亮的蓝色,对多次执行的代码函数设置为深蓝色,在图7-19中,分别对应图中的浅灰色与深灰色。
图7-19不同代码的高亮
启动调试器,然后通过调用RunTo(BeginEA())函数执行到函数的入口处,接下来调用的GetDebuggerEvent()函数会等待直到断点到达。
接着调用EnableTracing()函数来打开IDA的跟踪功能;然后调用GetDebuggerEvent()函数会继续执行调试器,配置跟踪步骤;最后进行颜色获取和设定,运行结果如图7-20所示。
图7-20程序的流程图
在图7-20中,整块的代码分为几种不同的颜色,函数的开始部分和结尾部分被执行了一次,设置为浅蓝色(即图中浅灰色所示),因为这个程序是一个if分支语句,所以在右边的代码没有被执行则为白色;左边的代码为深蓝色(即图中深灰色所示),因为这里是一个循环语句,被多次执行。这使得我们能够更容易理解代码执行的流程。
3.跟踪程序执行路径
每当命中一个断点时,打印出所在地址。开启IDA的内建调试器,就会看到相关的输出信息,这些输出信息能够使得调试器者更好地了解程序内部函数的调用情况,以及它们之间的调用顺序。
第02行首先建立一个FuncPath类,该类是DBG_Hooks的继承类;第03行到第05行编写类的函数dbg_bpt()用于断点的处理;第06行debugger=FuncPath()建立一个对象debugger.hook(),将钩子装入IDA内建调试器。
第09行遍历所有的函数,并添加相应的断点。其中第11行的SetBptAttr()设置一个断点控制标志,这个标志的作用是告诉调试器每当有断点被触发时,调试器不会停下。如果把这一步去掉,每次断点命中只能手动将调试器复位。
最后输出断点的个数。
上述代码执行结果如图7-21所示。双击断点地址就可以定位到汇编代码,如图7-22所示。
图7-21断点被命中图7-22定位汇编代码
7.3反 汇 编 技 术
7.3.1
Capstone简介
Capstone是一个轻量级的支持多平台多架构的反汇编框架。它可以支持多种硬件构架,如ARM、ARM64、MIPS和x86。该框架使用C语言实现,但它还支持C++、Python、Ruby、OCaml、C#、Java和Go语言,具有很好的扩展性。因此,该框架被256种工具所集成,如Cuckoo、Binwalk和IntelliJIDEA。渗透测试人员可以通过Python、Ruby语言编写脚本,引入Capstone引擎,从而构建自己的反汇编工具。
7.3.2Capstone安装
方式一:Python安装。
(1)
On*nixplatforms:
$sudopipinstallcapstone
(2) OnWindowsplatforms:
$pipinstallcapstone
或
$pipinstallcapstone-windows
方式二:在终端输入。
gitclone/aquynh/capstone
cdcapstone
make
makeinstall
7.3.3一个简单例子
Capstone安装完毕后,现在回答本节开头的问题,如果我们获取了一段机器码,那么该如何将其反汇编成易读易懂的汇编语言?在介绍Capstone的具体用法之前,先来看一个简单的例子。
[例7-4]
分别用Capstone和IDAPro将一段机器码反汇编成汇编代码。
(1) Capstone。
样本输出如下:
首先在第02行导入了Capstone;然后第03行是原始二进制代码,本例中的代码用十六进制来表示;接着在第04行创建了一个Cs类的实例对象并赋值给变量md;本例中,要反汇编的是x86架构、32位模式的机器码;在第05行通过Cs类的disasm()函数对机器码进行反汇编,第一个参数是机器码Byte数据,第二个参数是这段代码的“基地址”;该函数将反汇编所有代码或者直到遇到一条错误的指令为止;disasm(
)将会返回一个包含CsInsn对象的列表,而for循环将会遍历此列表中所有的CsInsn对象;最后第06行将输出地址信息、助记符、操作数字符串。
(2) IDAPro。
使用IDAPro对同一段机器码进行反汇编的结果如图7-23所示。
图7-23
IDAPro反汇编结果
7.3.4
Capstone基本用法
1.
classCs
在例7-4代码的第04行中出现了一个类——Cs类。当初始化一个Cs类时,需要给这个类传入两个参数:硬件架构(arch)和硬件模式(mode)。
目前,Capstone支持8种具有相应硬件模式的硬件架构,具体如表7-6所示。
注:还有几种模式是由表中一些基本模式组合而成的,具体内容参见网址/lang_python.html。
以下代码为初始化一个ARM架构、ARM模式的Cs类:
fromcapstoneimport*
CODE=“\x55\x48\x8b\x05\xb8\x13\x00\x00”
md=Cs(CS_ARCH_ARM,CS_MODE_ARM)
同样,Cs类也提供了一些常用的属性。
1)
detail
默认情况下,Capstone不会生成反汇编指令的详细信息,如读/写隐式寄存器和语义组等。若需要这些信息,则需要启用该选项,代码如下:
md=Cs(CS_ARCH_x86,CS_MODE_32)
md.detail=True
但是,启用detail选项后将生成更多的详细信息,从而消耗更多的内存,降低运行速度,因此只有在需要它时才启用。如果不需要该选项,那么可以将其关闭,代码如下:
md.detail=False
2)
syntax
在介绍syntax选项的基本用法之前,先介绍一下Intel语法和AT&T语法的区别。
(1)运算表达式(operands,即运算单元)的书写顺序相反。
Intel格式:<指令><目标><源>。
AT&T格式:<指令><源><目标>。
(2)
AT&T语法中,在寄存器名称之前使用百分号(%)标记,在立即数之前使用美元符号($)标记。AT&T语法使用圆括号,而Intel语法则使用方括号。
(3)
AT&T语法里,每个运算操作符都需要声明操作数的类型,如q指代quad(64位),l指代32位long型数据,w指代16位word型数据,b指代8位byte型数据等。
Intel语法和AT&T语法的其他区别请参考Sun公司发布的《x86AssemblyLanguageReferenceManual》。
x86汇编的默认语法是Intel语法。如果需要切换到AT&T语法,则需要对syntax选项进行相应操作。如下述代码:
以上代码运行结果如下:
3)
mode
我们可以通过mode选项更改模式。这对于Arm来说是非常有用的,因为某些操作需要在Arm和Thumb模式之间切换,这也是x86经常发生的,我们希望在保护模式和实模式代码之间来回切换。例如:
以上代码运行结果如下:
Cs类提供了反汇编的方法——disasm()函数:
disasm(code,offset,count=0)
disasm(
)函数是Cs类中一个非常重要的方法,其功能是对机器码进行反汇编并返回一个CsInsn对象。其中,第一个参数code表示需要进行反汇编的机器码数据;第二个参数offset表示这段代码的“基地址”。这个函数将反汇编所有的机器码或者直到遇到一条错误的指令为止。
在Cs类中还有一个轻量级的反汇编二进制代码的方法——disasm_lite()。
disasm_lite(code,offset,count=0)
disasm_lite()函数运行速度比disasm()的快20%左右,因为与disasm()方法不同,所以disasm_lite()只返回元组(address,size,mnemonic,op_str),而不是CsInsn对象。
下面介绍初始化一个x86架构、64位模式的Cs类,并用反汇编二进制代码并打印出汇编指令。代码如下:
2.
classCsInsn
CsInsn类是另一个很重要同时也很常用的类。CsInsn类中包含了我们想要获得的机器码的所有内部信息,如指令的地址、助记符等。这里简单介绍一些CsInsn类中常用的属性,如表7-7所示。
下述代码可提取指令读取的隐式寄存器的详细信息,以及指令所属的语义组。其中,md.disasm(CODE,0x1000)反汇编了“CODE”中的机器码并返回了CsInsn对象,所以一般是不需要使用者自己去初始化一个CsInsn对象。
以上代码运行结果如下:
7.3.5Capstone用法举例
[例7-5]
结合pefile,按照x86架构和32位模式,反汇编可执行文件capstone.exe。
7.4Hook技术
7.4.1uhooker简介
uhooker(即UniversalHooker)是拦截程序执行的工具,可以拦截DLL内部的API调用,以及查看内存中可执行文件的任意地址。
所谓通用(Universal)是指拦截一个程序中的函数有不同的方式,例如可以通过设置软件断点(int3h)、硬件断点(cpuregs)或覆盖函数的序言来跳转到一个stub。以上提到的方式中,特别是最后一个,需要编写钩子脚本文件的程序员要熟悉想要拦截的函数。如果代码是用C/C++语言这样的编程语言来编写的,那么每次拦截函数时,代码都需要被重新编译。但是uhooker允许程序员使用解释性语言(Python)为不同的API和非API函数编写钩子,而不需要编译任何东西。
钩子脚本文件用Python编写,当需要修改时,无须重新编译处理程序。而且,每次调用钩子函数时,钩子脚本文件都从磁盘重新加载(由服务器执行),这意味着可以更改钩子函数的行为,而不必重新编译代码,或必须重启正在被分析的应用程序。
7.4.2
uhooker安装
uhooker安装十分简单,只需要将安装包内uhooker.dll、server.py和proxy.py三个文件全部拷贝到OllyDbg的安装目录下即可。
注:所有的Python脚本文件(即钩子函数文件)必须被放置在OllyDbg的安装目录下;否则uhooker将找不到这些脚本文件,这些文件也就无法工作。
7.4.3工作原理
1.
uhooker的基本组件
(1)
uhooker内核,一个OllyDbg插件(uhooker.dll)。
(2)配置文件(.cfg文件)。
(3)用Python编写的服务器(server.py),用于处理与uhooker内核的通信。
(4)一个使用Python(proxy.py)编写的库,其中包含了不同的用于实现与uhooker内核通信的函数。开发人员可以使用这些函数对被拦截的进程进行不同的操作,例如读取内存、写入内存等。
(5)一个由开发人员编写的Python脚本文件包含了被钩住的函数或地址的代码。该模块使用Python库proxy.py完成对相应进程的拦截。
2.运行过程
在解析完配置文件之后,OllyDbg加载一个定义了被拦截函数或地址的配置文件。当一个钩子被触发时,uhooker内核与服务端进行通信并发送有关被拦截的函数或地址的信息,并且服务端执行配置文件中定义的相应的钩子函数。具体运行过程如图7-24所示。
图7-24运行过程
7.4.4基本用法
uhooker配置文件是一个常规的文本文件,其每行分别定义了被拦截的函数或地址。uhooker支持三种类型的钩子,分别是:
(1)钩子在进入函数前(类型“B”)。
(2)钩子在函数返回后(类型“A”)。
(3)钩子在当执行到该地址时(类型“*”)。
Uhooker在拦截时可以采用两种不同语法:
(1)拦截从一个dll导出的函数:
name_of_dll:function_name:number_of_parametes:python_module.hook_handler_name:hook_type
例如在文件mymodule.py中,使用钩子CreateFileA_handler在CreateFileA函数执行前拦截该函数的语法如下:
kernel32.dll:CreateFileA:7:mymodule.CreateFileA_handler:B
(2)在一个进程的某个地址处设置断点:
field_not_used:address_to_hook_in_hex:field_not_used:python_module.hook_handler_name:hook_type
例如在mymodule.py文件中,使用钩子anybp在地址0x401000处设置断点。
Dummy.dll:0x401000:0:mymodule.anybp:*
一旦配置文件编写完毕,就可以通过OD来加载。在菜单栏中“Plugins”的下拉列表中选中“uhooker”,再选中“LoadCfgFile”,如图7-25所示。
图7-25选中“LoadCfgFile”
在弹出的对话框中选择要加载的配置文件
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026深圳能源春季校园招聘备考题库有完整答案详解
- 2026西藏中共林芝市委员会宣传部招聘公益性岗位工作人员2人备考题库含完整答案详解(夺冠)
- 2026恒丰银行总行实习生招收备考题库有完整答案详解
- 2026陕西延安北方医院招聘备考题库附答案详解【巩固】
- 2026云南银卫达保安服务有限公司招聘法律顾问兼董事会秘书1人备考题库(含答案详解)
- 2026山东青岛海上综合试验场有限公司招聘38人备考题库含完整答案详解(必刷)
- 2026浙江温州市残疾人康复服务指导中心招聘编外康复教师2人备考题库附参考答案详解(a卷)
- 2026广东佛山三水区白坭镇岗头中心幼儿园春季招聘1人备考题库(真题汇编)附答案详解
- 2026西南石油大学南充校区安全与后勤保障部招聘2名临时聘用员工备考题库(四川)【满分必刷】附答案详解
- 2026浙江台州市温岭市滨海镇招聘编外工作人员1人备考题库(易错题)附答案详解
- 电器元件销售管理制度
- 三种方法评标计算(自带公式)
- 研究生导师培训讲座
- 《西藏自治区地质灾害危险性评估报告编制及审查技术要求(试行)》
- 3.2 工业的区位选择 课件 2024-2025学年高中地理鲁教版(2019)必修第二册
- DB13-T 6027-2024 超设计使用年限 医用空气加压氧舱安全性能鉴定规程
- 政府机关办公用品配送方案
- GB/T 3287-2024可锻铸铁管路连接件
- SL+174-2014水利水电工程混凝土防渗墙施工技术规范
- DZ/T 0430-2023 固体矿产资源储量核实报告编写规范(正式版)
- 历年中职高考《畜禽营养与饲料》考试真题题库(含答案)
评论
0/150
提交评论