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에서 미리 지정한 상수에 따라 움직인다.
각 relevant blocks의 끝에 다음으로 이동 하는 relevant blocks 상태 변수가 존재한다. (참고)
0x00405CBF-> 0x00407456 -> 0x00402E00 으로 진행.
처음엔 당황스럽지만 아주 간단한 xor 연산이고 이 알고리즘으로 구현된 함수가 3개 존재한다.
첫 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)
잘 풀린다.