绿斑
本文Python代码针对绿斑文件窃取器PE中的C2通过异或解密提取。
样本来源:
从绿斑APT组织的文件窃取器理解加密流量,生产流量规则以及密文还原
代码
反汇编
IDA
.text:00402B89 lea eax, [esp+900h+encryptedC2]
.text:00402B90 push 37h ; '7' ; Size
.text:00402B92 push offset encryptedC2 ; Src
.text:00402B97 push eax ; void *
.text:00402B98 call _memmove
.text:00402B9D movups xmm0, xmmword ptr [esp+90Ch+encryptedC2]
.text:00402BA5 add esp, 0Ch
.text:00402BA8 xor ecx, ecx
.text:00402BAA movups xmm1, key_422918
.text:00402BB1 mov edx, 17h
.text:00402BB6 pxor xmm1, xmm0
.text:00402BBA movups xmm0, xmmword ptr [esp+900h+var_848]
.text:00402BC2 movups xmmword ptr [esp+900h+encryptedC2], xmm1
.text:00402BCA movups xmm1, xmmword_422928
.text:00402BD1 pxor xmm1, xmm0
.text:00402BD5 movups xmmword ptr [esp+900h+var_848], xmm1
.text:00402BDD nop dword ptr [eax]
.text:00402BE0
.text:00402BE0 loc_402BE0: ; CODE XREF: _main+F3↓j
.text:00402BE0 mov al, key_422938[ecx]
.text:00402BE6 lea ecx, [ecx+1]
.text:00402BE9 xor byte ptr [esp+ecx+900h+var_848+0Fh], al
.text:00402BF0 sub edx, 1
.text:00402BF3 jnz short loc_402BE0
地址 | Hex | 反汇编代码 |
---|---|---|
402B89 | 8D8424 A8000000 | LEA EAX,DWORD PTR SS:[ESP+0xA8] |
402B90 | 6A 37 | PUSH 0x37 |
402B92 | 68 9E284200 | PUSH 0042289E |
402B97 | 50 | PUSH EAX |
402B98 | E8 13120000 | CALL 00403DB0 |
402B9D | 0F108424 B4000000 | MOVUPS XMM0,DQWORD PTR SS:[ESP+0xB4] |
402BA5 | 83C4 0C | ADD ESP,0xC |
402BA8 | 33C9 | XOR ECX,ECX |
402BAA | 0F100D 18294200 | MOVUPS XMM1,DQWORD PTR DS:[0x422918] |
402BB1 | BA 17000000 | MOV EDX,0x17 |
402BB6 | 66:0FEFC8 | PXOR MM1,MM0 |
402BBA | 0F108424 B8000000 | MOVUPS XMM0,DQWORD PTR SS:[ESP+0xB8] |
402BC2 | 0F118C24 A8000000 | MOVUPS DQWORD PTR SS:[ESP+0xA8],XMM1 |
402BCA | 0F100D 28294200 | MOVUPS XMM1,DQWORD PTR DS:[0x422928] |
402BD1 | 66:0FEFC8 | PXOR MM1,MM0 |
402BD5 | 0F118C24 B8000000 | MOVUPS DQWORD PTR SS:[ESP+0xB8],XMM1 |
402BDD | 0F1F00 | NOP DWORD PTR DS:[EAX] |
402BE0 | 8A81 38294200 | MOV AL,BYTE PTR DS:[ECX+0x422938] |
402BE6 | 8D49 01 | LEA ECX,DWORD PTR DS:[ECX+0x1] |
402BE9 | 30840C C7000000 | XOR BYTE PTR SS:[ESP+ECX+0xC7],AL |
402BF0 | 83EA 01 | SUB EDX,0x1 |
402BF3 | 75 EB | JNZ SHORT 00402BE0 |
参数inHEX
密文
密钥
密文长度
C2解密脚本
半自动-离线无文件版(手动输入数据,适配性高)
key = [ 0x18, 0x92, 0x1A, 0x01, 0x98, 0xE6, 0xF3, 0x00, 0x59, 0x9F, 0xF0, 0x25, 0x19, 0xEC, 0xB1, 0x35, 0x23, 0x7F, 0x73, 0x3F, 0xED, 0xD3, 0xF7, 0x69 ]
lenKey = len(key)
C2Encrypted = [ 0x7A, 0xE0, 0x75, 0x76, 0xEB, 0x83, 0x81, 0x6E, 0x3C, 0xEB, 0xDE, 0x4C, 0x6D, 0x9F, 0xD0, 0x5A, 0x4F, 0x51, 0x10, 0x50, 0x80, 0xD3, 0xCF, 0x59 ]
lenEncrypted = len(C2Encrypted)
C2 = ''
n0 = 0
for i in range(lenEncrypted):
xor = C2Encrypted[i] ^ key[i % lenKey]
if (xor == 0):
C2 += "\r\n"
n0 += 1
if (n0 == 2):
break
C2 += "端口:"
else:
C2 += chr(xor)
print("域名:", C2)
全自动-读取PE文件(PE适配性差)
好麻烦啊,眼泪流下来,ImageBase打出来不对——更新:数据是十进制的,又忘了转十六进制,永远健忘,永远热泪盈眶😡😡😡
🙇♀️🙇♀️🙇♀️特别感谢杰哥帮我对数据进行转换🙇♀️🙇♀️🙇♀️
import binascii
import pefile
import re
# 小端序转大端序
def Little2Big(strLittle):
strBig = (int(strLittle, 16).to_bytes(4, 'little')).hex()[:6].upper()
return strBig
# 十六进制字符串处理为list
def hexstr2list(strHex):
time = int(len(strHex)/2)
j = 0
listHex = []
for i in range(time):
strHex2 = strHex[j : j+2]
j += 2
listHex.append(strHex2)
return listHex
# 1. 以十六进制读取PE样本
def File2Read():
filePE = open(pathFile, "rb")
if(filePE):
print("文件:\t\t\t成功读取文件")
else:
print("文件:\t\t无文件 / 文件无数据")
exit(0)
dataPE = filePE.read()
global hexPE
hexPE = binascii.b2a_hex(dataPE).decode('utf-8').upper()
# 2. 正则匹配
def HexRegVA():
# 2.1 正则匹配:密文,密钥,长度
'''
# 原(compile + pattern),后(findall+转数据格式)更简洁点
pattern = re.compile(r'A80000006A[0-9A-F]{2}68([0-9A-F]{6})0050E8[0-9A-F]+0F100D[0-9A-F]{6}00BA[0-9A-F]{2}000000660FEFC8')
'''
#findall写在一个表达式里返回的是tuple
regData = list(re.findall(r'A80000006A[0-9A-F]{2}68([0-9A-F]{6})0050E8[0-9A-F]+0F100D([0-9A-F]{6})00BA([0-9A-F]{2})000000660FEFC8', hexPE)[0])
# 2.1.1 密文
addEncryptedString_Little = regData[0]
print("密文inHex:\t\t", addEncryptedString_Little, type(addEncryptedString_Little))
# 2.1.1 密钥
addKey_Little = regData[1]
print("密钥inHex:\t\t", addKey_Little, type(addKey_Little))
# 2.1.2 长度
global lenC2
lenC2 = int(regData[2], 16) + 1
print("C2明文长度:\t\t", lenC2, type(lenC2))
# 2.2 VA
# 2.2.1 密文
encrypted_Big = Little2Big(addEncryptedString_Little)
encryptedVA = int(encrypted_Big, 16)
print("密文inVA:\t\t", hex(encryptedVA).upper(), type(encryptedVA))
# 2.2.1 密钥
key_Big = Little2Big(addKey_Little)
keyVA = int(key_Big, 16)
print("密钥inVA:\t\t", hex(keyVA).upper(), type(keyVA))
# 2.3 PE数据
filePE = pefile.PE(pathFile)
# RAV = VA - ImageBase
# FileOffset = RVA - VRk
# 得:FileOffset = VA - ImageBase - VRk
# 2.3.1 ImageBase
intImageBase = filePE.OPTIONAL_HEADER.ImageBase
ImageBase = int((hex(intImageBase))[2:], 16)
print("ImageBase:\t\t", hex(ImageBase), type(ImageBase))
# 2.3.2 RVA
# 2.3.2.1 密文RVA
encryptedRVA = (int(encryptedVA) - ImageBase)
print("密文RVA:\t\t", hex(encryptedRVA).upper(), type(encryptedRVA))
# 2.3.2.2 密钥RVA
keyRVA = (int(keyVA) - ImageBase)
print("密钥RVA:\t\t", hex(keyRVA).upper(), type(keyRVA))
# 2.3.3 FileOffset
# 2.3.3.1 密文FileOffset
encryptedFileOffset = filePE.get_offset_from_rva(encryptedRVA)
print("密文FileOffset:\t", hex(encryptedFileOffset).upper(), type(encryptedFileOffset))
# 2.3.3.2 密钥FileOffset
keyFileOffset = filePE.get_offset_from_rva(keyRVA)
print("密钥FileOffset:\t", hex(keyFileOffset).upper(), type(keyFileOffset))
# 2.4 从PE中取数据
# 2.4.1 密文
global encrypted
encrypted = hexstr2list((hex(int(hexPE[2 * encryptedFileOffset : 2 * (encryptedFileOffset + lenC2)], 16))[2:].upper()))
print("密文为:\t\t\t", encrypted)
# 2.4.2 密钥
global key
key = hexstr2list((hex(int(hexPE[2 * keyFileOffset : 2 * (keyFileOffset + lenC2)], 16))[2:].upper()))
print("密钥为:\t\t\t", key)
# 3. 异或解密
def Encrypted():
C2 = ''
n0 = 0
for i in range(lenC2):
xor = int(encrypted[i], 16) ^ int(key[i], 16)
i += 2
if (xor == 0):
C2 += "\r\n"
n0 += 1
if (n0 == 2):
break
C2 += "端口:\t"
else:
C2 += chr(xor)
print("域名:", C2)
if __name__ == '__main__':
pathFile = ".\\GreenSpot2020"
File2Read()
HexRegVA()
Encrypted()
# 【长度都是24,考虑通过第两次0x00后break】