Created
October 2, 2017 03:41
-
-
Save herrcore/b935bd64d0af4e5761ba39c29a16b3f0 to your computer and use it in GitHub Desktop.
UCL NRV2B Decompression Library - Full Python (compression used by Zeus variants)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
################################################################################################ | |
## UCL NRV2B Decompression Library | |
## | |
## Code from "Clash of the Titans: ZeuS v SpyEye": | |
## https://www.sans.org/reading-room/whitepapers/malicious/clash-titans-zeus-spyeye-33393 | |
## Author: Harshit Nayyar, [email protected] | |
## | |
## NOTE: This is the compression algorithm used in the Zeus trojan and subsequent variants | |
## | |
## Lipstick, rouge, and bugfixes from: @herrcore | |
################################################################################################ | |
import struct | |
class DecompressError(Exception): | |
pass | |
def getBit(pos, recordDataEncoded, fourBytes, count): | |
#Get the bit at position count. If count == 0, reinitialize count and move to #next decompression. | |
if count == 0: | |
count = 31 | |
fourBytes = struct.unpack('<L', recordDataEncoded[pos:pos+4])[0] | |
pos += 4 | |
else: | |
count -= 1 | |
bit = ((fourBytes >> count ) & 1) | |
return (bit, pos, fourBytes, count) | |
def decompress(recordDataEncoded): | |
recordDataDecoded = '' | |
sPos = 0 | |
dPos = 0 | |
lastMOff = 1 | |
shift = 0 | |
fourBytes = 0 | |
#Main Loop | |
while True: | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
while(gb != 0): | |
recordDataDecoded += recordDataEncoded[sPos] | |
sPos += 1 | |
if sPos > len(recordDataEncoded): | |
raise DecompressError('Record Data Len Exceeded 1') | |
return recordDataDecoded | |
dPos += 1 | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
#mOff calculation | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
mOff = 2+gb | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
while(gb == 0): | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
mOff = 2*mOff + gb | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
if mOff == 2: | |
mOff = lastMOff | |
else: | |
mOff = (mOff - 3) * 256 + ord(recordDataEncoded[sPos]) | |
sPos += 1 | |
if sPos > len(recordDataEncoded): | |
raise DecompressError('Record Data Len Exceeded 2') | |
return recordDataDecoded | |
if int(mOff) == -1: | |
break; | |
else: | |
mOff += 1 | |
lastMOff = mOff | |
#mLen calculation | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(mLen, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
mLen = mLen*2 + gb | |
if mLen == 0: | |
mLen += 1 | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
mLen = 2*mLen + gb | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
while (gb == 0): | |
if sPos >= len(recordDataEncoded): | |
return recordDataDecoded | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
mLen = 2*mLen + gb | |
(gb, sPos, fourBytes, shift) = getBit(sPos, recordDataEncoded, fourBytes, shift) | |
mLen += 2 | |
if mOff > 0xd00: | |
mLen += 1 | |
mPos = dPos - mOff | |
if mPos < 0: | |
raise DecompressError('mPos is negative') | |
return recordDataDecoded | |
if mPos > dPos: | |
raise DecompressError('Oops mPos exceeds dPos') | |
return recordDataDecoded | |
#Copy uncompressed data | |
recordDataDecoded += recordDataDecoded[mPos] | |
mPos += 1 | |
dPos += 1 | |
while mLen > 0: | |
mLen -= 1 | |
recordDataDecoded += recordDataDecoded[mPos] | |
dPos += 1 | |
mPos += 1 | |
return recordDataDecoded |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment