运用加密技术保护Java源代码
内容:
一、为什么要加密?
二、定制类装入器
三、加密、解密
四、应用实例
五、注意事项
参考资源
Java程序的源代码很容易被别人 偷看。只要有一个反编译器,任何人都可以分析别人的代码。本文讨论如何在不修改原有程序的情况下,通过加密技术保护源代码。
一、为什么要加密?
对于传统的C或C++之类的语言来说,要在Web上保护源代码是很容易的,只要不发布它就可以。遗憾的是,Java程序的源代码很容易被别人偷 看。只要有一个反编译器,任何人都可以分析别人的代码。Java的灵活性使得源代码很容易被窃取,但与此同时,它也使通过加密保护代码变得相对容易,我们 唯一需要了解的就是Java的ClassLoader对象。当然,在加密过程中,有关JavaCryptographyExtension(JCE) 的知识也是必不可少的。
有几种技术可以“模糊”Java类文件,使得反编译器处理类文件的效果大打折扣。然而,修改反编译器使之能够处 理这些经过模糊处理的类文件并不是什么难事,所以不能简单地依赖模糊技术来保证源代码的安全。
我们可以用流行的加密工具加密应用,比如 PGP(PrettyGoodPrivacy)或GPG(GNUPrivacyGuard)。这时,最终用户在运行应用之前必须先进行解密。但解 密之后,最终用户就有了一份不加密的类文件,这和事先不进行加密没有什么差别。
Java运行时装入字节码的机制隐含地意味着可以对字节 码进行修改。JVM每次装入类文件时都需要一个称为ClassLoader的对象,这个对象负责把新的类装入正在运行的JVM。JVM给 ClassLoader一个包含了待装入类(比如java.lang.Object)名字的字符串,然后由ClassLoader负责找到类文件,装入原 始数据,并把它转换成一个Class对象。
我们可以通过定制ClassLoader,在类文件执行之前修改它。这种技术的应用非常广泛 ——在这里,它的用途是在类文件装入之时进行解密,因此可以看成是一种即时解密器。由于解密后的字节码文件永远不会保存到文件系统,所以窃密者很难得到解 密后的代码。
由于把原始字节码转换成Class对象的过程完全由系统负责,所以创建定制ClassLoader对象其实并不困难,只需 先获得原始数据,接着就可以进行包含解密在内的任何转换。
Java2在一定程度上简化了定制ClassLoader的构建。在 Java2中,loadClass的缺省实现仍旧负责处理所有必需的步骤,但为了顾及各种定制的类装入过程,它还调用一个新的findClass方法。
这为我们编写定制的ClassLoader提供了一条捷径,减少了麻烦:只需覆盖findClass,而不是覆盖loadClass。 这种方法避免了重复所有装入器必需执行的公共步骤,因为这一切由loadClass负责。
不过,本文的定制ClassLoader并不 使用这种方法。原因很简单。如果由默认的ClassLoader先寻找经过加密的类文件,它可以找到;但由于类文件已经加密,所以它不会认可这个类文件, 装入过程将失败。因此,我们必须自己实现loadClass,稍微增加了一些工作量。
二、定制类装入器
每一个运行着的JVM 已经拥有一个ClassLoader。这个默认的ClassLoader根据CLASSPATH环境变量的值,在本地文件系统中寻找合适的字节码文件。
应 用定制ClassLoader要求对这个过程有较为深入的认识。我们首先必须创建一个定制ClassLoader类的实例,然后显式地要求它装入另外一个 类。这就强制JVM把该类以及所有它所需要的类关联到定制的ClassLoader。Listing1显示了如何用定制ClassLoader装入类文 件。
【Listing1:利用定制的ClassLoader装入类文件】
//首先创建一个 ClassLoader对象
ClassLoadermyClassLoader=newmyClassLoader();
//利 用定制ClassLoader对象装入类文件
//并把它转换成Class对象
ClassmyClass=myClassLoader.loadClass("mypackage.MyClass");
//最后,创建该类的一个实例
ObjectnewInstance=myClass.newInstance();
//注意,MyClass所需要的所有其他类,都将通过
//定制的ClassLoader自动装入
如前所述,定制ClassLoader只需先获取类文件的数据,然后把字节码传递给运行时系统,由后者完成余下的任 务。
ClassLoader有几个重要的方法。创建定制的ClassLoader时,我们只需覆盖其中的一个,即loadClass, 提供获取原始类文件数据的代码。这个方法有两个参数:类的名字,以及一个表示JVM是否要求解析类名字的标记(即是否同时装入有依赖关系的类)。如果这个 标记是true,我们只需在返回JVM之前调用resolveClass。
【Listing2:ClassLoader.loadClass() 的一个简单实现】
publicClassloadClass(Stringname,booleanresolve)
throwsClassNotFoundException{
try{
//我们 要创建的Class对象
Classclasz=null;
//必需的步骤1:如果类已经 在系统缓冲之中,
//我们不必再次装入它
clasz=findLoadedClass(name);
if(clasz!=null)
returnclasz;
//下 面是定制部分
byteclassData[]=/*通过某种方法获取字节码数据*/;
if(classData!=null){
//成功读取字节码数据,现在把它转换成一个Class对象
clasz=defineClass(name,classData,0,classData.length);
}
//必需的步骤2:如果上面没有成功,
//我们尝试用默认的 ClassLoader装入它
if(clasz==null)
clasz=findSystemClass(name);
//必需的步骤3:如有必要,则装入相关的类
if(resolve&&clasz!=null)
resolveClass(clasz);
//把类返回给调用者
returnclasz;
}catch(IOExceptionie){
thrownewClassNotFoundException(ie.toString());
}catch(GeneralSecurityExceptiongse){
thrownewClassNotFoundException(gse.toString());
}
}
Listing2显示了一个简单的 loadClass实现。代码中的大部分对所有ClassLoader对象来说都一样,但有一小部分(已通过注释标记)是特有的。在处理过程 中,ClassLoader对象要用到其他几个辅助方法:
findLoadedClass:用来进行检查,以便确认被请求的类当前还不 存在。loadClass方法应该首先调用它。
defineClass:获得原始类文件字节码数据之后,调用defineClass把它转换 成一个Class对象。任何loadClass实现都必须调用这个方法。
findSystemClass:提供默认ClassLoader的 支持。如果用来寻找类的定制方法不能找到指定的类(或者有意地不用定制方法),则可以调用该方法尝试默认的装入方式。这是很有用的,特别是从普通的JAR 文件装入标准Java类时。
resolveClass:当JVM想要装入的不仅包括指定的类,而且还包括该类引用的所有其他类时,它会把 loadClass的resolve参数设置成true。这时,我们必须在返回刚刚装入的Class对象给调用者之前调用resolveClass。
三、 加密、解密
Java加密扩展即JavaCryptographyExtension,简称JCE。它是Sun的加密服务软件,包含了加密和 密匙生成功能。JCE是JCA(JavaCryptographyArchitecture)的一种扩展。
JCE没有规定具体的加 密算法,但提供了一个框架,加密算法的具体实现可以作为服务提供者加入。除了JCE框架之外,JCE软件包还包含了SunJCE服务提供者,其中包括许多 有用的加密算法,比如DES(DataEncryptionStandard)和Blowfish。
为简单计,在本文中我们将用 DES算法加密和解密字节码。下面是用JCE加密和解密数据必须遵循的基本步骤:
步骤1:生成一个安全密匙。在加密或解密任何数据之前 需要有一个密匙。密匙是随同被加密的应用一起发布的一小段数据,Listing3显示了如何生成一个密匙。【Listing3:生成一个密匙】
//DES 算法要求有一个可信任的随机数源
SecureRandomsr=newSecureRandom();
//为 我们选择的DES算法生成一个KeyGenerator对象
KeyGeneratorkg=KeyGenerator.getInstance("DES");
kg.init(sr);
//生成密匙
SecretKeykey=kg.generateKey();
//获取密匙数据
byterawKeyData[]=key.getEncoded();
/*接 下来就可以用密匙进行加密或解密,或者把它保存
为文件供以后使用*/
doSomething(rawKeyData);
步骤2:加密数据。得到密匙之后,接下来就可以用它加密数据。除了解密的ClassLoader之外,一 般还要有一个加密待发布应用的独立程序(见Listing4)。【Listing4:用密匙加密原始数据】
//DES 算法要求有一个可信任的随机数源
SecureRandomsr=newSecureRandom();
byterawKeyData[]=/*用 某种方法获得密匙数据*/;
//从原始密匙数据创建DESKeySpec对象
DESKeySpecdks=newDESKeySpec(rawKeyData);
//创建一个密匙工厂,然后用它把DESKeySpec转换成
//一个SecretKey对象
SecretKeyFactorykeyFactory=SecretKeyFactory.getInstance("DES");
SecretKeykey=keyFactory.generateSecret(dks);
//Cipher 对象实际完成加密操作
Ciphercipher=Cipher.getInstance("DES");
//用 密匙初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE,key,sr);
//现 在,获取数据并加密
bytedata[]=/*用某种方法获取数据*/
//正式执行加密操作
byteencryptedData[]=cipher.doFinal(data);
//进一步处理加密后的数据
doSomething(encryptedData);
步骤3:解密数据。运行经过加密的应用时,ClassLoader分析并解密类文件。操作步骤如Listing5所 示。【Listing5:用密匙解密数据】
//DES算法要求有一个可信任的随机数源
SecureRandomsr=newSecureRandom();
byterawKeyData[]=/*用某种方法获取原始密匙数据*/;
//从原始 密匙数据创建一个DESKeySpec对象
DESKeySpecdks=newDESKeySpec(rawKeyData);
//创建一个密匙工厂,然后用它把DESKeySpec对象转换成
//一个SecretKey对象
SecretKeyFactorykeyFactory=SecretKeyFactory.getInstance("DES");
SecretKeykey=keyFactory.generateSecret(dks);
//Cipher 对象实际完成解密操作
Ciphercipher=Cipher.getInstance("DES");
//用 密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE,key,sr);
//现 在,获取数据并解密
byteencryptedData[]=/*获得经过加密的数据*/
//正 式执行解密操作
bytedecryptedData[]=cipher.doFinal(encryptedData);
//进 一步处理解密后的数据
doSomething(decryptedData);
四、应用实 例
前面介绍了如何加密和解密数据。要部署一个经过加密的应用,步骤如下:
步骤1:创建应用。我们的例子包含一个App主类, 两个辅助类(分别称为Foo和Bar)。这个应用没有什么实际功用,但只要我们能够加密这个应用,加密其他应用也就不在话下。
步骤2:生成一 个安全密匙。在命令行,利用GenerateKey工具(参见GenerateKey.java)把密匙写入一个文 件:%javaGenerateKeykey.data
步骤3:加密应用。在命令行,利用 EncryptClasses工具(参见EncryptClasses.java)加密应用的 类:%javaEncryptClasseskey.dataApp.classFoo.classBar.class
该命令把每一个.class文件替换成它们各自的加密版本。
步骤4:运行经过加密的应用。用户通过一个DecryptStart程 序运行经过加密的应用。DecryptStart程序如Listing6所示。【Listing6:DecryptStart.java,启动被加 密应用的程序】
importjava.io.*;
importjava.security.*;
importjava.lang.reflect.*;
importjavax.crypto.*;
importjavax.crypto.spec.*;
publicclassDecryptStartextendsClassLoader
{
//这些对象在构造函数中设置,
//以后loadClass()方法将利用它们解密类
privateSecretKeykey;
privateCiphercipher;
//构造函数:设置解密所需要的对象
publicDecryptStart(SecretKeykey)throwsGeneralSecurityException,
IOException{
this.key=key;
Stringalgorithm="DES";
SecureRandomsr=newSecureRandom();
System.err.println("[DecryptStart:creatingcipher]");
cipher=Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE,key,sr);
}
//main过程:我们要在这里读入密匙,创建DecryptStart的
//实例,它就是我 们的定制ClassLoader。
//设置好ClassLoader以后,我们用它装入应用实例,
//最后,我们通过 JavaReflectionAPI调用应用实例的main方法
staticpublicvoidmain(Stringargs[])throwsException{
StringkeyFilename=args[0];
StringappName=args[1];
//这些是传递给应用本身的参数
StringrealArgs[]=newString[args.length-2];
System.arraycopy(args,2,realArgs,0,args.length-2);
//读 取密匙
System.err.println("[DecryptStart:readingkey]");
byterawKey[]=Util.readFile(keyFilename);
DESKeySpecdks=newDESKeySpec(rawKey);
SecretKeyFactorykeyFactory=SecretKeyFactory.getInstance("DES");
SecretKeykey=keyFactory.generateSecret(dks);
//创 建解密的ClassLoader
DecryptStartdr=newDecryptStart(key);
//创 建应用主类的一个实例
//通过ClassLoader装入它
System.err.println("[DecryptStart:loading"+appName+"]");
Classclasz=dr.loadClass(appName);
//最后,通过 ReflectionAPI调用应用实例
//的main()方法
//获取一个对main()的引用
Stringproto[]=newString[1];
ClassmainArgs[]={(newString[1]).getClass()};
Methodmain=clasz.getMethod("main",mainArgs);
//创建一个包含main()方法参数的数组
ObjectargsArray[]={realArgs};
System.err.println("[DecryptStart:running"+appName+".main()]");
//调用main()
main.invoke(null,argsArray);
}
publicClassloadClass(Stringname,booleanresolve)
throwsClassNotFoundException{
try{
//我们要创建的Class对象
Classclasz=null;
//必 需的步骤1:如果类已经在系统缓冲之中
//我们不必再次装入它
clasz=findLoadedClass(name);
if(clasz!=null)
returnclasz;
//下 面是定制部分
try{
//读取经过加密的类文件
byteclassData[]=Util.readFile(name+".class");
if(classData!=null){
//解密...
bytedecryptedClassData[]=cipher.doFinal(classData);
//...再把它转换成一个类
clasz=defineClass(name,decryptedClassData,
0,decryptedClassData.length);
System.err.println("[DecryptStart:decryptingclass"+name+"]");
}
}catch(FileNotFoundExceptionfnfe){
}
//必需的步骤2:如果上面没有成功
//我们尝试用默认的ClassLoader装入它
if(clasz==null)
clasz=findSystemClass(name);
//必需的步骤3:如有 必要,则装入相关的类
if(resolve&&clasz!=null)
resolveClass(clasz);
//把类返回给调用者
returnclasz;
}catch(IOExceptionie){
thrownewClassNotFoundException(ie.toString()
);
}catch(GeneralSecurityExceptiongse){
thrownewClassNotFoundException(gse.toString()
);
}
}
}
对于未经加密的应用,正常执行方式如 下:%javaApparg0arg1arg2
对于经过加密的应用,则相应的运行方式 为:%javaDecryptStartkey.dataApparg0arg1arg2
DecryptStart 有两个目的。一个DecryptStart的实例就是一个实施即时解密操作的定制ClassLoader;同时,DecryptStart还包含一个 main过程,它创建解密器实例并用它装入和运行应用。示例应用App的代码包含在App.java、Foo.java和Bar.java内。 Util.java是一个文件I/O工具,本文示例多处用到了它。完整的代码请从本文最后下载。
五、注意事项
我们看到,要在 不修改源代码的情况下加密一个Java应用是很容易的。不过,世上没有完全安全的系统。本文的加密方式提供了一定程度的源代码保护,但对某些攻击来说它是 脆弱的。
虽然应用本身经过了加密,但启动程序DecryptStart没有加密。攻击者可以反编译启动程序并修改它,把解密后的类文件 保存到磁盘。降低这种风险的办法之一是对启动程序进行高质量的模糊处理。或者,启动程序也可以采用直接编译成机器语言的代码,使得启动程序具有传统执行文 件格式的安全性。
另外还要记住的是,大多数JVM本身并不安全。狡猾的黑客可能会修改JVM,从ClassLoader之外获取解密后 的代码并保存到磁盘,从而绕过本文的加密技术。Java没有为此提供真正有效的补救措施。
不过应该指出的是,所有这些可能的攻击都有一 个前提,这就是攻击者可以得到密匙。如果没有密匙,应用的安全性就完全取决于加密算法的安全性。虽然这种保护代码的方法称不上十全十美,但它仍不失为一种 保护知识产权和敏感用户数据的有效方案。
- 浏览: 55040 次
- 性别:
- 来自: 深圳
-
文章分类
最新评论
发表评论
-
Java parameter types of annotation introduce
2018-03-11 18:31 480在实际应用中遇到了Ja ... -
Java performance tuning
2018-03-05 23:14 564Java性能调优的成功, ... -
图片上添加文字
2008-09-27 11:50 331public class ImageAddWord { ... -
java实现SMTP邮件服务器
2008-10-06 10:19 517电子邮件传递可以由多种协议来实现。目前,在Internet ... -
JNDI的含义和作用
2008-10-07 09:28 271JNDI是 Java 命名与目录接口(Java Naming ... -
java调用外部应用程序
2008-10-07 09:34 458String path = "C:/Documen ... -
jsp中实现参数隐藏的两种方法
2008-10-07 09:37 848在一个JSP页面有一个链接,//确定是一个链接?点击弹出一个 ... -
Java和Maven环境变量的配置
2008-10-07 09:51 479打开java官网Download页,选择你需要的版本,然后 ... -
SQLServer实现无限级树结构
2008-10-07 17:36 1055表结构如下: 数据库id path titlesort ... -
java经典的基础题目
2008-10-08 09:58 3311.列举出 10个JAVA语言的优势 a:免费,开源,跨平台 ... -
java中的接口Serializable的作用和例子
2008-10-08 10:32 295作用: 没有implements Se ... -
linux(Ubuntu)下常用命令备忘录1
2008-10-09 11:02 380在使用下面的命令是可 ... -
简单的checkbox管理代码
2008-10-09 11:41 454很简单的脚本操作 <script languag ... -
linux(Ubuntu)下常用命令备忘录2
2008-10-09 13:54 350以下也是常用到的命令 ... -
ubuntu中添加软件源
2008-10-10 13:42 394软件源是Ubuntu和Debian特有的一个概念,是它们的一 ... -
ubuntu下安装和卸载软件命令
2008-10-11 09:21 418安装软件命令:apt-getinstallsoftnam ... -
ubuntu配置拨号上网
2008-10-14 16:36 576这里就不截图了 记下配置命令: 1、设置拨号命令 sud ... -
java webservice在resin中实现
2008-10-17 11:59 351首先下载apache上的axis1.4 这里有两个版本axi ... -
java读写xml
2008-11-17 09:02 382这里总结一下XML文件的读写过程,先说一下写的过程,这里我用 ... -
JDK和JRE的区别
2008-12-31 15:02 352首先简单介绍了几个基本定义: JDK就是Java Devel ...
相关推荐
基于万能逼近原理的自适应模糊控制算法在多自由度AUV运动控制中的应用与抗干扰补偿Simulink仿真研究,自适应模糊控制算法的万能逼近原理与多自由度AUV运动控制的抗干扰补偿技术——基于Simulink的仿真研究,万能逼近原理自适应模糊控制算法的多自由度AUV运动控制抗干扰补偿simulink仿真 ,核心关键词:万能逼近原理; 自适应模糊控制算法; 多自由度AUV运动控制; 抗干扰补偿; Simulink仿真。,基于万能逼近的模糊控制算法多自由度AUV抗干扰补偿Simulink仿真
deepseek最新资讯、配置方法、使用技巧,持续更新中
deepseek最新资讯、配置方法、使用技巧,持续更新中
结合扩展卡尔曼滤波与滑模观测器的策略:优化电角度估计,反电势波形逼近完美正弦波,结合扩展卡尔曼滤波与滑模观测器的反电势波形优化:正弦波形展现近乎完美精度,电角度估算与实际应用差异微小,扩展卡尔曼滤波与滑模观测器的结合,反电势波形近乎完美的正弦波形,观测器估算转子电角度与实际电角度相差0.3弧度左右,转速跟随效果较好。 ,核心关键词:扩展卡尔曼滤波; 滑模观测器; 反电势波形; 转子电角度估算; 转速跟随效果。,卡尔曼滑模观测器:优化正弦波转子角度与转速估算
毕业设计_基于springboot+vue的**学生公寓管理系统**【源码+sql+可运行】【**50217**】.zip 全部代码均可运行,亲测可用,尽我所能,为你服务; 1.代码压缩包内容 代码:springboo后端代码+vue前端页面代码; 脚本:数据库SQL脚本 效果图:运行结果请看资源详情效果图 2.环境准备: - JDK1.8+ - maven3.6+ - nodejs14+ - mysql5.6+ - redis 3.技术栈 - 后台:springboot+mybatisPlus+Shiro - 前台:vue+iview+Vuex+Axios - 开发工具: idea、navicate 4.功能列表 - 系统设置:用户管理、角色管理、资源管理、系统日志 - **业务管理:业务管理:公寓信息、房间信息、入住记录、学生信息** 3.运行步骤: 步骤一:修改数据库连接信息(ip、port修改) 步骤二:找到启动类xxxApplication启动 4.若不会,可私信博主!!!
1、文件内容:xorg-x11-server-source-1.20.4-29.el7_9.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/xorg-x11-server-source-1.20.4-29.el7_9.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
1、文件内容:yum-plugin-ps-1.1.31-54.el7_8.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/yum-plugin-ps-1.1.31-54.el7_8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
基于模型预测控制(MPC)的无人船与无人车编队一致性协同控制研究(附原文献),基于模型预测控制(MPC)的无人船与无人车编队一致性协同控制研究(附原文献),无人船编队 无人车编队 MPC 模型预测控制 多智能体协同控制 一致性 MATLAB 无人车 USV 带原文献 ,无人船编队; 无人车编队; MPC 模型预测控制; 多智能体协同控制; 一致性; MATLAB; USV; 原文献,无人系统协同控制:MPC模型预测控制下的多智能体编队与一致性研究(原文献支撑)
4套中级通信工程师综合真题及答案(2019,2020,2021,2023),适用于需要考中级通信工程师的人群
deepseek最新资讯,配置方法,使用技巧,持续更新中
基于matlab的锁相环PLL相位噪声拟合仿真代码集合:多个版本建模与仿真,高质量的锁相环PLL仿真代码集合:Matlab与Simulink建模研究,[1]锁相环 PLL 几个版本的matlab相位噪声拟合仿真代码,质量杠杠的,都是好东西 [2]锁相环matlab建模稳定性仿真,好几个版本 [3]锁相环2.4G小数分频 simulink建模仿真 ,PLL; Matlab相位噪声拟合仿真; Matlab建模稳定性仿真; 锁相环2.4G小数分频Simulink建模仿真,MATLAB仿真系列:锁相环PLL及分频器建模仿真
exceptionLogs.zip
基于光伏微网的经济性与并网负荷波动率双目标优化调度策略:蓄电池与V2G协同管理策略仿真研究,MATLAB下光储充微网结合电动汽车V2G的多目标协同调度策略研究:经济性与并网负荷波动性的对比分析,MATLAB代码:考虑V2G的光储充一体化微网多目标优化调度策略 关键词:光储充微网 电电汽车V2G 多目标优化 蓄电池优化 调度 参考文档:《光伏微网下考虑V2G补偿蓄电池容量的双目标优化调度策略》,已经投稿EI会议,中文说明文档可联系我咨询 仿真平台:MATLAB 平台 优势:代码注释详实,适合参考学习,相关成果已经采用,程序非常精品,请仔细辨识 主要内容:过建立光伏微网中以经济性和并网负荷波动率为双目标的蓄电池和V2G的协同调度模型。 采用粒子群算法,对电网、微网调度中心和电动汽车用户三方在无、无序、转移和调度V2G电动汽车负荷四种运行模式下的经济和安全影响进行对比。 最后,根据算例分析,求解四种模式下两级负荷曲线及经济收益表。 对比分析得出,引入V2G可以替代部分容量的蓄电池,使光伏微网在负荷峰谷平抑、三方经济和安全等方面进一步优化。 求解采用的是PSO算法(粒子群算法),求解效果极
javascript 动态网页设计期末大作业(自己手写的,高分期末作业),含有代码注释,新手也可看懂,个人手打98分项目,导师非常认可的高分项目,毕业设计、期末大作业和课程设计高分必看,下载下来,简单部署,就可以使用。该项目可以直接作为毕设、期末大作业使用,代码都在里面,系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值,项目都经过严格调试,确保可以运行! javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期末大作业(自己手写的,高分期末作业)javascript 动态网页设计期
混合智能体系统编队控制:分布式优化与15异构混合阶的挑战,异构混合阶智能体系统编队控制的分布式优化策略研究,15异构混合阶多智能体系统编队控制的分布式优化(无参考文献) ,核心关键词:15异构混合阶; 多智能体系统; 编队控制; 分布式优化; 无参考文献。,15混合阶多智能体系统编队分布式优化控制
javascript 动态网页设计期末大作业(自己手写的,很适合期末作业),含有代码注释,新手也可看懂,个人手打98分项目,导师非常认可的高分项目,毕业设计、期末大作业和课程设计高分必看,下载下来,简单部署,就可以使用。该项目可以直接作为毕设、期末大作业使用,代码都在里面,系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值,项目都经过严格调试,确保可以运行! javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascript 动态网页设计期末大作业(自己手写的,很适合期末作业)javascrip
X光安检OPIXray数据集已经转换为VOC格式,可直接转换为为YOLO
DataX--Web:图形化界面简化大数据任务管理_datax-web
# 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!