---
title: "위협 탐지 콜백 연동"
description: "DoveRunner로 보호된 Android 앱에서 Android 브로드캐스트를 통해 실시간 위협 탐지 콜백을 수신합니다."
---
> For the complete documentation index, see [llms.txt](https://docs.doverunner.com/llms.txt).

보호된 Android 앱에서 동작이 **콜백**으로 설정된 위협이 탐지되면, DoveRunner Mobile App Security는 앱을 종료하지 않고 Android 브로드캐스트를 통해 앱에 실시간으로 알립니다. 앱은 이 알림을 받아 사용자 세션 종료, 경고 화면 표시, 서버로의 이벤트 보고 등 자체적인 방식으로 대응할 수 있습니다.

각 브로드캐스트에는 다음 두 값이 포함됩니다.

- `threat_code` (String) — 탐지된 위협을 식별하는 코드
- `timestamp` (long) — 탐지 시각

## 사전 요구 사항

- 앱이 실링 버전 **3.3.0.0 이상**으로 실링되어 있어야 합니다.
- 앱으로 전달할 탐지 항목마다 동작이 **콜백**으로 설정되어 있어야 합니다. 실링 위저드에서는 [위협 대응 동작](https://docs.doverunner.com/ko/mobile-app-security/android/android-sealing-options/#%EC%9C%84%ED%98%91-%EB%8C%80%EC%9D%91-%EB%8F%99%EC%9E%91)을, CLI에서는 해당 `action_*` 옵션을 `callback`으로 설정하는 방법을 [위협 대응 정책](https://docs.doverunner.com/ko/mobile-app-security/android/cicd/basic-configuration/#%EC%9C%84%ED%98%91-%EB%8C%80%EC%9D%91-%EC%A0%95%EC%B1%85-3300-%EC%9D%B4%EC%83%81)에서 참고하세요.
- [연동 절차](#연동-절차)에 따라 앱에 브로드캐스트 리시버가 등록되어 있어야 합니다.

**Caution:** 
탐지 항목을 **콜백**으로 설정했지만 앱에 리시버를 구현하지 않으면, 해당 탐지에 대해 앱은 모니터링 전용 모드로 동작합니다. 위협은 탐지·보고되지만 앱은 **종료되지 않습니다.**


## 브로드캐스트 명세

| 항목 | 값 |
| :--- | :- |
| Action | `com.doverunner.aos.security.action.THREAT_DISCOVERED` |
| 필요 권한 | `<보호된 앱 패키지명>.doverunner.permission.RECEIVE_ALERTS` |

**Extras**

| 키 | 타입 | 설명 |
| :- | :--- | :--- |
| `threat_code` | String | 탐지된 위협을 식별하는 코드입니다. [위협 대응 코드](#위협-대응-코드)를 참고하세요. |
| `timestamp` | long | 탐지 시각입니다. Unix epoch 기준 밀리초 단위입니다. |

## 연동 절차


1. **`AndroidManifest.xml`에 권한을 선언합니다.**

   콜백 권한은 보호된 앱의 패키지별로 생성됩니다. 아래 형식에 따라 앱 패키지명으로 권한 이름을 구성하세요.

   | 항목 | 값 |
   | :--- | :- |
   | 형식 | `<보호된 앱 패키지명>.doverunner.permission.RECEIVE_ALERTS` |
   | 예시: 앱 패키지명 | `com.company.bank` |
   | 예시: 권한 이름 | `com.company.bank.doverunner.permission.RECEIVE_ALERTS` |

   ```xml
   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.company.bank">

       <!-- "com.company.bank"를 앱의 패키지명으로 변경하세요 -->
       <permission
           android:name="com.company.bank.doverunner.permission.RECEIVE_ALERTS"
           android:protectionLevel="signature" />

       <uses-permission android:name="com.company.bank.doverunner.permission.RECEIVE_ALERTS" />

       <application>
           ...
       </application>

   </manifest>
   ```

2. **브로드캐스트 리시버를 등록합니다.**

   `<application>` 요소 안에 리시버를 등록하고, 1단계에서 선언한 권한으로 리시버를 보호합니다.

   ```xml
   <application>

       <receiver
           android:name=".security.ThreatCallbackReceiver"
           android:exported="true"
           android:permission="com.company.bank.doverunner.permission.RECEIVE_ALERTS">
           <intent-filter>
               <action android:name="com.doverunner.aos.security.action.THREAT_DISCOVERED" />
           </intent-filter>
       </receiver>

   </application>
   ```

   **Caution:** 
   브로드캐스트 Action과 권한 이름은 서로 대응하는 값이 아니라 별개의 값입니다. Action 문자열 `com.doverunner.aos.security.action.THREAT_DISCOVERED`는 모든 앱에서 동일하며, 권한 이름만 보호된 앱 패키지별로 고유합니다.
   

3. **리시버를 구현합니다.**

   리시버는 인텐트에서 `threat_code`와 `timestamp`를 읽어 대응 로직으로 전달합니다.

   **Java**


   ```java
   package com.company.bank.security;

   import android.content.BroadcastReceiver;
   import android.content.Context;
   import android.content.Intent;
   import android.util.Log;

   public class ThreatCallbackReceiver extends BroadcastReceiver {

       private static final String TAG = "ThreatCallbackReceiver";
       private static final String ACTION_THREAT_DISCOVERED =
               "com.doverunner.aos.security.action.THREAT_DISCOVERED";

       @Override
       public void onReceive(Context context, Intent intent) {
           if (intent == null) return;

           if (ACTION_THREAT_DISCOVERED.equals(intent.getAction())) {
               String threatCode = intent.getStringExtra("threat_code");
               long timestamp = intent.getLongExtra("timestamp", 0L);

               Log.i(TAG, "Threat callback received. threat_code=" + threatCode
                       + ", timestamp=" + timestamp);

               handleThreat(context, threatCode, timestamp);
           }
       }

       private void handleThreat(Context context, String threatCode, long timestamp) {
           if (threatCode == null) return;

           switch (threatCode) {
               case "D10001":
                   // 예: 루팅 탐지 - 사용자 로그아웃 및 세션 무효화
                   break;
               case "D60007":
                   // 예: 서명 인증서 불일치 - 경고 화면 표시
                   break;
               default:
                   // 그 외 지원 위협 코드에 대한 공통 처리
                   break;
           }
       }
   }
   ```

   
   **Kotlin**


   ```kotlin
   package com.company.bank.security

   import android.content.BroadcastReceiver
   import android.content.Context
   import android.content.Intent
   import android.util.Log

   class ThreatCallbackReceiver : BroadcastReceiver() {

       companion object {
           private const val TAG = "ThreatCallbackReceiver"
           private const val ACTION_THREAT_DISCOVERED =
               "com.doverunner.aos.security.action.THREAT_DISCOVERED"
       }

       override fun onReceive(context: Context, intent: Intent?) {
           if (intent == null) return

           if (intent.action == ACTION_THREAT_DISCOVERED) {
               val threatCode = intent.getStringExtra("threat_code")
               val timestamp = intent.getLongExtra("timestamp", 0L)

               Log.i(TAG, "Threat callback received. threat_code=$threatCode, timestamp=$timestamp")

               handleThreat(context, threatCode, timestamp)
           }
       }

       private fun handleThreat(context: Context, threatCode: String?, timestamp: Long) {
           when (threatCode) {
               "D10001" -> {
                   // 예: 루팅 탐지 - 사용자 로그아웃 및 세션 무효화
               }
               "D60007" -> {
                   // 예: 서명 인증서 불일치 - 경고 화면 표시
               }
               else -> {
                   // 그 외 지원 위협 코드에 대한 공통 처리
               }
           }
       }
   }
   ```

   

   **Tip:** 
   `onReceive()`는 메인 스레드에서 실행되므로 빠르게 반환해야 합니다. 서버 보고처럼 시간이 걸리는 작업은 WorkManager로 넘기거나 `goAsync()`를 사용하세요.
   


## 테스트

[샘플 앱](#샘플-앱-소스-코드) 또는 리시버를 연동한 자체 빌드를 사용하여 콜백이 정상적으로 전달되는지 확인합니다.


1. 샘플 앱 또는 리시버를 연동한 앱 빌드를 설치합니다.
2. 테스트할 탐지 항목을 **콜백**으로 설정하여 실링한 앱을 설치합니다.
3. 루팅된 기기나 에뮬레이터에서 앱을 실행하는 등의 방법으로 지원 대상 탐지를 발생시킵니다.
4. 다음 명령으로 로그를 확인합니다.

   ```bash
   adb logcat -s ThreatCallbackReceiver
   ```

   콜백이 수신되면 다음과 같은 로그가 출력됩니다.

   ```
   Threat callback received. threat_code=D10001, timestamp=1720000000000
   ```


## 동작 참고 사항

- 콜백은 실링 시 동작이 **콜백**으로 설정된 탐지 항목에 대해서만 전달됩니다.
- 위 샘플 코드처럼, 알 수 없는 `threat_code` 값은 기본(default) 분기에서 처리하세요.
- 코드 후킹 탐지는 콜백을 발생시키지 않습니다. 항상 기본 보호 동작(경고, 앱 종료, 보고)을 따릅니다.
- 동일한 `threat_code`가 여러 번 전달될 수 있습니다. 필요에 따라 `threat_code`와 `timestamp`를 기준으로 중복을 제거하세요.
- 서명 인증서 불일치(`D60007`)는 설치된 앱의 서명 인증서가 예상 인증서와 다를 때마다 콜백을 발생시킵니다.

## 위협 대응 코드

아래 표는 **콜백**을 선택할 수 있는 실링 위저드 옵션별로, 대응하는 CLI `action_*` 옵션과 `threat_code`로 전달되는 위협 대응 코드를 나타냅니다.

| 실링 위저드 옵션 | CLI 옵션 | 탐지 대상 | 위협 대응 코드 |
| :--------------- | :------- | :-------- | :------------- |
| 무결성 검사 | `action_app_integrity` | 앱 코드 또는 바이너리 변조, APK 리패키징 또는 재서명 | `D21003`, `D40001`, `D50001`, `D60001`, `D60002`, `D60004`, `D60005`, `D60006`, `D60007` |
| 루팅 | `action_rooting` | 루트 권한 | `D10001`, `D10002`, `D10003`, `D14001` |
| 개발자옵션 | `action_developer_options` | 개발자 옵션 활성화 | `D13001` |
| USB 디버깅 | `action_usb_debugging` | ADB / USB 디버깅 활성화 | `D13002` |
| 에뮬레이터 | `action_emulator` | 에뮬레이터 환경 | `D11001` |
| 직장 프로필 및 삼성 보안 폴더 | `action_work_profile` | 직장 프로필 또는 삼성 보안 폴더에서의 앱 실행 | `D12001` |
| 매크로 도구 | `action_macro` | 매크로 자동화 도구 | `D21001`, `D21002`, `D30001`, `D30002`, `D30003`, `D30004` |
| 패킷 공격 | `action_sniff` | 패킷 공격 도구 | `D21001`, `D21002`, `D30001`, `D30002`, `D30003`, `D30004` |
| 키로거 | `action_keylogger` | 키로거 앱 | `D21001`, `D21002`, `D30001`, `D30002`, `D30003`, `D30004` |
| 오버레이 윈도우 | `action_overlay` | 다른 앱의 오버레이 윈도우 | `D60003` |

**Note:** 
매크로 도구, 패킷 공격, 키로거는 같은 위협 대응 코드를 공유하므로, `threat_code`만으로는 세 탐지 중 어느 것이 콜백을 발생시켰는지 구분할 수 없습니다. 탐지별로 다르게 처리해야 한다면 필요한 탐지 항목만 **콜백**으로 설정하세요.


## 샘플 앱 소스 코드

위협 콜백 연동 및 수신 방법을 보여 주는 샘플 앱을 참고 및 테스트 용도로 제공합니다.

[샘플 앱 소스 코드 다운로드 (ZIP)](https://drive.google.com/file/d/1Ho6iLaoN5bEVOlUjiJ3fo6OjL81ohw4W/view?usp=sharing)

**Caution:** 
샘플 앱 링크는 사전 안내 없이 변경되거나 더 이상 유효하지 않을 수 있습니다. 링크가 동작하지 않는데 샘플이 필요하다면 [헬프 센터](https://support.doverunner.com)로 문의해 주세요.
