[IDA] Obfuscated String --> De-Obfuscation (IDAPython으로 문자열 난독화 해제)


0) 개요 및 목표

  • 악성코드 분석가로서, IDA Pro를 사용
  • 복합적인 문자열 난독화를 스크립트를 이용해서 분석
  • 난독화 알고리즘 확인
  • 목표 : 난독화 된 문자열이 특정 함수가 호출 된 후 난독화 해제가 되는 경우 IDAPython을 이용하여 동적 실행없이 복호화

1) 배경지식

  • 0. 난독화 된 문자열 부분 확인
    (MD5: e53428a55b212e61507746b649b648d8 / SHA2: b4e43fec37a026e4452ebfa6e480ecaa96e109899e6282852af451c4d8ad5a40)
  • 1. 문자열을 복호화하는 역할로 추정되는 함수 확인
  • 2. Xrefs to 를 이용한 함수 확인
    • Xrefs 확인 결과, 20개로 해당 함수가 호출되면 각각의 경우
  • 3. 해당 함수를 호출하는 경우
  • 런타임 상에서 해당 함수를 이용하여 문자열을 복호화 하는 것을 확신
  • 이러한 상태에 직면하게 되는 경우 선택 가능한 부분
    • 1) 난독화 된 문자열을 수동으로 복호화 및 복호화 된 이름으로 변경
    • 2) 동적으로 실행 후 복호화 및 복호화 된 이름으로 변경
    • 3) 복호화 및 복호화 된 이름으로 변경하는 스크립트 작성
  • 이러한 경우가 많지 않은 경우에는 1), 2) 로 커버가능하지만 몇 백개가 되는 경우에는 한계가 있으므로 스크립트를 이용한 해결 방법 필요

2) IDAPython 을 이용한 스크립트

  • 2-1) XrefsTo() - 0x10003b00
  • for addr in XrefsTo(0x10003b00, flags=0):
    print hex(addr.frm)

    ————— 실행 화면 ——————
    Python>for addr in XrefsTo(0x10003b00, flags=0):
    Python> print hex(addr.frm)
    Python>
    0x100034feL
    0x100036afL
    0x10003908L
    0x1000391fL
    0x10003936L
    0x1000394cL
    0x10003963L
    0x1000397aL
    0x10003990L
    0x100039b0L
    0x100039d0L
    0x100039efL
    0x10003a0fL
    0x10003a2fL
    0x10003a4fL
    0x10003a6fL
    0x10003a8fL
    0x10003aafL
    0x10003acfL
    0x10003ae6L

  • 2-2) supplied argument (문자열 인자) 가져오기
  • .text:100038F0 push ebp
    .text:100038F1 mov ebp, esp
    .text:100038F3 push ecx
    .text:100038F4 mov [ebp+var_4], ecx
    .text:100038F7 mov eax, [ebp+var_4]
    .text:100038FA add eax, 318h ; Add
    .text:100038FF push eax
    .text:10003900 push offset aCxwweckrxwTeey ; "Cxwweckrxw: teey-aurme"
    .text:10003905 mov ecx, [ebp+var_4]
    .text:10003908 call sub_10003B00 ; Call Procedure
    .text:1000390D mov ecx, [ebp+var_4]
    .text:10003910 add ecx, 418h ; Add
    .text:10003916 push ecx
    .text:10003917 push offset aCxwkewkUewgkh ; "Cxwkewk-Uewgkh: "
    .text:1000391C mov ecx, [ebp+var_4]
    .text:1000391F call sub_10003B00 ; Call Procedure
    .text:10003924 mov edx, [ebp+var_4]
    .text:10003927 add edx, 44Ah ; Add
    .text:1000392D push edx
    .text:1000392E push offset aCacheCxwkixuVa ; "Cache-Cxwkixu: vao-age=0"
    .text:10003933 mov ecx, [ebp+var_4]
    .text:10003936 call sub_10003B00 ; Call Procedure
    .text:1000393B mov eax, [ebp+var_4]
    .text:1000393E add eax, 47Ch ; Add
    .text:10003943 push eax
    .text:10003944 push offset aAcceyk ; "Acceyk: */*"
    .text:10003949 mov ecx, [ebp+var_4]
    .text:1000394C call sub_10003B00 ; Call Procedure
    .text:10003951 mov ecx, [ebp+var_4]
    .text:10003954 add ecx, 0D80h ; Add
    .text:1000395A push ecx
    .text:1000395B push offset aCxwkewkKpyeVlu ; "Cxwkewk-Kpye: vlukryaik/fxiv-daka; bxlw"...
    .text:10003960 mov ecx, [ebp+var_4]
    .text:10003963 call sub_10003B00 ; Call Procedure
    .text:10003968 mov edx, [ebp+var_4]
    .text:1000396B add edx, 0E84h ; Add
    .text:10003971 push edx
    .text:10003972 push offset aAcceykEwcxdrwg ; "Acceyk-Ewcxdrwg: gzry,defuake,jdch"
    .text:10003977 mov ecx, [ebp+var_4]
    .text:1000397A call sub_10003B00 ; Call Procedure

    • 관련 내용 스크립트
    • .text:10003900 push offset aCxwweckrxwTeey ; "Cxwweckrxw: teey-aurme"
      .text:10003905 mov ecx, [ebp+var_4]
      .text:10003908 call sub_10003B00 ; Call Procedure
      .rdata:1002471C aCxwweckrxwTeey db 'Cxwweckrxw: teey-aurme',0


      def find_function_arg(addr):
      while True:
      addr = idc.PrevHead(addr)
      if GetMnem(addr) == "push":
      print 'We found it at 0x%x' % GetOperandValue(addr, 0)
      break


      Python>def find_function_arg(addr):
      Python> while True:
      Python> addr = idc.PrevHead(addr)
      Python> if GetMnem(addr) == "push":
      Python> print 'We found it at 0x%x' % GetOperandValue(addr, 0)
      Python> break
      Python>
      Python>find_function_arg(0x10003908)
      We found it at 0x1002471c
      • 문자열이 위치한 오프셋 주소 획득 => 0x1002471c

  • 2-3) 오프셋 주소에서 문자열 추출
  • .rdata:1002471C aCxwweckrxwTeey db 'Cxwweckrxw: teey-aurme',0

    def get_string(addr):
    out = ""
    while True:
    if Byte(addr) != 0:
    out += chr(Byte(addr))
    else:
    break
    addr += 1
    return out


    Python>def get_string(addr):
    Python> out = “"
    Python> while True:
    Python> if Byte(addr) != 0:
    Python> out += chr(Byte(addr))
    Python> else:
    Python> break
    Python> addr += 1
    Python> return out
    Python>
    Python>get_string(0x1002471c)
    Cxwweckrxw: teey-aurme

  • 2-4) 복호화 함수 (0x10003B00) 분석
    • Hex-Ray를 이용하여 복호화 함수를 파악 후 스크립트로 구현

    def de_obf_str(obf_str):
    deobf = list(obf_str)
    deobf_str = ""
    num = 0
    for i in deobf:
    each_str = ord(i)
    if each_str < ord("i") or each_str > ord("p"):
    if each_str < ord("r") or each_str > ord("y"):
    if each_str < ord("I") or each_str > ord("P"):
    if each_str >= ord("R") and each_str <= ord("Y"):
    each_str -= 9
    deobf_str += chr(each_str)
    else : deobf_str += chr(each_str)
    else:
    each_str += 9
    deobf_str += chr(each_str)
    else:
    each_str -= 9
    deobf_str += chr(each_str)
    else:
    each_str += 9
    deobf_str += chr(each_str)
    return deobf_str

  • 2-5) 출력 및 자동 주석 기능 추가
  • print "[*] Attempting to de-obfuscate strings in malware"
    for x in XrefsTo(0x10003b00, flags=0):
    ref = find_function_arg(x.frm)
    string = get_string(ref)
    deobf_string = de_obf_str(string)
    print '[STRING]:%s\n[Deobfuscated]:%s' % (string,deobf_string)
    MakeComm(x.frm, deobf_string)
    MakeComm(ref, deobf_string)

  • def find_function_arg(addr):
    while True:
    addr = idc.PrevHead(addr)
    if GetMnem(addr) == "push":
    return GetOperandValue(addr, 0)
    return ""

    def get_string(addr):
    out = ""
    while True:
    if Byte(addr) != 0:
    out += chr(Byte(addr))
    else:
    break
    addr += 1
    return out

    def de_obf_str(obf_str):
    deobf = list(obf_str)
    deobf_str = ""
    num = 0
    for i in deobf:
    each_str = ord(i)
    if each_str < ord("i") or each_str > ord("p"):
    if each_str < ord("r") or each_str > ord("y"):
    if each_str < ord("I") or each_str > ord("P"):
    if each_str >= ord("R") and each_str <= ord("Y"):
    each_str -= 9
    deobf_str += chr(each_str)
    else : deobf_str += chr(each_str)
    else:
    each_str += 9
    deobf_str += chr(each_str)
    else:
    each_str -= 9
    deobf_str += chr(each_str)
    else:
    each_str += 9
    deobf_str += chr(each_str)
    return deobf_str


    print "[*] Attempting to de-obfuscate strings in malware"
    for x in XrefsTo(0x10003b00, flags=0):
    ref = find_function_arg(x.frm)
    string = get_string(ref)
    deobf_string = de_obf_str(string)
    print '[STRING]:%s\n[Deobfuscated]:%s' % (string,deobf_string)
    MakeComm(x.frm, deobf_string)
    MakeComm(ref, deobf_string)


  • 2-7) 실행화면
    • 2-7-1) 스크립트 파일 선택

    • 2-7-2) 문자열 출력 => print '[STRING]:%s\n[Deobfuscated]:%s' % (string,deobf_string)

    • 2-7-3) 자동주석

[References]

  1. Requirements: IDA 5.x and Python 2.5 (some versions use 2.6). Works with IDA Demo and Wine :)
  2. Optional IPython support (does't work for me, I get a black window): https://www.openrce.org/blog/view/1509/Interactive_IPython_Shell_for_IDA_Python

[Future works]

Copyright 2018. (JAEKI KIM) all rights reserved.
Copyright 2018. (JAEKI KIM) All pictures cannot be copied without permission.


comments powered by Disqus