모든 소프트웨어는 누군가에게는 기술의 결정체, 또 다른 누군가에게는 해킹의 대상이다. 하지만 아무리 정교하게 프로그램을 설계하더라도, 바이너리를 다운로드한 공격자는 리버스엔지니어링을 통해 코드의 핵심 로직을 꿰뚫어볼 수 있다. 이런 리버스엔지니어링을 방지하기 위한 대표적인 기법이 바로 코드 난독화 이다. 필자는 이번 글에서 코드 난독화의 원리, 기술 종류, 장단점에 대해서 알아보도록 하겠다.
코드 난독화는 말 그대로 사람이 이해하기 어렵게 코드를 복잡하게 만드는 기술이다. 이런 코드 난독화의 목표는 코드가 실행은 되지만 분석자는 해석하지 못하게 만드는 것이다.
일반적으로, 리버싱, 해킹, 크랙, 디컴파일을 어렵게 만들기 위해 사용된다.
원리: if-else, 반복문, switch 같은 로직을 분기 트리, 중첩된 루프, 상태머신으로 변경하여 코드 흐름을 비논리적으로 만들고 가독성이 없는 수준으로 만든다.
Before
if (user.isAdmin){
grantAccess();
}
After
switch(x^x){
case 0: if(flag + 3 - 3) break;
default: if(((x^x) | 0x0) == 0) grantAccess);
break;
원리: 로그 메시지, API 이름, 키워드 등을 암호화 후 런타임에 복호화하여 정적 분석 도구에서 민감한 문자열의 노출을 방지한다.
Before
printf("license key");
After
char* mas = decrpt("7abjfc9d987am..."); //AES or XOR
코드 난독화는 코드 분석을 지연시키는 방법이다. 하지만 요즘에는 AI기반 난독화 탐지 툴이 증가하면서 난독화가 더욱 복잡해질 필요가 있다. 또한 복호화된 문자열과 API 이름을 런타임 메모리에서 추출하는 등 여러가지 우회 방법이 있으니 난독화만 하지 말고 실행 환경 보호까지 하자. 그럼 이번 글은 여기서 마치고 필자는 다음 글로 돌아오겠다.