들어가며
프로그래머로 살다 보면 "이거 내가 만들면 더 잘 만들겠는데" 싶은 순간이 있습니다. 연차 관리 시스템 DayOFF는 그 충동에서 시작됐습니다.
주변에서 연차 관리를 엑셀로 하는 걸 보면서 항상 불편함을 느꼈습니다. 수식이 잘못 설계되어 있어도 아무도 모르고, 누군가 실수로 셀을 건드리면 데이터가 날아가고, 매년 새해가 되면 파일을 새로 복사해서 써야 하는 상황. HR 담당자가 연차 계산 실수로 노동부에 신고 당하는 일도 심심치 않게 일어납니다.
"근로기준법대로 정확하게, 실무에서 바로 쓸 수 있는 연차관리 프로그램을 직접 만들자."
그렇게 DayOFF 개발이 시작됐습니다.
지난 1편에서는 DayOFF의 탄생 배경과 연차 계산 엔진 구현, 그리고 개발하면서 발견한 버그들을 다뤘습니다.
1편 보러가기
https://imoracle.tistory.com/78
[연차 관리 프로그램] DayOFF 개발기, 편리한 연차 계산의 시작 -1-
들어가며프로그래머로 살다 보면 "이거 내가 만들면 더 잘 만들겠는데" 싶은 순간이 있습니다. 연차 관리 시스템 DayOFF는 그 충동에서 시작됐습니다. 주변에서 연차 관리를 엑셀로 하는 걸 보면
imoracle.tistory.com
이번 2편에서는 DayOFF의 또 다른 핵심인 라이센스 시스템과 보안을 다룹니다.
"프로그램 만드는 건 쉬운데 보호하는 게 어렵다"는 말이 있습니다. 실제로 라이센스 없이 프로그램을 마음대로 쓰거나, 크랙해서 배포하는 경우가 비일비재합니다. DayOFF는 이를 막기 위해 여러 보안 기술을 적용했습니다. 어떤 공격이 가능한지 직접 테스트해보고 하나씩 막아나간 과정을 공유합니다.

1. 라이센스 시스템 설계
왜 라이센스가 필요한가?
소프트웨어를 보호 장치 없이 배포하게 되면 다음과 같은 문제가 발생합니다.
- 무한 복사/배포 가능
- 30일 체험판의 무기한 사용
- 수익 창출 불가
- 사용 현황 파악 불가
DayOFF는 체험판(30일)과 정식판을 구분하고 기기별로 사용을 제한하는 서버 기반 라이센스 시스템을 구축했습니다.
전체 구조

라이센스 키 구조
- ABCD-EFGH-IJKL-MNOP + 1234 (인증코드)
- 16자리 키: 영문+숫자 조합
총 조합 수는 36^16 x 10^4 로 천문학적인 숫자입니다. 무작위 대입(Brute-force)으로 맞출 확률은 사실상 0에 가깝습니다. 또한 서버 DB에 발급된 키만 등록되어 있기 때문에, 키젠(Keygen)으로 유효한 규칙의 키를 만들어내도 서버 검증 단계에서 완벽히 차단됩니다.
2. 핵심 보안 메커니즘
기기 식별 (Machine ID)
동일한 라이센스로 여러PC에서 사용하는 것을 막기 위해 고유한 기기 식별값이 필요합니다.

MAC 주소는 사용자가 쉽게 변경할 수 있고, 하드디스크 시리얼은 가상화 환경에서 복제될 우려가 있습니다.
따라서 상대적으로 가장 변동성이 적고 안정적인 CPU ID를 채택했습니다.
암호화 오프라인 캐시
인터넷이 끊긴 환경에서도 일정 기간(예:7일)은 프로그램을 사용할 수 있도록, 라이센스 정보를 로컬에 암호화하여 저장합니다.

캐시 파일 자체를 다른 PC로 복사해 가더라도, 실행 시 측정한 MachineId 와 캐시 내부의 MachineId 가 불일치하기 때문에 검증에서 즉시 탈락합니다.
HMAC 서명 검증 (네트워크 위조 방어)
서버 응답을 네트워크에서 가로채서 위조하는 공격을 방어합니다.
가장 흔한 해킹 방식 중 하나는 Fiddler 같은 프록시 툴로 네트워크 응답을 가로채서 {"success": false}를 {"success": true}로 위조하는 것입니다. 이를 HMAC 서명으로 방어합니다.
웹 서버 측

클라이언트 측

비밀키(HMAC_SECRET)를 모르면 올바른 서명을 생성할 수 없습니다. 응답값을 임의로 조작해도 클라이언트의 서명 검증 로직에서 튕겨냅니다. 타임스탬프를 섞은 이유는 정상적인 응답을 녹화했다가 나중에 재전송하는 리플레이 어택(Replay Attack)을 막기 위함입니다.
브루트포스(무작위 대입) 방어
자동화 스크립트로 라이센스 키를 무한정 시도하는 것을 막기 위해 서버 단위에서 IP별 실패 횟수를 추적합니다.

5회 실패 시 30분간 차단되며, 성공하면 실패 기록이 초기화됩니다. 필요한 경우 관리자 페이지에서 억울하게 차단된 IP를 수동으로 해제할 수 있습니다.
3. 실제 창과 방패의 대결 (공격 테스트)
배포 전, 제가 직접 만든 라이센스 시스템 우회를 시도해 보았습니다.
시도 1: 치트엔진으로 런타임 패치
치트엔진을 통해 메모리를 분석하여 분기점(jump)을 찾았습니다.

jne를 jmp로 강제 패치하니 라이센스 체크를 건너뛰었습니다. 하지만 이는 런타임 메모리 패치라 프로그램을 재시작하면 원상 복구됩니다.
시도 2: dnSpy로 IL 코드 직접 패치
C#(.NET) 프로그램의 가장 큰 약점입니다. dnSpy로 어셈블리를 열면 IL(Intermediate Language) 코드가 훤히 보입니다.

조건문(brtrue.s)을 무조건 점프(br.s)로 수정 후 저장하니 라이센스 체크가 완전히 무력화되었습니다. 정보 탭에는 "미활성화"라 뜨지만 기능은 100% 동작했습니다. 이래서 난독화가 필수입니다.

방어 1: ConfuserEx 난독화 도입
ConfuserEx는 .NET 어셈블리 난독화 도구입니다. 무료이면서도 강력한 보호 기능을 제공합니다.
| Anti Tamper | 파일이 수정되면 런타임에 감지 후 강제 종료함으로써, IL 패치 후 저장하면 실행 자체가 되지 않습니다 |
| Anti ILDASM | dnSpy 같은 분석 툴에서 열기 어렵게 만듭니다 |
| Anti Proxy | 메서드 호출을 프록시를 통해 간접 호출로 변경하기에 코드 흐름 분석 난이도가 대폭 상승합니다 |
| Control Flow | 조건문, 반복문 구조를 복잡하게 변형하여 je/jne 패치 위치 자체를 찾기 어렵게 합니다 |
| Resource Protection | 리소스 파일을 암호화합니다 |
| Hardening | 위 보호들을 더 강화합니다 |
Anti Debug, Anti Dump 기능은 Windows Defender에서 바이러스로 오탐하는 경우가 잦아 보안과 사용성의 타협점을 찾아 제외했습니다.
난독화 전후 비교


난독화 적용 결과: 메서드와 변수명이 모두 깨진 유니코드로 변경되고, 제어 흐름이 박살 나 분석이 불가능해졌습니다.
방어 2: 파일 무결성 검사 (버전별 SHA256)
난독화를 뚫고 패치에 성공하더라도, 파일 해시값이 변경된 것을 서버가 감지해 차단합니다.


서버 DB에 {"2026.05.20": "aaa111...", "2026.05.26": "bbb222..."} 형태로 버전별 정상 해시를 관리하여, 구버전 사용자가 업데이트를 안 했다고 불법 사용자로 오인당하는 일을 방지했습니다.
4. 운영 편의성: 관리자 대시보드와 자동 업데이트
PHP 기반 실시간 관리자 페이지
보안 정책을 실시간으로 통제하기 위해 별도의 웹 대시보드를 구축했습니다.
???.com/???/admin/
├── dashboard.php (전체 현황, 차단 IP, 최근 로그)
├── license.php (키 생성/관리, 기간/최대 기기수 설정)
├── device.php (기기별 활성화 현황, 강제 해제)
├── security.php (무결성/디버거 감지 ON/OFF, 긴급 공지)
├── version.php (업데이트 관리, 앱 전체 차단 제어)
└── attempt.php (IP 차단 목록 관리)
취약점이 발견되면 즉시 앱 차단 ON으로 전면 통제하거나, 최소 지원 버전을 올려 구버전 사용자를 최신 버전으로 강제 이주시키는 등 유연한 대응이 가능합니다.
프로세스 무중단 자동 업데이트 (BAT 파일 활용)
실행 중인 EXE 파일은 덮어쓰기가 불가능합니다. 이를 해결하기 위해 백그라운드에서 임시 BAT 파일을 생성해 교체 작업을 위임합니다.

설치 경로를 Program Files가 아닌 AppData\Roaming\DayOFF로 지정하여, 업데이트 시 관리자 권한(runas)을 요구하지 않도록 설계했습니다. 덕분에 백신 오탐 문제도 깔끔하게 해결되었습니다.
스플래시 스크린 기반의 체계적인 부팅
프로그램이 켜지면서 백그라운드에서 이루어지는 방대한 검증 작업들을 스플래시 스크린으로 시각화했습니다.
- DB 초기화
- 공휴일 정보 로드
- 서버 연결 및 보안/무결성 체크
- 라이센스 검증
- 트레이 및 백업 설정
- 메인 화면 출력
마치며
라이센스 시스템과 보안을 직접 밑바닥부터 구현하며 뼈저리게 느낀 점이 있습니다. "완벽한 보안은 없다."
시간과 실력만 충분하다면 메모리 패치든 네트워크 위조든 결국 뚫리게 되어 있습니다. 대형 AAA 게임들도 며칠 만에 크랙이 나오는 마당이니까요.
결국 보안의 진짜 목표는 "뚫기 번거롭게 만들어서 해커가 크랙 시도 자체를 포기하게 만드는 것"입니다. DayOFF의 구매 비용보다 뚫는 데 들어가는 리소스(시간/노력)가 훨씬 크다면, 대부분은 정상적인 구매를 선택할 것입니다.
- 1차 방어 : HMAC 서명 (네트워크 패킷 위조 방어)
- 2차 방어 : ConfuserEx (코드 분석 및 패치 방해)
- 3차 방어 : Anti Tamper (런타임 파일 변조 감시)
- 4차 방어 : SHA256해시 (변조된 파일 실행 차단)
- 5차 방어 : Rate Limit (무작위 키 대입 차단)
- 6차 방어 : Machine ID (로컬 캐시 파일 복제 무력화)
- 7차 방어 : ...
- N차 방어 : ... 위 외에도 나오지 않은 보안 기술들 존재
각 보안 레이어가 독립적인 톱니바퀴처럼 맞물려 작동하기 때문에 하나를 무력화해도 다음 레이어에서 막힙니다. 완벽하진 않겠지만, 1인 개발 소프트웨어로서는 꽤 현실적이고 단단한 방어벽을 세웠다고 자부합니다.
다음편에서 드디어 완성된 DayOFF의 실제 모습을 직접 확인해 보세요!
3편 보러가기
'Project > [연차 관리 프로그램] DayOFF' 카테고리의 다른 글
| [연차 관리 프로그램] DayOFF 개발기, 완성 그리고 배포 -3- (0) | 2026.06.01 |
|---|---|
| [연차 관리 프로그램] DayOFF 개발기, 편리한 연차 계산의 시작 -1- (0) | 2026.06.01 |