背景

项目中碰到个用ionCube V10加密的源码,需要解密做审计,记录下踩的坑

image-20201011210643747

文件数量2000多,找了一番网站发现https://easytoyou.eu/可以提供解密服务,

image-20201011210820831

BTW,这个站一次只能上传一个文件,手动点2000多次不说累死也得烦死

过程

按照惯例,先注册个号看看有什么功能

image-20201011210916064

好嘛 10欧元30天会员,冲了再看看解密面板

image-20201011211004379

面板变了,上传个加密的文件抓个包看看

image-20201011211326533

解密完成后在页面中回显下载链接,那么就可以写脚本批量跑了。

思路

脚本需要实现的功能是遍历所有指定格式的文件,保存获取到的列表,上传解密,下载解密后的文件

直接贴代码

遍历符合格式的文件代码

# Load Folders
def get_all_path(self):
if os.path.exists(self.FileList):
pass
else:
postfix = set(['php']) # 设置要记录的文件格式
for maindir, subdir, file_name_list in os.walk(self.dstFolder):
for filename in file_name_list:
apath = os.path.join(maindir, filename)
if True: # 保存全部文件名。若要保留指定文件格式的文件名则注释该句
# 匹配后缀,只保存所选的文件格式。若要保存全部文件,则注释该句
if apath.split('.')[-1] in postfix:
try:
self.success('GET ' + str(apath))
with open(self.FileList, 'a+') as fo:
fo.writelines(apath)
fo.write('\n')
except:
pass # 所有异常全部忽略即可

写到一半想起来个事,直接覆盖源码有点不太合适,所以需要复制一份到保存的目录,然后用复制后的文件解密

拷贝到另一个文件夹代码

# Copy source dir to dest dir
def copy_search_file(self, srcDir, desDir):
self.info('Copy files to ' + desDir)
if os.path.exists(self.FileList):
pass
else:
if not os.path.isdir(desDir):
os.makedirs(desDir)
for files in os.listdir(srcDir):
name = os.path.join(srcDir, files)
back_name = os.path.join(desDir, files)
if os.path.isfile(name):
shutil.copy(name, back_name)
else:
if not os.path.isdir(back_name):
os.makedirs(back_name)
self.copy_search_file(name, back_name)

剩下的就是核心代码了

获取待解密的文件列表

# Load Files
def decrypt_FilesList(self):
self.success('Loading Need Decrpyt File List.')
startTime = time.process_time()
with open(self.FileList, 'r') as needDecryptFile:
DecryptFileList = needDecryptFile.read().splitlines()
endTime = time.process_time()
self.success('Load {0} File(s) in {1} s, \nNow start Decrypt.'.format(
len(DecryptFileList), endTime - startTime))
return DecryptFileList

上传+下载

# Decode_Files
def decode_Files(self, DecryptFileList):
for files in DecryptFileList:
self.info('Load ' + files + ', filename ' + files.split("\\")[-1])
file_content = open(files, 'rb+').read()
if b'ionCube Loader' in file_content:
file_name = files.split("\\")[-1]
keyParam = (datetime.datetime.now() +
datetime.timedelta(hours=self.timeZone)).strftime("%H")
file = {
str(keyParam) + '[]':
(file_name, file_content, 'application/octet-stream')
}
self.info("Upload " + files)
try:
res = requests.post(self.ez2uURL,
files=file,
headers=self.reqHeader,
timeout=self.reqTimeout)
if '403 Forbidden' not in res.text:
#print(res.content)
soup = BeautifulSoup(res.text, "lxml")
htmlATag = soup.select(
'div.container > div.alert.alert-success.fade.in > a'
)
if len(htmlATag) == 0:
self.errorLog(
'[!] File {0} maybe already decrypt'.format(
files))
else:
self.success("Get received link " +
htmlATag[0].string +
" Downloading...")
res2 = requests.get(htmlATag[0].string,
headers=self.reqHeader,
timeout=self.reqTimeout)
with open(files, 'wb+') as df:
df.write(res2.content)
else:
self.errorLog(
'[!] Maybe Network Error? File {0} Decrypt FAILED'
.format(files))
except:
self.errorLog(
'[!] Maybe Network Error? File {0} Decrypt FAILED'.
format(files))

日志功能

# Log
def errorLog(self, content):
print(content)
with open(self.errorLogPath, 'a+') as err:
err.write('[' +
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +
'] ' + content + ' \n')

def info(self, content):
print('[' + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +
'] [*] ' + content)

def success(self, content):
print('[' + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") +
'] [+] ' + content)

启动

# run
def run(self):
self.copy_search_file(self.sourceFolder, self.dstFolder)
self.get_all_path()
self.DecryptFileList = self.decrypt_FilesList()
self.decode_Files(self.DecryptFileList)

亿点点配置

配置

if __name__ == '__main__':

class options:
sourceFolder = "" # need decode path
dstFolder = '' # save path
ez2uURL = 'https://easytoyou.eu/decoder/ic10php56/'
reqHeader = {}
reqHeader['Cookie'] = ''
reqHeader['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'
errorLogPath = dstFolder + 'error.log' # errorlog path
timeZone=-7 #CN to EU
reqTimeout=20 #request timeout

deIC = IC_Decrypt(options)
deIC.run()

然后挂机跑即可,由于网络问题导致无法解密或解密失败的文件会输出到保存目录的error.log里,多跑几次即可。

成果

image-20201011212512201

完整代码

https://github.com/NS-Sp4ce/easytoyou_script