#!/usr/bin/env python
# encoding: utf-8

import re
import codecs
from modules.mp_module import MpModule
from common.utils import randomAlpha
from random import randint
import logging


class ObfuscateStrings(MpModule):
    
    hexToStringRoutine = \
'''Private Function HexToStr(ByVal hexString As String) As String
Dim counter As Long
For counter = 1 To Len(hexString) Step 2
HexToStr = HexToStr & Chr$(Val("&H" & Mid$(hexString, counter, 2)))
Next counter
End Function
'''


    def _splitStrings(self, macroLines):
        
        # Find strings and randomly split them in half 
        for n,line in enumerate(macroLines):
            #Check if string is not preprocessor instruction, const or contain escape quotes
            if len(line) > 3 and "\"\"" not in line  and "PtrSafe Function" not in line and "Declare Function" not in line and "Declare Sub" not in line and "PtrSafe Sub" not in line and "Environ" not in line:
                # Find strings in line
                findList = re.findall( r'"(.+?)"', line, re.I) 
                if findList:
                    for detectedString in findList:
                        if len(detectedString) > 3:
                            # Compute value to cut string randomly
                            randomValue = randint(1, len(detectedString)-1)
                            newStr = detectedString[:randomValue] + "\" & \"" + detectedString[randomValue:] 
                            line = line.replace(detectedString, newStr)
                    macroLines[n] = line
        return macroLines
    
    
    
    def _maskStrings(self,macroLines, newFunctionName):
        """ Mask string in VBA by encoding them """
        # Find strings and replace them by hex encoded version
        for n,line in enumerate(macroLines):
            #Check if string is not preprocessor instruction, const or contain escape quoting
            if line.lstrip() != "" and line.lstrip()[0] != '#' and  "Const" not in line and  "\"\"" not in line and "PtrSafe Function" not in line and "Declare Function" not in line and "PtrSafe Sub" not in line and "Declare Sub" not in line and "Environ" not in line:
                # Find strings in line
                findList = re.findall( r'"(.+?)"', line, re.I) 
                if findList:
                    for detectedString in findList: 
                        # Hex encode string
                        encodedBytes = codecs.encode(bytes(detectedString, "utf-8"), 'hex_codec')
                        newStr = newFunctionName + "(\"" + encodedBytes.decode("utf-8")  + "\")"
                        wordToReplace =  "\"" + detectedString + "\""
                        line = line.replace(wordToReplace, newStr)
                # Replace line if result is not too big
                if len(line) < 1024:
                    macroLines[n] = line
        
        return macroLines
    
    
    
    def run(self):
        logging.info(" [+] VBA strings obfuscation ...") 
        logging.info("   [-] Split strings...")
        logging.info("   [-] Encode strings...")
        for vbaFile in self.getVBAFiles():
            # Compute new random function and variable names for HexToStr
            newFunctionName = randomAlpha(12)
            newVarName1 = randomAlpha(12)
            newVarName2 = randomAlpha(12)
            
            f = open(vbaFile)
            content = f.readlines()
            f.close()
            
            # Split string
            content = self._splitStrings(content)
            # mask string
            content = self._maskStrings(content, newFunctionName)
        
            # Write in new file 
            f = open(vbaFile, 'w')
            f.writelines(content)
            f.write(self.hexToStringRoutine.replace("HexToStr", newFunctionName).replace("counter", newVarName1).replace("hexString", newVarName2))
            f.close()
        logging.info("   [-] OK!")