前言
本文不会解释rc4加密是什么,以及ctf编码在我的理解中为一个大类,并非单独一种编码形式,当然不管是rc4还是ctf编码,其宗旨都是为了使字符串变得“毫无意义”从而达成无法被杀软正确的检查出是shellcode。 其实这篇文章更多的算是踩坑记录,因为本身代码的实现复制粘贴即可,讲加密代码的原理也大可不必,不可逆就行了。 目前针对rc4网上有不同的写法,主要根本是环境的不同,分别有
- Python2
- Python3
经过测试,Python3在脚本中计算rc4存在问题,常常导致过长的字符串加密后就无法还原,所以按照我看的某篇文章的说法是,环境最好使用Python2,实际环境测试,确实Python2的运算不会报错,那么下面写一下优缺点。 Python2
- 优点
- 能通过定义函数的方式完成rc4的加解密
- 缺点
- 大部分的工具(如pyinstaller最新版)都已经停止了2版本的支持,旧版本的pyinstaller打包的效果并不好,并且本身旧Pyinstaller打包的exe就算只print,有的杀软都会查
- 像自己用的比较好的nuitka,在python2上运行还是挺折磨的,必须存在双版本(因为其中一个组件不支持2),同时打包的exe无法正常执行。
Python3
- 优点
- 能用最新版的Pyinstaller打包程序,目前来看,比python2所支持的旧版本的pyinstaller打包的效果好(vt中美那么多默认杀的)
- 至少不会有那么多因为版本不适配导致的模块无法使用
- 缺点
- python3没法直接把rc4的加解密写代码里,所以只能通过加载第三方库的方式使用
- 像nuitka这种打包软件,加载第三方库后打包,有点大。
Python2版本实现
rc4实现代码如下
代码语言:javascript复制from ctypes import *
import ctypes,codecs
import sys, os, hashlib, time, base64
import random, string
import time
def GenPassword(length):
numOfNum = random.randint(1,length-1)
numOfLetter = length - numOfNum
slcNum = [random.choice(string.digits) for i in range(numOfNum)]
slcLetter = [random.choice(string.ascii_letters) for i in range(numOfLetter)]
slcChar = slcNum slcLetter
random.shuffle(slcChar)
getPwd = ''.join([i for i in slcChar])
return getPwd
def rc4(string, op='encode', public_key=GenPassword(7), expirytime=0):
ckey_lenth = 4
public_key = public_key and public_key or ''
key = hashlib.md5(public_key).hexdigest()
keya = hashlib.md5(key[0:16]).hexdigest()
keyb = hashlib.md5(key[16:32]).hexdigest()
keyc = ckey_lenth and (op == 'decode' and string[0:ckey_lenth] or hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]) or ''
cryptkey = keya hashlib.md5(keya keyc).hexdigest()
key_lenth = len(cryptkey)
string = op == 'decode' and base64.b64decode(string[4:]) or '0000000000' hashlib.md5(string keyb).hexdigest()[0:16] string
string_lenth = len(string)
result = ''
box = list(range(256))
randkey = []
for i in xrange(255):
randkey.append(ord(cryptkey[i % key_lenth]))
for i in xrange(255):
j = 0
j = (j box[i] randkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp
for i in xrange(string_lenth):
a = j = 0
a = (a 1) % 256
j = (j box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result = chr(ord(string[i]) ^ (box[(box[a] box[j]) % 256]))
if op == 'decode':
if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0) and result[10:26] == hashlib.md5(
result[26:] keyb).hexdigest()[0:16]:
return result[26:]
else:
return None
else:
return keyc base64.b64encode(result)
https://cloud.tencent.com/developer/article/1591312
按照原文的说法,每次加密的密钥都是随机生成的字符串,以此来减少特征,不是完全赞同,但是也没什么反驳的例子吧
有了上面的加密代码后,直接用即可
代码语言:javascript复制def kaisa_jiemi(s,k):
lower=string.ascii_lowercase
upper=string.ascii_uppercase
before=string.ascii_letters
after=lower[k:] lower[:k] upper[k:] upper[:k]
table=string.maketrans(after,before)
return s.translate(table)
...
rcpw = GenPassword(13)
buf = rc4(kaisa_jiemi("123",<此处是恺撒的key>),'encode',rcpw)
scode = rc4(buf, 'decode', rcpw)
由于脚本中只需要密文,所以加密的方法是可以不写在文件里的,方法如下
代码语言:javascript复制def kaisa_jiami(s,k):
lower=string.ascii_lowercase#小写英文字母
upper=string.ascii_uppercase#大写英文字母
before=string.ascii_letters#全部英文字母字母
after=lower[k:] lower[:k] upper[k:] upper[:k]#建立循环字母
table=''.maketrans(before,after)#创建映射表
return s.translate(table)
特别注意的是,python2的注释貌似不允许有中文,反正我是一直报错,所以请删除注释 ctf编码用的是,恺撒编码,其实也就是根据一个偏移量去变换位置 那么细心的你此时肯定发现了,加密的代码和解密的代码,除了maketrans中before和after的位置发生变化外,前面string.和''.也有区别 这是因为我运行的平台,生成shellcode.py的平台是python3的,所以加密部分的代码是按照python3来的,而在python2中不允许这么写,所以需要改为string.maketrans()
Python3版本实现
其实跟python2版本的内容差不多的
代码语言:javascript复制from Crypto.Cipher import ARC4
import base64
import random
import string
import codecs
import ctypes
import time
def GenPassword(length):
numOfNum = random.randint(1,length-1)
numOfLetter = length - numOfNum
slcNum = [random.choice(string.digits) for i in range(numOfNum)]
slcLetter = [random.choice(string.ascii_letters) for i in range(numOfLetter)]
slcChar = slcNum slcLetter
random.shuffle(slcChar)
getPwd = ''.join([i for i in slcChar])
return getPwd
def kaisa_jiemi(s,k):
lower=string.ascii_lowercase
upper=string.ascii_uppercase
before=string.ascii_letters
after=lower[k:] lower[:k] upper[k:] upper[:k]
table=''.maketrans(after,before)
return s.translate(table)
def rc4_encrypt(data, key1):
key = bytes(key1, encoding='utf-8')
enc = ARC4.new(key)
res = enc.encrypt(data.encode('utf-8'))
res=base64.b64encode(res)
res = str(res,'utf-8')
return res
def rc4_decrypt(data, key1):
data = base64.b64decode(data)
key = bytes(key1, encoding='utf-8')
enc = ARC4.new(key)
res = enc.decrypt(data)
res = str(res,'gbk')
return res
key=GenPassword(13)
rc4_encrypt("123",key)
在后续编写负责上线的.py文件时,千万要注意,rc4的key是字符串,恺撒的key需要的是数字(偏移量),两个变量名要区分一下。 和2版本一样,加密的方法是无需写在文件中的,方法如下
代码语言:javascript复制import string
import random
def kaisa_jiami(s,k):
lower=string.ascii_lowercase#小写英文字母
upper=string.ascii_uppercase#大写英文字母
before=string.ascii_letters#全部英文字母字母
after=lower[k:] lower[:k] upper[k:] upper[:k]#建立循环字母
table=''.maketrans(before,after)#创建映射表
return s.translate(table)
k=random.randint(1,6)
kaisa_jiami("123",k)