반응형
컴파일 후에도 주석을 유지하는 방법 (꼼수 및 우회법)
일반적으로 주석은 컴파일 과정에서 제거되기 때문에, 실행 파일에서 직접 확인할 수 없다. 하지만 몇 가지 우회 방법을 활용하면 주석과 유사한 정보를 실행 파일에 남길 수 있다.
다음과 같은 기법을 사용하면 디컴파일 시에도 주석에 해당하는 정보를 유지할 수 있다.
1. 문자열 리터럴(String Literal)로 주석 유지
가장 간단한 방법은 주석을 문자열로 변환하여 코드에 포함하는 것이다. 컴파일러는 실행되지 않는 주석을 제거하지만, 문자열 리터럴은 유지된다.
예제: C/C++에서 문자열 리터럴 활용
const char* comment = "이 코드는 두 수를 더하는 함수입니다.";
int add(int a, int b) {
return a + b;
}
- comment 변수는 코드 실행에는 영향을 미치지 않지만, 바이너리 내 문자열 섹션(.rodata)에 저장되므로 디컴파일 시 확인할 수 있다.
- 디컴파일러(Ghidra, IDA Pro 등)로 분석하면 "이 코드는 두 수를 더하는 함수입니다." 같은 문자열이 노출된다.
2. asm 또는 volatile을 이용한 강제 유지
일부 최적화 수준에서는 사용되지 않는 문자열 상수가 최적화로 인해 제거될 수 있다. 이를 방지하려면 volatile 또는 asm을 활용할 수 있다.
예제: volatile을 이용한 주석 유지 (C/C++)
volatile const char* comment = "이 코드는 두 수를 더하는 함수입니다.";
- volatile을 사용하면 컴파일러가 최적화하지 않고 강제로 변수 유지
- 디컴파일 시 문자열이 남아 있음
예제: asm을 이용한 주석 유지 (GCC, Clang)
__asm__("이 코드는 두 수를 더하는 함수입니다.");
- __asm__(inline assembly)을 이용하면, 해당 문자열이 어셈블리 코드에 포함됨
- 실행 파일을 분석하면 특정 어셈블리 섹션에 문자열이 남아 있음
3. 로그(Log) 또는 디버깅 메시지를 활용
로그 출력 함수를 활용하면 주석처럼 정보를 남길 수 있다.
예제: Python에서 print를 이용
print("DEBUG: 이 함수는 두 수를 더합니다.") # 주석을 남기는 효과
def add(a, b):
return a + b
- 실행 파일을 디컴파일하면 print 출력 문자열이 남아 있음
- 단점: 실행 시 로그가 출력될 수 있음
예제: C/C++에서 printf 또는 fprintf 활용
#include <stdio.h>
int add(int a, int b) {
printf("DEBUG: 이 함수는 두 수를 더합니다.\n");
return a + b;
}
- 실행 바이너리를 분석하면 "DEBUG: 이 함수는 두 수를 더합니다." 가 남아 있음
- 디컴파일 시 로그 출력이 포함됨
4. 매크로(Macro) 또는 애노테이션(Annotation) 사용
C/C++에서는 매크로를, Java/Python에서는 애노테이션을 활용하여 주석을 유지할 수 있다.
예제: C/C++ 매크로 활용
#define COMMENT(x) static const char* comment_##__LINE__ = x;
COMMENT("이 함수는 두 수를 더합니다.")
int add(int a, int b) {
return a + b;
}
- COMMENT() 매크로를 사용하여 각 라인의 주석을 문자열 변수로 변환
- 디컴파일 시 comment_23 = "이 함수는 두 수를 더합니다." 같은 형태로 남아 있음
예제: Java 애노테이션 활용
@Retention(RetentionPolicy.RUNTIME)
@interface Comment {
String value();
}
@Comment("이 메서드는 두 수를 더하는 역할을 합니다.")
public int add(int a, int b) {
return a + b;
}
- @Retention(RetentionPolicy.RUNTIME)을 적용하면 리플렉션을 통해 실행 중에도 주석 조회 가능
- 디컴파일 시 애노테이션이 남아 있음
5. 코드 난독화(Obfuscation) 기법을 활용
컴파일러가 최적화 과정에서 문자열을 제거하는 것을 막기 위해, 난독화 기법을 사용할 수도 있다.
예제: 문자열 인코딩 후 실행 시 복호화 (C)
#include <stdio.h>
void decode_and_print() {
char comment[] = { 73, 32, 108, 111, 118, 101, 32, 67, 111, 100, 101, 0 }; // "I love Code"
printf("%s\n", comment);
}
int main() {
decode_and_print();
return 0;
}
- 디컴파일 시 주석이 보이지 않지만, 실행 시 복구 가능
- 리버스 엔지니어링을 어렵게 만드는 효과
결론: 주석을 유지하는 꼼수는 가능하지만, 목적에 따라 방법이 다르다
방법 | 주석 유지 여부 | 장점 | 단점 |
문자열 리터럴 활용 | O | 간단하고 효과적 | 최적화 시 제거될 수도 있음 |
volatile 또는 asm 사용 | O | 최적화 방지 가능 | 코드 가독성이 떨어질 수 있음 |
로그(Log) 활용 | O | 디버깅에 유용 | 실행 시 로그가 남을 수 있음 |
매크로 또는 애노테이션 사용 | O | 코드 분석 시 유용 | 언어에 따라 적용 방식이 다름 |
난독화 기법 활용 | O | 리버스 엔지니어링 방지 | 복잡하고 유지보수 어려움 |
가장 쉬운 방법은 문자열 리터럴을 활용하는 것이며, 디컴파일 시에도 보이게 하려면 volatile 또는 로그 출력 방식을 사용할 수 있다.
반면, 리버스 엔지니어링을 어렵게 만들고 싶다면 난독화 기법을 활용하는 것이 효과적이다.
반응형
'IT생활' 카테고리의 다른 글
마인크래프트 작물 성장 속도(청크 틱 업데이트 속도)를 올리는 방법 (0) | 2025.03.16 |
---|---|
Windows에서 Remote Desktop 및 Samba(파일 공유) 계정 추가 방법 (0) | 2025.03.16 |
소스 코드의 주석은 컴파일될까? 그리고 디컴파일하면 복구될까? (0) | 2025.03.15 |
TrueNAS SCALE에서 Amazon S3 및 Google Cloud Storage와 같은 오브젝트 스토리지 구축 가능 여부 (0) | 2025.03.15 |
가용성(Availability) 비율에 따른 연간 다운타임 (0) | 2025.03.15 |