一个给新手进阶的IAT加密壳

前言

这篇文章中介绍了IAT加壳与解壳的全过程,并用Ollydbg进行逆向分析,说明这个壳的鸡肋的之处,最后给出了核心源代码。

必备基础

必须很熟悉PE结构,特别是导入表的双桥结构。

IAT(Import Address Table),导入地址表。

节就是区段,区段就是节,但节和节表不是同一个概念。

RVA 相对(基址ImageBase)虚拟地址。

VA 虚拟地址。

实验环境:WIN10 、VS2017、DIE(Detect it Easy)、Ollydbg

PS:这里还不需要了解混淆、花指令、反调试等技术,掌握了上面提到的基础,新手就可以放心阅读了。

IAT加解壳的主要步骤

一、加壳步骤

1、打开源程序

2、加载 Packer.dll

3、AES加密所有区段

4、清除目录表

5、添加新区段

6、修复重定位表

7、保存源程序的OEP

8、将 packer.dll 的 .text 段 拷贝到 .NewSec 段里

9、修改OEP为 Packer.dll函数入口点在.NewSec 段中的RVA

10、去掉随机基址

11、保存被加壳的程序

加壳完成。

示意图如下(从左到右是大致的加壳过程):图片仅供参考。

二、解壳步骤

解壳的步骤都封装在Packer.dll中,我们来解剖一下它。

1、动态获取函数的API地址

2、AES解密所有区段

3、恢复目录表

4、修复IAT表

5、密码验证对话框弹出

6、加密IAT表

7、跳转到原始OEP

解壳完成。

示意图如下(从左到右是大致的解壳过程):图片仅供参考。

IAT加解壳之3问3知

一、加壳之3问3知

1、为什么要加密所有区段 & 清楚目录项呢?

那当然是为了防止别人分析PE文件,达到保护PE文件的目的。

2、为什么要添加新节?添加新节后,为何要修复重定位表?

如果PE文件不增加新节,也可以在其他空白区添加代码。但是这样会有两个不妙的情况:

(1)空白区根本放不下你的代码,

(2)即使空白区能放得下你的代码,可能空白区的节属性不能执行,修改属性,可能会导致程序执行出错。

明白了新增节的作用,也就知道为啥我们要把Packer.dll的 .text段拷贝到 .NewSec段了:

(1) .NewSec段是我们自己添加的,大小自己定。

(2)放在新节里,目的是让Packer.dll被执行起来,直接加载到主进程空间是不会运行的,需要获取主进程的控制权。在 .NewSec段 我们可以让节"RWE(可读、可写、可执行)",这样才能让shellcode执行起来。

3、为什么要去掉随机基址?

观察这段嵌入汇编:

//跳转到原始OEP
__asm
{
mov eax, g_conf.srcOep; //跳转到源程序的OEP
add eax,0x400000 //srcOep将RVA-->VA ,加上基址0x400000,所以选择的源程序需要选择0x400000,否则会水土不服
jmp eax
}

在Packer.dll的入口函数Start()中,我采用固定基址0x400000的方法来计算src OEP的RV,也就是这段shellcode利用成功的前提要确定一个明确的跳转地址。无论是JMP ESP 等通用跳板指令还是Ret2Libc 使用的各指令,我们都要先确定这条指令的入口点。所谓惹不起躲得起,微软的ASLR(Address Space Layout Randomization)技术就是通过加载程序的时候不再使用固定的基址加载,从而干扰shellcode 定位的一种保护机制。

——引自《0day漏洞.软件漏洞分析技术(第二版)》

二、解壳之3问3知

1、为什么要动态获取函数的API地址?

通常我们使用windwos API都是直接获取IAT表中的函数地址,而这里的情况比较特殊:后面需要对IAT表进行加密,加了密后就不能从IAT表里获取函数地址。

附上一张IAT的表回顾一下:

那么有啥办法可以获取到函数地址呢?

常用的办法:1、LoadLibrary(),然后 GetMoudleName() 2、动态加载

我这里用的是第2种办法,为啥呢?

对于方法1: 加载进来后,调用的方法是 [0x12345678] 的形式,这是全局变量的调用方式,当需要重定位的时候,访问这个地址会出错。

对于方法2:动态的方式获取API的地址,兼容性好。获取的方式有3种:(1)利用PEB结构来查找 (2)利用堆栈暴力搜索 (3)使用SEH的链表来查找。

这里利用PEB结构来查找API的方式,接下来是动态获取API的代码,代码中有详细的解释:

_asm
{
pushad;
; //获取kernel32.dll的加载基址;
;// 1. 找到PEB的首地址;
mov eax, fs:[0x30]; //fs偏移0x30处为peb的首地址, fs为段寄存器
;// 2. 得到PEB_LDR_DATA的值;
mov eax, [eax + 0ch]; //在PEB偏移的0x0c处是指向PEB_LDR_DATA结构的指针
mov eax, [eax + 0ch]; //eax = > PEB.Ldr的值;
; //3. 得到_PEB_LDR_DATA.InLoadOrderMoudleList.Flink的值, 实际得到的就是主模块节点的首地址;
mov eax, [eax]; //eax = > _PEB_LDR_DATA.InLoadOrderMoudleList.Flink(NTDLL);
; //4. 再获取下一个;
mov eax, [eax]; _LDR_DATA_TABLE_ENTRY.InLoadOrderMoudleList.Flink(kernel32), ;
mov eax, [eax + 018h]; _LDR_DATA_TABLE_ENTRY.DllBase;
mov hKernel32, eax;;
; //遍历导出表;
;// 1.依次获取:dos头、 nt头、 扩展头、 数据目录表;
mov ebx, [eax + 03ch]; //偏移到NT头;
add ebx, eax; // NT头的首地址;
add ebx, 078h; //引出表偏移
; //2. 得到导出表的RVA;
mov ebx, [ebx];
add ebx, eax; //ebx = 导出表首地址(VA);
; //3. 遍历名称表找到GetProcAddress;
; //3.1 找到名称表的首地址;
lea ecx, [ebx + 020h]; //ebx=函数名地址,AddressOfName
mov ecx, [ecx]; // ecx =名称表的首地址(RVA);
add ecx, eax; // ecx =名称表的首地址(VA);
xor edx, edx; // 作为index来使用.
; //3.2 遍历名称表;
_WHILE:;
mov esi, [ecx + edx * 4]; //esi= 名称的RVA;
lea esi, [esi + eax];// esi =名称首地址;
cmp dword ptr[esi], 050746547h; 47657450 726F6341 64647265 7373; //dword:  'PteG' 、'rocA' 、'ddre' 、'ss' =>GetProcAddress,如果是GetProcAddress,表示在AddressOfName中找到了
jne _LOOP;
cmp dword ptr[esi + 4], 041636f72h;
jne _LOOP;
cmp dword ptr[esi + 8], 065726464h;
jne _LOOP;
cmp word  ptr[esi + 0ch], 07373h;
jne _LOOP;
; //找到GetProcAddress后;
mov edi, [ebx + 024h]; // edi = 函数序号(RVA);
add edi, eax; 
mov di, [edi + edx * 2]; //ecx=计算出序号值,序号表是2字节的元素, 因此是 * 2;
and edi, 0FFFFh; //edi=GetProcAddress在地址表中的下标;
; //得到地址表首地址;
mov edx, [ebx + 01ch]; //edx = 地址表的RVA;
add edx, eax; //edx = 地址表的VA;
mov edi, [edx + edi * 4]; //edi = GetProcAddress的RVA;
add edi, eax; ; //edx =  GetProcAddress的VA;
mov MyGetProcAddress, edi;
jmp _ENDWHILE;
_LOOP:;
inc edx; // ++index;
jmp _WHILE; //跳转
_ENDWHILE:;
popad; //平衡堆栈
 }

2、为什么要选择加密IAT表?

个人从攻防的角度来思考,原因有2:

(1)对IAT表的函数地址加密后,API就不能一下子看得出来了(比如说Ollydbg就解析不出来dll名),增大逆向分析PE的难度。

(2)AT表PE程序动态执行依赖的dll,加了密之后,恶意代码也就不能用我们的IAT表来使坏了,这也是对程序的一种保护。

3、如何对IAT进行加解密?

IAT加解密原理如下:

(1)遍历导入表获取IAT表里的每个函数地址

(2)取出IAT的函数地址,该函数地址异或 0x12345678,得到加密后的地址

(3)申请一段内存,存放解密后的地址,然后调用该地址的代码。

(4)把申请的内存地址放入IAT表对应表项中。

如此这般,IAT就被加密了。

我们再来仔细推敲一下步骤(3),NB的你可能早就发现:解密后的地址放在内存中,不做任何保护,恢复IAT表依然无障碍!!!

别着急,我们来看看这里的解密怎么处理?

(1)写一段具有迷惑性的代码,干扰逆向分析者对解密后地址的定位,也就是传说中的混淆+花指令操作。

(2)动态解密:在目标程序运行起来之后,动态地对代码段进行解密。

先运行一段代码、解密一部分的代码,然后再运行解密后的代码,循环直到解密完成。这种方式给逆向带来的挑战是:盯着运行着的代码及附近代码,同时又能兼顾隔得很远的加密状态的代码。

本人新手还是太菜了,现在只能理解混淆+花指令操作,代码如下:

_asm
{
push eax;
mov eax, dwFunAddr; //未加密的函数地址
xor eax, 0x12345678; //eax = dwFunAddr 异或 0x15151515
mov dwEncryptFunAddr, eax; //dwEncryptFunAddr=eax
pop eax;
}
// 3.构造一段花指令shellcode,用来解密函数地址
BYTE OpCode[] = {
0xE8, 0x01, 0x00, 0x00, //call
0x00, 0xE9, 0x58, 0xEB, //jmp 
0x01, 0xE8, 0xB8, 0x85, //MOV EAX,
0xEE, 0xCB, 0x60, 0xEB, //JMP
0x01, 0x15, 0x35, 0x12,//ADC EAX,
0x34, 0x56, 0x78, 0xEB, //ADC EAX,
0x01, 0xFF, 0x50, 0xEB, //JMP SHORT 1F
0x02, 0xFF, 0x15, 0xC3  //CALL
  };
//把函数地址放到解密的OpCode里
OpCode[11] = dwEncryptFunAddr;    // 0x85  假如:dwEncryptFunAddr = 0x12345678
OpCode[12] = dwEncryptFunAddr >> 0x08;// 0xEE  十六进制右移8位刚好截掉低位的2位  0x00123456
OpCode[13] = dwEncryptFunAddr >> 0x10;// 0xCB  十六进制右移16位刚好截掉低位的4位 0x00001234
OpCode[14] = dwEncryptFunAddr >> 0x18;// 0x60  十六进制右移16位刚好截掉低位的4位 0x00001234

Ollydbg逆向分析IAT加密壳

我们先用工具(DiE、importREC)来尝试一下侦壳、脱壳,然后再用Ollydbg分析一下。

未知壳信息

导入表修复失败

从壳的入口点F7跳进去,进入壳的Start()入口函数

真正的入口点,由于这个壳在入口处没加反调试、花指令保护,所以原始程序的OEP一眼就看得到

F4 跳到密码验证弹框处

输入密码123 验证,断下;再F7进入IAT加密

F7跳到真正的AT函数地址加密的地方,看见一堆花指令

解密密钥藏在花指令的这里

这是shellcode解密函数地址的地方

观察加密前和加密后eax的值:

看完了分析,是不是觉得没加保护的壳很鸡肋呢?只要nop填充密码验证弹框和IAT加密,轻而易举就绕过了壳到达真正的程序入口。

在这个基础上,我们该如何去隐藏我们这个入口呢?带着这个问题,和我一样的新手小菜就可以进一步进阶,做出更安全的壳了。

最后我还想说,其实加壳与解壳拼的就是谁对PE更了解,所以掌握基础还是至关重要的。

核心代码

一、加壳

#include <windows.h>
#include <stdio.h>
#include "PeFileOperator.h"
#include <stdio.h>
int main()
{
PeFileOperator myPE;//PE文件操作类对象
char path[MAX_PATH] = "src.exe";
//1、打开被加壳程序
int nTargetSize = 0;
char* pTargetBuff = myPE.GetFileData(path, &nTargetSize);
//2、加载Packer.dll
StubInfo packer = { 0 };
myPE.LoadStub(&packer); ///这里对IAT表已经加了密,具体查看packer的 Start() 函数
//3、加密所有区段
myPE.Encrypt(pTargetBuff, packer);
//4、清除目录表
myPE.ClearDataDir(pTargetBuff, packer);
//添加新节
char cNewSectionName[] = {".NewSec"};//新节表名
myPE.AddSection(pTargetBuff, nTargetSize, cNewSectionName,
myPE.GetSection(packer.dllbase,".text")->Misc.VirtualSize);
//修复重定位
myPE.FixStubRelocation((DWORD)packer.dllbase,
myPE.GetSection(packer.dllbase,".text")->VirtualAddress,
myPE.GetOptionHeader(pTargetBuff)->ImageBase,
myPE.GetSection(pTargetBuff, cNewSectionName)->VirtualAddress);
//保存目标文件的OEP到packer的全局变量中
//如果不知道为什么移步到这里看一下手动方式注入shellcode,修改OEP  https://www.cnblogs.com/Erma/p/12593860.html 
packer.pStubConf->srcOep = myPE.GetOptionHeader(pTargetBuff)->AddressOfEntryPoint;
//将packer.dll的代码段复制到新加的NewSec段中(注意:packer.dll也是个PE文件,主进程加载packer.dll时,作为一个模块附加在主程序的4GB地址空间)
memcpy(myPE.GetSection(pTargetBuff, cNewSectionName)->PointerToRawData+pTargetBuff,
myPE.GetSection(packer.dllbase,".text")->VirtualAddress+packer.dllbase,
myPE.GetSection(packer.dllbase,".text")->Misc.VirtualSize);
//修改OEP ( OEP = Start(RV)-dll加载基址)-段首RVA+新区段的段首RVA ) ,注意:packer.dll 加载进来是不会自己执行的,一定要获得控制权才可以
//Start(RV)-dll加载基址)-段首RVA: Start()在.text内的偏移
/因为获取到的 start 函数的地址是在dll中的地址,现在这个区段被拷贝到了
    /被加壳程序中,所以需要重新计算 start 的 RVA 并设置为 OEP
myPE.GetOptionHeader(pTargetBuff)->AddressOfEntryPoint =
packer.pfnStart-(DWORD)packer.dllbase
-myPE.GetSection(packer.dllbase,".text")->VirtualAddress
+myPE.GetSection(pTargetBuff, cNewSectionName)->VirtualAddress;
//去掉随机基址:利于shellcode的定位 具体解释见《0day安全:软件漏洞分析技术(第二版)》
myPE.GetOptionHeader(pTargetBuff)->DllCharacteristics &= (~0x40);
//保存被加壳的程序
myPE.SavePEFile(pTargetBuff,nTargetSize,"AES.Packed.exe");
return 0;
}

二、解壳

//************************************************************
// 函数名称: Start
// 函数说明: dll的OEP
// 参 数: void
// 返 回 值: void
//************************************************************
extern "C" __declspec(dllexport) __declspec(naked)
void Start()
{
//获取函数的API地址
GetApis();
//解密所有区段
Decrypt();
//恢复数据目录表
RecoverDataDir();
//修复IAT
FixImportTable();
//密码验证对话框弹出
AlertPasswdBox();
//加密IAT
EncryptIAT();
//跳转到原始OEP

网络安全学习资源分享:

给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

因篇幅有限,仅展示部分资料,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,需要点击下方链接即可前往获取

CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享(安全链接,放心点击)

同时每个成长路线对应的板块都有配套的视频提供: 

大厂面试题

视频配套资料&国内外网安书籍、文档

当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料

所有资料共282G,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,可以扫描下方二维码或链接免费领取~ 

 读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享(安全链接,放心点击)

特别声明:

此教程为纯技术分享!本教程的目的决不是为那些怀有不良动机的人提供及技术支持!也不承担因为技术被滥用所产生的连带责任!本教程的目的在于最大限度地唤醒大家对网络安全的重视,并采取相应的安全措施,从而减少由网络安全而带来的经济损失。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Shiro+Jwt+Redis

如何整合ShiroJwtRedis&#xff0c;以及为什么要这么做 我个人认为 ①为什么用shiro&#xff1a;“ShiroJwtRedis”模式和“单纯的shiro”模式相比&#xff0c;主要用的是shiro里面的登录认证和权限控制功能 ②为什么用jwt&#xff1a;“ShiroJwt”模式和“ShiroCookie”模式相…

怎么搭建微信留言板功能

在信息爆炸的时代&#xff0c;微信已经成为了我们日常生活中不可或缺的一部分。它不仅仅是一个简单的聊天工具&#xff0c;更是一个充满无限可能的营销平台。今天&#xff0c;我要向大家介绍的是如何在你的微信平台上搭建一个独具特色的留言板功能&#xff0c;让用户能够自由发…

【Flutter】Dialog组件PageView组件

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Flutter学习 &#x1f320; 首发时间&#xff1a;2024年5月27日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; 目…

需求跟踪矩阵是什么?怎么创建?一文详解

一、什么是需求跟踪矩阵 对项目经理或产品经理来说&#xff0c;需求清单肯定不陌生&#xff0c;那什么是需求跟踪矩阵呢&#xff1f; 需求跟踪矩阵&#xff08;Requirement Track Matrix&#xff0c;简称RTM &#xff09;&#xff0c;是把产品需求从其来源连接到能满足需求的…

Spring中@Component注解

Component注解 在Spring框架中&#xff0c;Component是一个通用的注解&#xff0c;用于标识一个类作为Spring容器管理的组件。当Spring扫描到被Component注解的类时&#xff0c;会自动创建一个该类的实例并将其纳入Spring容器中管理。 使用方式 1、基本用法&#xff1a; Co…

[AI OpenAI] OpenAI 安全更新

AI 首尔峰会中分享我们的实践 我们自豪地构建并发布了在能力和安全性方面都处于行业领先地位的模型。 超过一亿用户和数百万开发者依赖于我们安全团队的工作。我们将安全视为我们必须在多个时间范围内投资并取得成功的事项&#xff0c;从使今天的模型与我们未来预期的更具能力…

【Spring Cloud】远程调用

目录 Spring Cloud Netflix Feign简介前言Feign是什么OpenFeign组件和Spring Cloud OpenFeignOpenFeign组件Spring Cloud OpenFeign OpenFeign-微服务接口调用需求说明1. 启动Eureka Server服务2.创建两个项目&#xff0c;将其注册到Eureka Server3.在服务提供者中添加业务处理…

如何处理逻辑设计中的时钟域

1.什么是时钟域 2.PLL对时钟域管理 不管是否需要变频变相&#xff0c;在FPGA内部将外部输入时钟从专用时钟引脚扇入后先做PLL处理。如何调用pll&#xff0c;见另一篇文章。 约束输入时钟 creat_clock -period 10 -waveform {0 5} [get_ports {sys_clk}] 3.单bit信号跨时钟…

【Linux进程篇】父子进程fork函数||进程生死轮回状态||僵尸进程与孤儿进程

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 前言&#xff1a;上篇文章中我们认识了进程&#xff0c;可执行程序在内存中加载运行被称作进程&#xff0c;而操作系统是通过给每一个可执行程序创建一个PCB来管理进程的。并且学习了一些查看进程的指令&#xff0c;认识…

Flask 蓝图路由的模块化开发

基于 Flask 蓝图路由的模块化开发 1. 编程目标 为了提高Flask应用的可维护性和可扩展性&#xff0c;我们通过使用Flask的蓝图(Blueprint)功能&#xff0c;可以将不同的功能模块拆分到独立的文件中&#xff0c;方便后续的开发和维护。 2. 项目结构 项目结构树如下&#xff1…

助力企业标准化搭建--图框模板的创建

古有秦皇书同文、车同轨&#xff0c;今各行各业都有国际标准、国家标准&#xff0c;其目的就是为了标准化、统一化&#xff0c;由此可见标准化的重要性&#xff1b;一个企业若是想规范员工的操作&#xff0c;推行标准化也很重要&#xff1b;因此对于需要绘制电气图纸的行业来说…

从0开始学统计-秩和检验

1.什么是秩和检验&#xff1f; 秩和检验&#xff0c;也称为Wilcoxon 秩和检验&#xff0c;是一种非参数统计检验方法&#xff0c;用于比较两个独立样本的中位数是否有显著差异。它不要求数据满足正态分布假设&#xff0c;因此适用于小样本或者数据不满足正态分布假设的情况。 …

2024年怎么下载学浪app视频

想要在2024年紧跟潮流&#xff0c;成为一名优秀的学浪用户吗&#xff1f;今天就让我们一起探索如何下载学浪app视频吧&#xff01; 学浪视频下载工具打包 学浪下载工具打包链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1234 --来自百度网盘超级会员V10的分享…

性能怪兽!香橙派 Kunpeng Pro 开发板深度测评,带你解锁无限可能

性能怪兽&#xff01;香橙派 Kunpeng Pro 开发板深度测评&#xff0c;带你解锁无限可能 文章目录 性能怪兽&#xff01;香橙派 Kunpeng Pro 开发板深度测评&#xff0c;带你解锁无限可能一、背景二、香橙派 Kunpeng Pro 硬件规格概述三、使用准备与系统安装1️⃣、系统安装步骤…

字节面试:百亿级数据存储,怎么设计?只是分库分表吗?

尼恩&#xff1a;百亿级数据存储架构起源 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;经常性的指导小伙伴们改造简历。 经过尼恩的改造之后&#xff0c;很多小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试机会&#xff0c…

cuda 11.6 pytorch安装

在安装之前&#xff0c;需要先配置GPU环境&#xff08;安装CUDA和CudaNN) 命令行输入nvidia-smi&#xff0c;查看驱动信息 nvidia-smi 安装相应的CUDA 和CUDANN 验证&#xff1a;输入nvcc --version 或者nvcc -V 进行检查 nvcc --version nvcc -V 在anaconda里创建环境 con…

彭永东所交“答卷”道尽万般无奈,贝壳找房营收、利润双双锐减

就今年第一季度业绩披露后两日的股价变动来看&#xff0c;贝壳找房&#xff08;下称“贝壳”&#xff09;似乎并未在港股和美股市场取得预期的效果。 港股市场&#xff0c;截至5月24日收盘&#xff0c;贝壳-W&#xff08;HK:02423&#xff09;报收43.9港元/股&#xff0c;当日跌…

海外网红营销新趋势:“快闪式”营销如何迅速提升品牌曝光度

在当今数字化时代&#xff0c;海外网红营销已成为品牌迅速触达全球消费者、提升品牌曝光度和刺激销售的重要手段。其中&#xff0c;“快闪式”营销以其独特的时效性、创意性和互动性&#xff0c;成为品牌与海外网红合作的新趋势。本文Nox聚星将和大家探讨如何利用海外网红的影响…

梭住绿色,植梦WILL来,容声冰箱“节能森林计划”再启航

近日&#xff0c;容声冰箱再度开启了“节能森林计划”绿色公益之旅。 据「TMT星球」了解&#xff0c;此次活动深入到阿拉善荒漠化地带&#xff0c;通过实地考察和亲身体验&#xff0c;见证容声了“节能森林计划”项目的持续落地和实施效果。 2022年&#xff0c;容声冰箱启动了…

5个好用的AI写论文网站推荐

目录 1.AIQuora论文写作 2.passyyds 答辩PPT 3.AIPassgo论文降AIGC 4.文状元 5.passyyds论文写作 毕业论文是每个毕业生的痛&#xff0c;不管你是本科还是硕士要想顺利毕业你就不得不面对论文。然而&#xff0c;面对论文写作时常常感到无从下手&#xff1a;有时缺乏灵感&a…