LLVM 프로젝트는 IR (Intermediate Representation) 수준에서 구현되어 프로그래밍 언어 (C, C ++, Objective-C, Ada 및 Fortran)와 CPU (x86, x86-64, PowerPC, PowerPC-64, ARM, Thumb, SPARC, Alpha, CellSPU, MIPS, MSP430, SystemZ, XCore)등이 호환된다.

 

여기서 bogus control-flow, control-flow flattening, code tamper-proofing 등을 적용한 것이 OLLVM이고 이 프로젝트는 OpenSource이다.

 

2018년에 OLLVM이 적용된 놈을 android 보안 솔루션에서 처음 접하여 분석했었는데... PC 악성코드에서 만나니 반가웠다. 

 

히오스 폭리 큐 기다리는 동안 작성 할 수 있는 것이 String Obfuscation 까지라... 여기까지만 정리하겠다.

 

분석 대상은  Dropper이며 뱅킹 타겟인 Emotet 악성코드이다.

 

 

MD5 SHA-256 File size
d0621ab41e352e3b73401169da6453e1 4096c2e62a6cc2dad5ad11e6fb77579de331ec545fb40144b1870133546694d6 66.50 KB (68096 bytes)

 

ollvm이 적용된 function의 control flow graph를 보면 다음과 같다.

내가 왜 이걸 다시 

 

ollvm으로 추가된 코드 동작은 prologue에서 상태 변수를 확인한 뒤 main dispatcher에서 미리 지정한 상수에 따라 움직인다.

 

0x1D415723

각 relevant blocks의 끝에 다음으로 이동 하는 relevant blocks 상태 변수가 존재한다. (참고)

 

 

Flow

0x00405CBF-> 0x00407456 -> 0x00402E00 으로 진행.

 

String_decode

 

처음엔 당황스럽지만 아주 간단한 xor 연산이고 이 알고리즘으로 구현된 함수가 3개 존재한다.

 

String Obfuscation algorithm

 

첫 4바이트가 키고 다음 4바이트는 길이고 그다음 바이트가 실제 문자열이다.

Key Length Data
90 EF 5B 6D 82 EF 5B 6D B5 9C 61 37 FF 81 3E 43

이 암호문의 문자열 길이는 18다.

enc_str B5 9C 61 37 FF 81 3E 43
xor key 90 EF 5B 6D 90 EF 5B 6D
dec_str 25 73 3A 5A 6F 6E 65 2E 
  %s:Z  one.

dec_str : %s:Zone.Identifier

 

이제 알고리즘도 알았고 일일이 복호화하기 귀찮으니 idapython으로 짜 보자

 

 

import struct

def getFunction(curHead):
  curHead = idc.PrevHead(curHead)
  
  while not (GetMnem(curHead) == "mov" and "ecx" in GetOpnd(curHead, 0)):
    curHead = idc.PrevHead(curHead)

  return GetOperandValue(curHead, 1)


def getString(addr):
  curAddr = addr
  enc = ""
  check = 0
  
  while Byte(curAddr) != 0 or len(enc) <= 8:
    curAddrByteVal = Byte(curAddr)

    enc     += chr(curAddrByteVal)
    check   += curAddrByteVal
    curAddr += 1
    
  return enc, check


def decrypt(encStr):
  paddedStr = encStr + ("\x00" * (4 - (len(encStr) % 4)))

  info = [paddedStr[i:i+4] for i in range(0, len(paddedStr), 4)]
  unpackedInfo = list(map(lambda x : struct.unpack('I', x), info))
  
  key    = unpackedInfo[0][0]
  length = unpackedInfo[1][0] ^ key
  decStr = ""
  
  for ui in unpackedInfo:
    decStr += struct.pack("I",ui[0] ^ key)

  decStr = decStr[8:length+8]
    
  return decStr


def decryptXrefStrings(addr):
    xrefs = XrefsTo(addr, flags = 0)

    for addr in xrefs:
        ref = getFunction(addr.frm)
        encStr, check = getString(ref)

        if check != 0:
          decStr = decrypt(encStr)

          MakeComm(addr.frm, decStr)
          MakeComm(ref, decStr)

          print("Address: %x Enc string %s.." % (addr.frm, encStr.encode('hex')[:10]))
          print("Dec string: %s" % decStr)


#Main routine

targetAddrs = [0x00402D40, 0x00402C60, 0x00406730]

for targetAddr in targetAddrs:
  decryptXrefStrings(targetAddr)

 

Output window

 

잘 풀린다.

Posted by kkoha :