破解 Acronis Cyber Backup 12.5

大类
Crack
技术标签
逆向-Windows
开发-HookPatch-Windows
优先级
Critical
开始日期
Nov 25, 2020
状态
Finalized
Public
Public
最后更新
Sep 8, 2021

Round 1:逆向Windows验证思路(12.5.16327)

通过直接分析安装后文件夹的结构以及输错license Key时的日志,可以很轻松的得到调用关系:
  • Management Server → AccountServer → LicenseServer
将LicenseServer的pyc反编译得到源码,可以分析到在model/license_key.py中有:
from acrolic_check import check_license as parse_serial_number
 
对应acrolic_check搜索后可知在PyShell模块中,ida分析可知从函数入口开始,该模块与内置的14种key参数做验证,成功了哪个,哪个就是这个key对应的发行版。
Key的大概验证算法:
v45 = (A^x · k^y) % B v44 = inv(v45, C) · y = y / v45 (mod C) A、B、C固定,k可以任意指定,v45、v44已知,求x、y MD5(v44[XXX] + v45[XXX]) == v45
 

Round 2:破解 Windows

破解思路

破解思路:hook libcrypto10使k变为0,同时令y = 0,则A^x可为任意数,剩下控制y即可实现等式
 

Keygen

def GenLicPayload(dist, timeLimit, lang, genDate, edition): LangTable = [ "ru-RU", "en-US", "de-DE", "ko-KR", "ja-JP", "pl-PL", "nl-NL", "fr-FR", "cs-CZ", "ja-JP", "fr-FR", "pt-BR", "es-ES", "zh-CN", "zh-TW", "it-IT", "th-TH", "fr-FR", "sk-SK", "pt-PT", "gsw-CH", "fr-CA", "en-GB" ] import struct langID = LangTable.index(lang) licPayload = struct.pack('HBBH', (dist & 0xfff) | (timeLimit << 12), edition, langID, genDate) return licPayload licPayload = GenLicPayload(dist, timeLimit, lang, genDate, edition) #v45 = ((ExeHash3 ** InputVal2) * (Key ** InputVal1) % ExeHash1) # input2 -> 0 --> v45 = 1 #v44 = inv(v45 % ExeHash2) * InputVal1 % ExeHash2 #v44[:14] = MD5(v44[14:] + v45)[:14] # (licPayload == v44[14:]) # If Key = 1, InputVal2 = 0 # v45 = 1 # v44 = InputVal1 % ExeHash2 # If Key = 1, # v45 = ((ExeHash3 ** InputVal2) % ExeHash1) # v44 = inv(v45 % ExeHash2) * InputVal1 % ExeHash2 --> InputVal1 = v44 * v45 % ExeHash2 # make InputVal2 = 1, v45 = ExeHash3 % ExeHash1 #ExeHash1_LE = '1903ADC443A8359DD274D921D5A42B2BB4D91CF266863C325457AC231398981C1C0FE7B9ED2C7B17A60E581C8CED6AFF0342355FA0407543462F6CBE7FEF27B67DB786E4565D4A775B22E5ACECB732DEBBA6009B313340D2A3E95643C1DEE565413ECB465977122969D8932B9289C0EDECE972D48D8184095CE8736059450DBD' ExeHash1 = int('0x' + '1903ADC443A8359DD274D921D5A42B2BB4D91CF266863C325457AC231398981C1C0FE7B9ED2C7B17A60E581C8CED6AFF0342355FA0407543462F6CBE7FEF27B67DB786E4565D4A775B22E5ACECB732DEBBA6009B313340D2A3E95643C1DEE565413ECB465977122969D8932B9289C0EDECE972D48D8184095CE8736059450DBD'.decode('hex')[::-1].encode('hex'), 16) #ExeHash2_LE = '37EB5D79D091D8782D57834A91139691361C09F3000000000000000000000000' ExeHash2 = int('0x' + '37EB5D79D091D8782D57834A91139691361C09F3000000000000000000000000'.decode('hex')[::-1].encode('hex'), 16) #ExeHash3_LE = '983951689797F3446B08BECCFE405582702966370110C0A31ECA0FEC96C06979C28685E241A9B18547B175CD5D96C3388820695AE662E424A6DABD3FF25302357D738034DFB7BE5FB7C014CBD2C52F82D5EF5C02E758C99FBA0CE8C50F1A7CB53D9028F8ACD16465114FCC87489B7B04062C8ED36FE55D4758ED7B26AD65DDB7' ExeHash3 = int('0x' + '983951689797F3446B08BECCFE405582702966370110C0A31ECA0FEC96C06979C28685E241A9B18547B175CD5D96C3388820695AE662E424A6DABD3FF25302357D738034DFB7BE5FB7C014CBD2C52F82D5EF5C02E758C99FBA0CE8C50F1A7CB53D9028F8ACD16465114FCC87489B7B04062C8ED36FE55D4758ED7B26AD65DDB7'.decode('hex')[::-1].encode('hex'), 16) dist = 0 # default timeLimit = 0 # no time limit lang = 'en-US' genDate = 0x1fff # maximum, in fact future = 978350400 + 86400 * 0x1fff edition=4 # VOLUME (no limit) def convertToLEB(v, n): return hex(v & ((2 << (8*n)) - 1))[2:].rstrip('L').rjust(2*n,'0').decode('hex')[::-1] from hashlib import md5 inputVal2 = 1 v45 = (ExeHash3 ** inputVal2) % ExeHash1 md5input = licPayload + convertToLEB(v45, 128) h = md5(md5input).digest() v44 = h[:14] + licPayload v44 = int('0x' + v44[::-1].encode('hex'), 16) inputVal1 = v44 * v45 % ExeHash2 fullPayload = convertToLEB(inputVal1, 20) + convertToLEB(inputVal2, 20) ALPHA = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ' def decode_acronis_key(a): bins = '' for c in a: bins = bin(ALPHA.index(c))[2:].rjust(5, '0') + bins bins = bins.rjust(((len(bins) + 7) // 8) * 8, '0') ret = [] for i in range(0, len(bins), 8): ret.append(int(bins[i:i+8], 2)) return ''.join((map(chr, ret[::-1]))) def encode_acronis_key(raw): binstr = ''.join([bin(ord(c))[2:].rjust(8, '0') for c in raw[::-1]]) binstr = binstr.rjust(((len(binstr) + 4) // 5) * 5, '0') ret = '' for i in range(len(binstr), 0, -5): ret += ALPHA[int(binstr[i-5:i], 2)] return ret #INPUT = 'FW3E5WZS24UNA4QH7XXFFG5PQZPJ8B4T4ERA9QAYKGKY3H2TKK4LFLUS4MBKXZYU' #ret = decode_acronis_key(INPUT) #print ret.encode('hex') #assert ret.encode('hex') == '8d0736f8c740688a847da5f7d6dca8f6576892c8825d742cf2d1451f1ec8310ad9a4c662a6d8bfd7' #ret = encode_acronis_key(ret) #assert ret == INPUT licCodeRaw = encode_acronis_key(fullPayload) licCode = [] for i in range(0, len(licCodeRaw), 8): licCode.append(licCodeRaw[i:i+8]) print '-'.join(licCode)
SampleKey: 23AAJGQQ-TRLP63MJ-9BUQUQZH-9KAMCVDH-32222222-22222222-22222222-22222222

破解实现

破解实现:使用https://github.com/kevinalmansa/DLL_Wrapper 做了一个DLL Forwarder,劫持BN_bin2bn函数,并在load对应bignum时替换为0
最后使用特殊的license: 23AAJGQQ-TRLP63MJ-9BUQUQZH-9KAMCVDH-32222222-22222222-22222222-22222222 即可

Round 3:破解Linux

Linux的acrolic_check同样位于PyShell,libcrypto10.dll变为liblibcrypto10.so。
破解实现:使用https://github.com/yugr/Implib.so 做了一个So Wrapper,同样劫持BN_bin2bn函数即可

打包破解docker

Agent端:直接抄的官方的Dockerfile
Server端:经总结必须要有systemd,否则就会各种出错。借鉴自https://github.com/silvenga-docker/acronis-backup