개발 slecs

결제 앱 APK 변조·정산 탈취 막은 다층 무결성 방어

목차

배경

이커머스 앱이 마켓에 풀린 뒤로 이상한 트래픽이 잡히기 시작했음. 정상 클라이언트로는 절대 나올 수 없는 호출 패턴이 보였고, 추적해 보니 APK 디컴파일 후 재서명한 변종이었음. 단순 난독화로는 한계가 명확했고, 결제 플랫폼 특성상 파트너 정산 데이터에 손대는 게 보여서 더 미룰 수 없었음.

1차: 서명 해시 검증

가장 먼저 한 건 런타임 서명 해시 검증임. 앱 부팅 시 패키지매니저로 서명 인증서를 꺼내 SHA-256 해시를 계산하고, 박아둔 정상 해시와 비교했음. 다르면 API 클라이언트 초기화 자체를 차단.

다만 자바 레이어에 박아두면 우회가 너무 쉬웠음. Smali 한 줄 패치로 비교 결과를 뒤집을 수 있어서, 정상 해시 자체를 NDK 영역으로 옮김.

2차: NDK 키 보관

native-keys.cpp 에 정상 해시와 시드 값을 XOR 인코딩으로 저장. CMakeLists.txt 에 strip 옵션과 visibility hidden 을 걸어 심볼 노출을 최소화.

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Os")
set_target_properties(nativekeys PROPERTIES LINK_FLAGS "-Wl,--gc-sections")

JNI 진입점만 export 하고 내부 함수는 전부 숨김. IDA 로 까도 문자열이 바로 안 보이게 됨.

3차: 루팅/Frida 탐지

런타임 무결성 체크 항목을 다음과 같이 분리했음.

검사 신호 대응
루팅 su 바이너리, magisk 흔적, /system 쓰기 가능 세션 차단
Frida gum-js-loop 스레드, frida-agent 매핑 즉시 종료
디버거 TracerPid 비제로 API 호출 거부
에뮬레이터 빌드 핑거프린트, 센서 부재 서버 플래그

특히 Frida 탐지는 /proc/self/maps 와 스레드 이름을 같이 봐야 우회가 어려웠음. 한 군데만 보면 후킹 한 방에 뚫림.

4차: 난독화 룰 정리

proguard-rules.pro 에서 리플렉션 깨질까봐 과하게 keep 해뒀던 항목을 싹 정리했음. 직렬화 모델만 남기고 나머지는 전부 난독화 대상으로 돌림. 빌드 후 mapping.txt 사이즈가 3배로 커진 게 체감됨.

검증하며 깨달은 것

  • 한 레이어만 막으면 무조건 뚫림. 자바·NDK·서버 셋이 동시에 검사해야 했음
  • 탐지 즉시 앱을 죽이지 않고, 서버에 신호로 먼저 올리는 채널을 따로 둠 → 공격자의 우회 시도 패턴이 로그로 쌓임
  • API 클라이언트 초기화 자체를 무결성 통과 후로 미루니, 우회 실패 시 네트워크 호출이 아예 안 나가서 분석 난이도가 한 단계 더 올라감

완벽한 방어는 불가능함. 다만 공격 비용을 충분히 올리면 자동화 변종은 거의 다 걸러지더라.

다음

댓글 0

첫 댓글 달아줘.