




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
主题:【游戏修改器制作】对不起,我想这是不可能的,因为VB是一个如此简单的编程语音。如果有人这么告诉你,别去理他。我可以肯定告诉你,对于制作修改器这种简单的程序,VB完全可以胜任。然而,有个问题必须首先考虑:使用VB编写的修改器需要VB的运行库才能运行。如果考虑到有些使用者(实际上可能是大部分使用者)没有运行库,那么在最后制作的ZIP压缩文件中就必须包含这些庞大的文件。在下面的教程里我将制作一个修改器,如果为它再制作一个安装程序,那么整个修改器的体积将超过1MB。其中包括一个很好的安装和反安装程序,但大部分还是VB40032.DLL这个文件。除了以上这点,使用VB制作修改器是非常简单的。一旦制作了多次后,你会发现能很快地制作出一个修改器。而且使用VB制作的修改器能够毫无困难地解决游戏运行时的动态内存分配问题,因此即使是最新的游戏,也可以使用VB制作修改器。在本教程中将不涉及动态内存分配,因为虽然简单,但仍然属于一个高级的选项。一些背景知识不象C语音,VB不会自动包括普通的API函数的声明,因此我们必须把他们加入我们的项目文件。在几乎所有的修改器中会使用到6个主要的函数,讨论如下:1. FindWindow(ClassName, WindowTitle) - FindWindow 返回符合指定的类名( ClassName )和窗口名( WindowTitle )的窗口句柄。对我们来说,可以让 ClassName 为空( Null ),只给出游戏的 WindowTitle。函数应该这样声明: Declare Function FindWindow Lib user32 Alias FindWindowA (ByVal lpClassName As String, ByVal lpWindowName As String) As Long2. GetWindowThreadProcessId(WindowHandle, ProcessId) - 在这里我们把 FindWindow 函数中得到的句柄作为参数,来获得进程标识符(ProcessId )。声明如下: Declare Function GetWindowThreadProcessId Lib user32 (ByVal hwnd As Long, lpdwProcessId As Long) As Long3. OpenProcess(DesiredAccess, Inherit, ProcessId) - 这个函数将返回一个我们目标进程的句柄,可以用来对目标进行读写操作。 DesiredAccess 参数的值决定了句柄对进程的存取权利,对我们来说,要使用 PROCESS_ALL_ACCESS (完全存取权限)。Inherit 应该总是 False。 ProcessId 是从 GetWindowThreadProcessId 函数中取得的。 Declare Function OpenProcess Lib kernel32 (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long4. CloseHandle(ProcessHandle) - 每一个打开的句柄必须呼叫这个函数来关闭。 Declare Function CloseHandle Lib kernel32 (ByVal hObject As Long) As Long5. WriteProcessMemory(ProcessHandle, Address, Value, SizeofValue, BytesWritten) - 把指定的值 Value 写入由 Address 指定的目标地址。 Declare Function WriteProcessMemory Lib kernel32 (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long6. ReadProcessMemory(ProcessHandle, Address, Value, SizeofValue, BytesWritten) - 把 Address 指定的目标地址的值存入 Value 位置的变量中。 Declare Function WriteProcessMemory Lib kernel32 (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long这些函数一环扣一环,缺一不可。更详细的内容可以参考VB的帮助文件。一个简单的修改器范例如何使上面介绍的这些函数一起工作,制作出我们需要的修改器呢?下面是一个为Windows的计算器程序制作修改器的例子。这个修改器将读出计算器窗口中显示的数值,并在点击一个按钮后在计算器窗口中显示我们的名字。首先我们需要找到计算器显示窗口中显示值的地址。本教程不是关于如何进行内存搜索,因而我将只作简单的说明: 在计算器窗口中输入123456 使用你喜欢的任何一种内存地址搜索程序寻找字串123456 使用另一个值重复上面的过程直到只返回1个地址那是制作我们的修改器需要的唯一一个地址。在我的计算器程序里这个地址是40B181 hex, 4239745 dec。用你找到的地址替代在下面的代码里使用的这个地址。现在让我们开始设计修改器的界面: 在VB中新建一个项目,加入一个文本框( Textbox )、一个按钮和一个计时器( timer )。文本框用来显示从计算器窗口取得的字串,按钮用来把我们的名字传到计算器窗口 把表单( form )的标题( Caption )属性设为 Calculator Trainer 把文本框改名为 txtDisplay 并清除 Text 属性 把计时器改名为 ReadTimer 并把间隔( interval )设为500 把按钮的标题改为 Display Name,按钮的名字改为 btnPasteName在这个修改器中我们将使用所有6个函数,ReadProcessMemory、WriteProcessMemory、OpenProcess、GetWindowThreadProcessId、FindWindow 和 CloseHandle。在项目中插入一个新的模块,增加下列代码。(下面的一些行自动换行了,在你的模块中每一句必须在一行里,或使用延长符_)Declare Function FindWindow Lib user32 Alias FindWindowA (ByVal lpClassName As String, ByVal lpWindowName As String) As LongDeclare Function GetWindowThreadProcessId Lib user32 (ByVal hwnd As Long, lpdwProcessId As Long) As LongDeclare Function OpenProcess Lib kernel32 (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As LongDeclare Function WriteProcessMemory Lib kernel32 (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As LongDeclare Function ReadProcessMemory Lib kernel32 (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As LongDeclare Function CloseHandle Lib kernel32 (ByVal hObject As Long) As Long下面我们要开始写在计时器窗口中显示我们名字的代码了。首先我们使用 FindWindow 函数取得目标窗口的句柄。把这个返回值保存在一个变量中,并检查它的值是否出错来确保计时器程序正在运行。(FindWindow函数出错时返回0)Dim hwnd As Longhwnd = FindWindow(vbNullString, Calculator"If (hwnd = 0) ThenMsgBox Window not found!Exit SubEnd If注意在这里我们传递了一个 Null 值给 FindWindow 函数,而不是 ClassName。因此任何名为 Calculator 的窗口都符合条件。如果知道计算器程序窗口的 ClassName,你可以传给它,但这不是必须的。现在使用得到的窗口句柄来取得进程标识符( ProcessId )。注意 pid 是作为参数传递给函数的,而不是被赋以函数返回值。Dim pid As LongGetWindowThreadProcessId hwnd, pid再利用变量pid得到计算器程序的进程句柄。再次检查函数的返回值,如果是非法数据则退出程序。Dim pHandle As LongpHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)If (pHandle = 0) ThenMsgBox Couldn't get a process handle!Exit SubEnd If在我们的修改器中 WriteProcessMemory 函数是最重要的部分,而且非常容易出错。不妨让我们再仔细讨论一下它的参数。WriteProcessMemory (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As)hProcess 是目标进程的句柄,从上面的 OpenProcess 函数中取得的。lpBaseAddress 是在计算器程序的虚拟内存中将要被修改的地址,也就是使用内存搜索程序找到的那个地址。(在我的程序里是&H40B181)lpBuffer 是将要写如上述地址的数据,可以是一个数值、数组、字符串或其他任何数据类型。nSize 是希望写入 lpBaseAddress 的字节数。这个位置应该与你的数据类型相符。如果写入的是一个长整数( long ),这里应该是4。如果写入的是一个字符串,那么这里应该是字符串的长度。lpNumberOfBytesWritten 是函数执行返回后,写入目标地址的实际字节数。它能被用来确认函数实际的执行情况。把我们的数据放到函数中,得到 WriteProcessMemory pHandle, &H40B181, Beans, 5, 0&。我把0传递到 lpNumberOfBytesWritten 位置是因为不需要检查两次实际写入的字节数。最后通过传递进程句柄给 CloseHandle() 函数来关闭由 OpenProcess 打开的句柄。CloseHandle hProcess现在将所有的代码输入我们的编辑器中。双击按钮,显示它的代码编辑窗口。代码应该加到名为 btnPasteName 的 Click 事件中。(不必输入注释)Private Sub btnPasteName_Click()' 声明一些需要的变量Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄Dim pid As Long ' 储存进程标识符( Process Id )Dim pHandle As Long ' 储存进程句柄' 首先取得目标窗口的句柄hwnd = FindWindow(vbNullString, Calculator"If (hwnd = 0) ThenMsgBox Window not found!Exit SubEnd If' 取得进程标识符GetWindowThreadProcessId hwnd, pid' 使用进程标识符取得进程句柄pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)If (pHandle = 0) ThenMsgBox Couldn't get a process handle!Exit SubEnd If' 在内存地址中写入名字WriteProcessMemory pHandle, &H40B181, Beans, 5, 0' 关闭进程句柄CloseHandle hProcessEnd Sub完毕。现在单击按钮将使计算器窗口文本变为我们键如的名字。(可能需要最小化计算器程序,再还原,以便程序更新显示)下面将给我们的修改器增加一个新功能。我们将检测计算器程序的窗口显示数据,并在修改器中显示。双击计时器,显示它的代码编辑窗口,然后输入以下代码: Private Sub ReadTimer_Timer()' 声明变量Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄Dim pid As Long ' 储存进程标识符Dim pHandle As Long ' 储存进程句柄Dim str As String * 20 ' 存储显示文本' 取得目标窗口的句柄hwnd = FindWindow(vbNullString, Calculator"If (hwnd = 0) Then Exit Sub' 取得进程标识符GetWindowThreadProcessId hwnd, pid' 取得进程句柄pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)If (pHandle = 0) Then Exit Sub' 读取内存数据ReadProcessMemory pHandle, &H40B181, str, 20, 0' 在文本框显示txtDisplay = str' 关闭进程句柄CloseHandle hProcessEnd Sub在这里出现的新东西是 ReadProcessMemory 函数。从 &H40B181 地址中读出的数据被存入变量 str 中,然后显示在名为 txtDisplay 的文本框中。本教程中所讲的是非常简单的东西,主要是想起抛砖引玉的目的。最重要的是不断学习,不断实践,了解其他的API并在修改器中使用。练习越多,就会觉得越容易。 此文章由游软创作室提供掀开高级游戏黑客的面纱,教你打造游戏修改器 工具:SoftICE、金山游侠2002、VC+7.0、PE查看器、SPY+ 测试平台:Window2000 Professional SP2 大家好!我先给大家拜个晚年,时间过真快工,一年又过去了,我也和大家分开一年了,真是感慨万分呀,不知道大还记得不记得YY了,不过我是不会忘记大家的. 这一年真是太忙了,根本就没有时间再像从前那样在深夜里一边听着音乐一边写文章了,今天就着假期,再给大家充充电:D 今天YY给大家带来些什么呢?呵呵,看题目就知道了,看起来很酷吧,“高级游戏黑客”,什么?你说你就游戏黑客?看好了,是“高级”的!什么是高级的?等你看完这篇文章就知道了:D 首先我介绍一下将会用到的工具: 1、 SoftICE(不用多说了吧,我想你应该会用) 2、 金山游侠2002(这个你也应该会用) 3、 VC+7.0(不要求你一定会用,但至少应该会一种编程工具) 4、 PE查看器(你可以随意找一个,没有也没关系,我会教你用SoftICE查看) 5、 SPY+(VC里的一个查看程序信息的工具,你可以和别的,比如Delphi和C+Builder的WinSight32) 然后就是你应该会的知识: 1、 汇编基础 2、 一些编程基础,至少应该看懂我介绍的几个API函数 3、 PE文件结构的基础,不会也没关系,我会解释给你 以上几点你都具备了的话我们就可以开始了。 我来介绍一下我要教给你的东西。想必大家都玩过PC游吧,那么也一定用过一些专用的游戏修改器吧,比如暗黑,红警,大富翁这些经典的游戏都有它们专用的修改器,注意,我说的不是FPE之类的通用修改工具。 你试没试过用金山游侠修改红警二的金钱?如果有的话你应该知道每玩一次就要改一次,因为这个游戏是动态分配内存的,每次重新开始都会改变。所以你会选择到网上去下载一个专用的修改器,那么你有没有想过自己做一上呢?想过?那你为什么不做?什么不会?那就好办了,看了这篇教程你就会了:D费话少说,我来讲一下原理。 有一些经常修改游的朋友一定会知道,不论游戏中“物品”的内存地址是否是动态的,物品与物品之间相隔的距离都是不变的,我拿“楚留香新传”为例,我先用金山游侠查找内力值的内存地址,找到的结果是:79F695C,再查找物品“金创药”的地址是:328D1DC,现在我用79F695C减去328D1DC,得到:4769780,这个数就是内力值与金创药的偏移值,没看懂?接着看呀,我还没说完呢,现在重新再运行游戏,查找内力值的地址,得到:798695C再查找金创药得到的地址是:321D1DC,两个值的内存地址都改变了,但是用你内力值的地址减去金创药的地址得到的结果是什么?没错,还是4769780,也就是说,无论这两个值的内存地址变成多少,它们之间的距离是永远不变的,不光是这个游戏,一般的游戏都是,至少我没见过不是的:D 上面讲的东西总结出一个结论,那就是我们只要得到这两个地址中的任何一个,就可以得到另外一个,只要你知道它们之间的偏移量是多少。 我们第一步要做的就是得到这个地址,但是内存中的地址是动态改变的,得到也没有用,这里我就教你把它变成静态的,叫它永远都不变!我继续拿“楚留香新传”为例,如果你有这个游的话就跟我一起做,没有的也没关系,只要看懂这几个步骤就行了。开工! 首先进入游戏,查找内值的地址,得到的是:798695C(不知道为什么这上游并不是每次重起都改变内存地址),按Ctrl+D打开SoftICE,下命令:BPM 798695C W(写这个地址时则中断),回到游戏中,打开人物属性面板,游戏中断了,在SofitICE中你会看到这条指令: 0047EB17MOVEAX EDX+000003F4 下命令:D EDX+3F4将看到内力值 0047EB1DPUSHEAX 从上面可看出0047EB17处的指令是将内力值的指针送到EAX寄存器中,这是一个典型的寻址方式,设想一下,我们是到了EDX中的基址,那么无论什么时候只要用EDX+3F4就可以轻松的得到内力值的地址,因为000003F4是一个常量,它是不会改变的,改变的只是EDX中的地址,所以只要有办法得到EDX中的值就什么都好办了,你明白了没有?如果还是不懂,那么请再看一遍。现在要做的就是如何得到这个值,下面我教给你如何做: 我的办法就是设计一段代码,把EDX中的值存放到一个地址中,然后运行这段代码,再返回游戏的原有指令继续执行,什么?补丁技术?SMC?随你怎么说啦,只要运行正常就一切OK啦:D 实际操作: 首先在程序中找一段空白处来存放我们设计的代码,很简单,只要懂得一些PE文件结构的朋友都会知道,一般在EXE文件的数据段(.data段)的结尾都会有一段缓冲区,我们可以在这段区域中写任何东西,当然你也可以用“90大法”找一段空白区,但我还是推荐你用我教给你的方法。上同我提到,如果你没有PE文件查看工具我可以教你用SoftICE查看,而且很简单,只要一个命令:MAP32 “模块名”,看一下我是怎么做的你就知道了。 Ctrl+D呼收出SoftICE,然后下命令:MAP32 CrhChs,这时你应该看到EXE各个段的信息,我们要注意的只是.data段,既然要找的是数据段的结尾,那么我们就从下一个段开始向上找,如下: .data004FB000 .rsrc00507000 .data的下一个段是.rsrc段,它是从00507000开始的,也就是说以00507000为基础向上一个字节就是数据段的结尾,我所择从00506950处开始写代码,说了这么半天那么我们的代码到底是什么样子呢?修改后的指令又是什么样的呢?别急,请看下面: 修改0047EB17后代码: 0047EB17JMP 00506950 /跳到我们的代码中去执行0047EB1CNOP/由于这条指令原来的长度是6字节,而修改后的长度是5个字节,所以用一个空指令补上 0047EB1DPUSHEAX /我们的代码: 00506950MOVDWORD PTREAX,EDX+00003F4 /恢复我们破坏的指令 00506956MOVDWORD PTR00506961,EDX /把EDX保存以00506961中去 0050695CJMP0047EB1D /返回原来的指令去执行 把上面的代码用SoftICE的A命令写入,OK! 现在我们试一下运行的效果,你现在用金山游侠搜索一下内力址的地址,什么又变了?那就地啦,它要是不变我们还用费这么大劲儿吗?记下这个地址返回到游戏中去,Ctrl+D呼出SoftICE,下命令 D *00506961+000003F4,在数据窗口看到什么了?呵呵,没错,看到了你刚才记住的那个地址,里面的数值正是内力的值,试着改一下,回到游戏中,呵呵,内力值变了吧:D 讲到这里,我们的工作已经完成了%90,但别高兴的太早,后面的%10要远比前的%90花的时间长,因为我们要用编程实现这一切,因为你不能每次都像刚才那样做一次吧! 现在我来说一下编程的步骤: 首先用FindWindow函数得到窗口句柄,然后用GetWindowThreadID函数从窗口句柄得到这个进程的ID,接着用OpenProcess得到进程的读写权限,最后用WriteProcessMemory和ReadProcessMemory读写内存,然后。呵呵,你的修改器就做成啦 下面是我抄写以前写的修改器源程序片断,第一部分是动态写入刚才的代码,第二部分是读取并修改内力值,由于我没有时间整理和测试,所以不能保证没有错误,如果大家发现有遗漏的话,可以在QQ上给我留言或写信给我,代码如下: 有几点请大家注意: 1、 写机器码时要一个字节一个字节的写 2、 注意要先写入自己的代码,然后再修改游中的指令(下面的代码没有这样做,因为不影响,但是你应该注意这个问题) / /动态写入代码 /0047EB17 #define MY_CODE10xE9 #define MY_CODE20x34 #define MY_CODE30x7E #define MY_CODE40x08 #define MY_CODE50x00 #define MY_CODE60x90 /00506950 #define MY2_CODE10x8B #define MY2_CODE20x82 /这部分是要写入的机器码的常量定义 #define MY2_CODE30xF4 #define MY2_CODE40x03 #define MY2_CODE50x00 #define MY2_CODE60x00 #define MY3_CODE10x89 #define MY3_CODE20x15 #define MY3_CODE30x61 #define MY3_CODE40x69 #define MY3_CODE50x50 #define MY3_CODE60x00 #define MY4_CODE10xE9#define MY4_CODE20xBC #define MY4_CODE30x81 #define MY4_CODE40xF7 #define MY4_CODE50xFF /-/ DWORD A1 =MY_CODE1; DWORD A2 =MY_CODE2; DWORD A3 =MY_CODE3; DWORD A4 =MY_CODE4; DWORD A5 =MY_CODE5; DWORD A6 =MY_CODE6; DWORD B1 =MY2_CODE1; DWORD B2 =MY2_CODE2; DWORD B3 =MY2_CODE3;/这部分是变量的定义 DWORD B4 =MY2_CODE4; DWORD B5 =MY2_CODE5; DWORD B6 =MY2_CODE6; DWORD C1 =MY3_CODE1; DWORD C2 =MY3_CODE2; DWORD C3 =MY3_CODE3; DWORD C4 =MY3_CODE4; DWORD C5 =MY3_CODE5; DWORD C6 =MY3_CODE6; DWORD D1 =MY4_CODE1; DWORD D2 =MY4_CODE2; DWORD D3 =MY4_CODE3; DWORD D4 =MY4_CODE4; DWORD D5 =MY4_CODE5; /-/ HWND hWnd =:FindWindow(CRHClass,NULL); /得到窗口句柄 if(hWnd =FALSE) MessageBox(游戏没有运行! else GetWindowThreadProcessId(hWnd,&hProcId); / 从窗口句柄得到进程ID HANDLE nOK =OpenProcess(PROCESS_ALL_ACCESS|PROCESS_TERMINATE|PROCESS_VM_OPERATION|PROCESS_VM_READ| PROCESS_VM_WRITE,FALSE,hProcId);/打开进程并得到读与权限 if(nOK =NULL) MessageBox(打开进程时出错 else /0047EB17 WriteProcessMemory(nOK,(LPVOID)0x0047EB17,&A1,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x0047EB18,&A2,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x0047EB19,&A3,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x0047EB1A,&A4,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x0047EB1B,&A5,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x0047EB1C,&A6,1,NULL); /00506950 WriteProcessMemory(nOK,(LPVOID)0x00506950,&B1,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x00506951,&B2,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x00506952,&B3,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x00506953,&B4,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x00506954,&B5,1,NULL); WriteProcessMemory(nOK,(LPVOID)0x00506955,&B6,1,NULL); /第二句 WriteProcessMemory(nOK,(LPVOID)0x0050
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 珠宝鉴定的职场挑战试题及答案
- 深入探讨农业职业经理人考试的职业导向试题及答案
- 探讨珠宝市场动态的试题及答案
- 2024年农业职业经理人考试全书知多少试题及答案
- 2024春高中化学第1章原子结构与元素周期律第1节原子结构第1课时原子核核素练习鲁科版必修2
- Unit 7 Tall Buildings Part A(教学设计)-2023-2024学年闽教版英语五年级下册
- Unit3 (Presenting ideas+Reflection )教学设计-2024-2025学年外研版(2024)英语七年级上册
- 在家调酒的相关试题及答案
- 2024年食品安全检测方法考题及答案
- 福建事业单位考试能力提升规划与试题及答案
- 2023年北京市通州初三一模物理试卷及答案
- 歌曲《wake》中英文歌词对照
- 2024年职教高考《机械制图》考试题库
- 2024年-2025年公路养护工理论知识考试题及答案
- 2024年财经考试-内部审计考试近5年真题集锦(频考类试题)带答案
- 《人工智能技术基础》课件 第1章 人工智能简介
- 儿科题库单选题100道及答案解析
- 物业费欠缴调解协议书范文
- DB34T 3663-2020 植保无人飞机农田施药作业技术规范
- 公司安全生产教育培训制度范本
- 概览中外民间美术 课件 2024-2025学年赣美版(2024)初中美术七年级上册
评论
0/150
提交评论