앨범저장소 상세 분석 보고서

app_7179 (앨범저장소) 양자 나노 단위 분석 종합 보고서

분석일: 2026-02-21

분석자: 주식회사 아크링크 (Deep-Coding 보안연구소)

분석 목적: 피싱/스미싱 [피해자 정보 비공개] 위한 악성앱 심층 분석

보고서 버전: v2.0 (심층 바이너리 + 행위 분석 반영)

위협 등급: CRITICAL

목차

1. [개요](#1-개요)

2. [정적 분석](#2-정적-분석)

3. [패킹/보호 분석](#3-패킹보호-분석)

4. [코드 구조 분석 (JS Bundle)](#4-코드-구조-분석-js-bundle)

5. [C2 서버 분석](#5-c2-서버-분석)

6. [데이터 수집 분석](#6-데이터-수집-분석)

7. [권한 분석](#7-권한-분석)

8. [네트워크 통신](#8-네트워크-통신)

9. [동적 분석 결과](#9-동적-분석-결과)

10. [IOC (Indicators of Compromise)](#10-ioc-indicators-of-compromise)

10.5. [심층 바이너리 + 행위 분석 (v2.0)](#105-심층-바이너리--행위-분석-v20)

10.6. [🔬 기법 점검](#106--기법-점검)

11. [결론](#11-결론)

1. 개요

1.1 분석 목적

본 보고서는 "앨범저장소"라는 표시명으로 위장한 악성앱 app_7179에 대한 양자 나노 단위 분석 결과를 정리한 것이다. "양자 나노 단위 분석"이란 앱의 JS 번들 코드, 네이티브 라이브러리, 매니페스트 구성, 런타임 동작까지 가장 작은 단위로 분해하여 동작 원리를 완전히 파악하는 심층 분석 기법이다.

1.2 분석 대상

항목 · 내용

**앱 ID** · app_7179

**표시명** · 앨범저장소

**위장 제목** · 클래스룸 (Classroom) - 내비게이션 바 타이틀

**패키지명** · allOkkenTeighteen.andPk18.andrDAItaa

**UniApp ID** · __UNI__77338A0

**버전** · 0.6.2 (versionCode: 62)

**파일 크기** · 14.92 MB (15,606,111 bytes)

**프레임워크** · DCloud UniApp (Weex 기반)

**패커** · BaiduProtect (Sagittarius v6)

**위협 등급** · Critical

1.3 분석 환경

항목 · 내용

**에뮬레이터** · Android 15 (API 35) x86_64

**디컴파일러** · apktool, jadx

**JS 분석** · webpack 번들 수동 역난독화

**동적 분석** · logcat, adb shell

**네트워크 분석** · 트래픽 캡처

1.4 핵심 발견 요약

```

[CRITICAL] 이중 위장 기법: 앱 이름 "앨범저장소" + 내부 타이틀 "클래스룸"

[CRITICAL] GPS 위치정보 수집: onLoad 시점에 자동 수집 (사용자 인지 불가)

[CRITICAL] 초대코드 기반 [피해자 정보 비공개] 시스템

[CRITICAL] C2 서버 URL 암호화 은닉 (app-confusion.js)

[CRITICAL] 연락처, 카메라, 파일, 위치정보 등 광범위 개인정보 탈취

[HIGH] BaiduProtect + UniApp Confusion 이중 보호

[HIGH] 다른 APK 설치 권한 보유 (2차 감염 가능)

[HIGH] 에뮬레이터 탐지 시도 (SELinux에 의해 차단)

[MEDIUM] WeexCore IPC 초기화 실패 (에뮬레이터 환경)

```

1.5 앱 패밀리 식별

본 앱은 allOkken UniApp 패밀리에 속하며, 동일 패밀리의 특징은 다음과 같다:

· 동일한 lib39285EFA.so 네이티브 라이브러리

· BaiduProtect (Sagittarius v6) 패킹

· DCloud UniApp + Weex 프레임워크

· 39285EFA.dex 암호화 DEX

· 초대코드(邀请码) 기반 인증 시스템

· plus.confusion 필드를 통한 설정값 암호화

2. 정적 분석

2.1 APK 구조 분석

```

app_7179.apk (15,606,111 bytes)

├── AndroidManifest.xml

├── META-INF/

│ └── CERT.RSA, CERT.SF, MANIFEST.MF

├── classes.dex (5.6 MB) ← DCloud 프레임워크 코드

├── classes2.dex (5.9 MB) ← Weex SDK + 부가 라이브러리

├── 39285EFA.dex (71 KB) ← 암호화된 커스텀 DEX

├── baiduprotect1.jar.i.dex (1.6 MB) ← BaiduProtect 암호화 JAR #1

├── baiduprotect2.jar.i.dex (320 KB) ← BaiduProtect 암호화 JAR #2

├── baiduprotect3.jar.i.dex (91 KB) ← BaiduProtect 암호화 JAR #3

├── assets/

│ ├── apps/__UNI__77338A0/www/

│ │ ├── manifest.json ← UniApp 매니페스트

│ │ ├── app-service.js ← 메인 JS 번들 (웹팩)

│ │ ├── app-config.js ← 라우팅 설정

│ │ ├── app-config-service.js ← 서비스 설정

│ │ ├── app-confusion.js ← 암호화된 설정 (C2 URL 포함!)

│ │ └── __uniappview.html ← WebView 진입점

│ └── data/

│ └── dcloud_*.xml ← DCloud 설정 파일들

├── lib/

│ ├── arm64-v8a/

│ │ ├── libbaiduprotect.so (502 KB) ← 패커 네이티브

│ │ ├── lib39285EFA.so ← 암호화 처리 SO

│ │ ├── libweexcore.so ← Weex 코어 엔진

│ │ ├── libweexjss.so (13.5 MB) ← Weex JS 엔진

│ │ ├── libweexjsb.so ← Weex JS 브릿지

│ │ ├── libc++_shared.so ← C++ 런타임

│ │ └── libbreakpad-core.so ← 크래시 리포터

│ └── x86_64/ (동일 구조)

└── res/ (리소스 파일들)

```

2.2 DEX 파일 구성 분석

DEX 파일 · 크기 · 역할 · 암호화

classes.dex · 5.6 MB · DCloud UniApp SDK, io.dcloud.* · 비암호화

classes2.dex · 5.9 MB · Weex SDK, com.taobao.weex.* · 비암호화

39285EFA.dex · 71 KB · 패밀리 고유 암호화 로직 · 부분 암호화

baiduprotect1.jar.i.dex · 1.6 MB · BaiduProtect 런타임 #1 · 완전 암호화

baiduprotect2.jar.i.dex · 320 KB · BaiduProtect 런타임 #2 · 완전 암호화

baiduprotect3.jar.i.dex · 91 KB · BaiduProtect 런타임 #3 · 완전 암호화

총 DEX 파일: 7개

총 DEX 크기: 약 14.0 MB (APK 전체의 89.7%)

2.3 네이티브 라이브러리 분석

SO 파일 · 크기 · 역할

libweexjss.so · **13.5 MB** · Weex JavaScript 엔진 (V8 기반)

libbaiduprotect.so · 502 KB · BaiduProtect 패커 런타임

lib39285EFA.so · - · 패밀리 고유 암호화/복호화 엔진

libweexcore.so · - · Weex 코어 렌더링 엔진

libweexjsb.so · - · Weex JS 브릿지 (IPC 통신)

libbreakpad-core.so · - · Google Breakpad 크래시 리포터

libc++_shared.so · - · C++ 표준 라이브러리

총 SO 파일: 18개 (arm64-v8a + x86_64 쌍으로 구성)

주목할 점: libweexjss.so가 13.5MB로 전체 APK의 86.5%를 차지한다. 이는 완전한 V8 JavaScript 엔진을 내장하고 있음을 의미하며, 별도의 프로세스(WeexJSBridgeThread)에서 JS를 실행하는 Weex의 멀티프로세스 아키텍처를 지원한다.

2.4 AndroidManifest.xml 주요 설정

```xml

<!-- 패커 스텁 애플리케이션 -->

<application

android:name="com.sagittarius.v6.StubApplication"

android:label="앨범저장소"

android:allowBackup="true"

android:networkSecurityConfig="@xml/network_security_config"

android:usesCleartextTraffic="true">

<!-- 메인 진입점 -->

<activity

android:name="io.dcloud.PandoraEntry"

android:theme="@style/TranslucentTheme"

android:launchMode="singleTask">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<!-- 실제 WebApp 액티비티 -->

<activity

android:name="io.dcloud.PandoraEntryActivity"

android:launchMode="singleTask"

android:configChanges="..." />

</application>

```

2.5 이중 위장 기법 분석

본 앱은 이중 위장(Double Disguise) 기법을 사용한다:

```

┌─────────────────────────────────────────────────────────┐

│ 제1 위장: 앱 이름 │

│ ├─ AndroidManifest: android:label="앨범저장소" │

│ └─ 사용자 인식: "앨범 관련 앱이구나" │

├─────────────────────────────────────────────────────────┤

│ 제2 위장: 앱 내부 UI │

│ ├─ app-config-service.js: │

│ │ navigationBarTitleText: "클래스룸" │

│ ├─ 모든 페이지 (home, index) 동일 타이틀 │

│ └─ 사용자 인식: "교육용 앱이구나" (내부 진입 시) │

├─────────────────────────────────────────────────────────┤

│ 실제 목적: 개인정보 탈취 (연락처, GPS, 이미지 등) │

└─────────────────────────────────────────────────────────┘

```

이 기법의 의도는 다음과 같다:

1. 앨범저장소: 앱 설치 시 사진 접근 권한 요청을 자연스럽게 유도

2. 클래스룸: 앱 실행 후 "교육 서비스" 분위기를 연출하여 초대코드 입력을 유도

3. 실제 기능: 초대코드 확인 후 연락처, GPS, 이미지 등 개인정보 일괄 탈취

3. 패킹/보호 분석

3.1 BaiduProtect (Sagittarius v6)

본 앱은 BaiduProtect 패커의 Sagittarius v6 버전을 사용한다.

```

패킹 체인:

┌──────────────────────────────────────────────────────┐

│ 1. StubApplication (com.sagittarius.v6) │

│ └─ 앱 시작 시 최초 진입 │

│ │

│ 2. libbaiduprotect.so 로드 (502KB) │

│ └─ 네이티브 언패킹 엔진 초기화 │

│ │

│ 3. 암호화된 DEX 복호화 │

│ ├─ baiduprotect1.jar.i.dex (1.6 MB) → 메모리 로드 │

│ ├─ baiduprotect2.jar.i.dex (320 KB) → 메모리 로드 │

│ └─ baiduprotect3.jar.i.dex (91 KB) → 메모리 로드 │

│ │

│ 4. 실제 Application 클래스 교체 │

│ └─ DCloudApplication.onCreate() 호출 │

│ │

│ 5. UniApp 엔진 초기화 │

│ └─ WeexSDK, DCloud 플러그인 로드 │

└──────────────────────────────────────────────────────┘

```

logcat 증거:

```

02-21 05:31:20.244 libbaiduprotect.so ... ok

02-21 05:31:20.400 com.sagittarius.v6.StubApplication.onCreate(Unknown Source:35)

02-21 05:31:20.400 io.dcloud.application.DCloudApplication.onCreate(SourceFile:2)

```

3.2 UniApp Confusion 암호화

DCloud UniApp 플랫폼 고유의 Confusion 암호화 기능을 사용하여 핵심 설정값을 보호한다.

```json

// manifest.json 내 plus.confusion 필드

"confusion": "BQ81KwABAAMAAACQMhpt6HtgF3mZNiqj1MnH8lHG..."

```

이 암호화 blob은 다음 정보를 포함하는 것으로 추정된다:

· C2 서버 URL (HTTP.API_HOST)

· API 타임아웃 설정 (HTTP.API_TIME_OUT)

· 기타 설정값

app-confusion.js 파일은 완전한 바이너리 데이터로 구성되어 있으며, 런타임에 lib39285EFA.so를 통해 복호화된다.

3.3 lib39285EFA.so 암호화 엔진

```

암호화 엔진 구조:

├─ lib39285EFA.so ← 네이티브 복호화 라이브러리

├─ 39285EFA.dex ← 관련 Java/Kotlin 인터페이스

└─ app-confusion.js ← 암호화된 설정 데이터

복호화 흐름:

1. StubApplication이 lib39285EFA.so 로드

2. 39285EFA.dex의 JNI 인터페이스를 통해 복호화 호출

3. app-confusion.js의 바이너리 데이터를 메모리에서 복호화

4. 복호화된 HTTP 설정을 JS 런타임에 주입

5. webpack 모듈 a5fe로 HTTP.API_HOST 노출

```

logcat 증거:

```

02-21 05:31:20.494 lib39285EFA.so ... ok

```

3.4 에뮬레이터 탐지 메커니즘

앱은 최소 2가지 에뮬레이터 탐지를 시도한다:

3.4.1 QEMU 하드웨어 키 프로퍼티 체크

```

탐지 대상: qemu.hw.mainkeys

방법: System Property 읽기 시도

결과: SELinux에 의해 차단 (AVC denied)

```

logcat 증거:

```

02-21 05:31:21.858 Access denied finding property "qemu.hw.mainkeys"

02-21 05:31:21.856 avc: denied { read } for name="u:object_r:qemu_hw_prop:s0"

scontext=u:r:untrusted_app:s0:c200,c256,c512,c768

tcontext=u:object_r:qemu_hw_prop:s0 tclass=file permissive=0

```

3.4.2 SELinux 상태 체크

```

탐지 대상: getenforce 실행

방법: 별도 프로세스 포크하여 SELinux 모드 확인

결과: SELinux에 의해 차단 (AVC denied)

```

logcat 증거:

```

02-21 05:31:20.640 getenforce: avc: denied { read } for name="enforce"

dev="selinuxfs" ino=4

scontext=u:r:untrusted_app:s0:c200,c256,c512,c768

tcontext=u:object_r:selinuxfs:s0 tclass=file permissive=0

```

3.4.3 디버거 접근 가능성 체크

```

탐지 대상: ro.debuggable 프로퍼티

방법: System Property 읽기

결과: 접근 거부

```

logcat 증거:

```

02-21 05:31:20.725 Access denied finding property "ro.debuggable"

02-21 05:31:20.720 avc: denied { read } for name="u:object_r:userdebug_or_eng_prop:s0"

```

3.5 보호 메커니즘 요약

보호 기법 · 수준 · 상태

BaiduProtect Sagittarius v6 · 고급 · 활성 (DEX 암호화)

UniApp Confusion 암호화 · 중급 · 활성 (C2 URL 은닉)

lib39285EFA.so 네이티브 암호화 · 중급 · 활성 (복호화 엔진)

에뮬레이터 탐지 (qemu.hw.mainkeys) · 기본 · **차단됨** (SELinux)

SELinux 상태 체크 · 기본 · **차단됨** (SELinux)

디버거 탐지 (ro.debuggable) · 기본 · **차단됨** (SELinux)

4. 코드 구조 분석 (JS Bundle)

4.1 Webpack 모듈 아키텍처

app-service.js는 Webpack 번들로 패키징된 UniApp 애플리케이션이다. 역난독화를 통해 다음 모듈 구조를 식별하였다:

```

webpack 모듈 의존성 트리:

4113 (엔트리포인트 - App 초기화)

├── 7ca3 (defineProperty 헬퍼)

├── 9e04 (Promise polyfill + __definePage 등록)

├── 897b (App.vue 컴포넌트)

│ └── 0fb5 → ef39 (App 라이프사이클: onLaunch, onShow, onHide)

├── 951c (Vue 런타임)

├── 4a45 (uni.addInterceptor - returnValue 래핑)

├── 11dd (★ $H 헬퍼 모듈 - 핵심 C2 통신)

├── [페이지 등록]

│ ├── 8d83 → pages/home/home (스플래시 + GPS 수집)

│ │ ├── cd1e (home 뷰 템플릿)

│ │ └── 16c0 → c3de (home 로직: getLocation + $H.init())

│ │

│ └── a7a1 → pages/index/index (초대코드 입력)

│ ├── 1d93 (index 뷰 템플릿)

│ └── b75f → 7a02 (index 로직: yqm 입력 + $H.set())

└── b0f3 (HTTP 요청 유틸리티)

└── a5fe (★ 암호화된 설정 - HTTP.API_HOST, app-confusion.js에서 제공)

헬퍼 모듈:

├── 828b (SFC 컴포넌트 래퍼)

├── 47a9 (interopRequireDefault)

├── 3b2d (typeof 헬퍼)

├── d551 (toPrimitive 변환)

├── e6db (toPrimitive 구현)

└── f3b9 (로깅 유틸리티 - 채널/디버그 분기)

```

4.2 페이지 구조 분석

앱은 단 2개의 페이지로 구성되어 있다:

```

페이지 라우팅:

┌──────────────────────────────────────────────────────────┐

│ pages/home/home (스플래시 페이지) │

│ ├─ isQuit: true (뒤로가기 시 앱 종료) │

│ ├─ navigationStyle: "custom" (시스템 네비바 숨김) │

│ ├─ navigationBarTitleText: "클래스룸" │

│ │ │

│ ├─ [UI 구성] │

│ │ └─ splash 클래스의 view + image (로딩 화면) │

│ │ │

│ ├─ [onLoad 시 자동 실행] │

│ │ ├─ uni.getLocation({type:"wgs84"}) ← GPS 수집!! │

│ │ └─ $H.init() ← C2 서버 초기화 │

│ │ │

│ └─ → 초기화 완료 후 index 페이지로 이동 │

├──────────────────────────────────────────────────────────┤

│ pages/index/index (초대코드 입력 페이지) │

│ ├─ navigationStyle: "custom" │

│ ├─ navigationBarTitleText: "클래스룸" │

│ │ │

│ ├─ [UI 구성] │

│ │ ├─ img_top: 상단 이미지 (로고/안내) │

│ │ ├─ info: 정보 표시 영역 │

│ │ ├─ info bg: 배경 + input 필드 (초대코드 입력) │

│ │ └─ img_buttom: 하단 이미지 (제출 버튼) │

│ │ │

│ ├─ [데이터] │

│ │ ├─ uid: "" (사용자 ID, onLoad 파라미터) │

│ │ ├─ yqm: "" (邀请码/초대코드) │

│ │ └─ isTouch: false (터치 상태) │

│ │ │

│ ├─ [메소드] │

│ │ ├─ Input(e): yqm = e.target.value │

│ │ └─ set(): this.$H.set(this.yqm, this) │

│ │ └─ 초대코드를 C2 서버에 전송! │

│ │ │

│ └─ onLoad(t): this.uid = t.uid │

└──────────────────────────────────────────────────────────┘

```

4.3 모듈 ef39 - App 라이프사이클

```javascript

// 모듈 ef39: App.vue 라이프사이클

{

onLaunch: function() { console.log("App Launch") },

onShow: function() { console.log("App Show") },

onHide: function() { console.log("App Hide") }

}

```

단순한 로깅만 수행하며, 별다른 초기화 로직은 없다. 실제 악성 로직은 onLoad 단계에서 실행된다.

4.4 모듈 c3de - home 페이지 (GPS 수집)

```javascript

// 모듈 c3de: pages/home/home 로직

var o = require("11dd"); // $H 헬퍼 모듈

{

data: function() { return {} },

onLoad: function() {

// ★ GPS 위치정보 즉시 수집!!

uni.getLocation({

type: "wgs84", // WGS84 좌표계 (표준 GPS)

success: function(t) {

// 성공 시 위치 데이터 확보

// t.latitude, t.longitude, t.accuracy 등

},

fail: function(t) {

// 실패해도 무시 (권한 미부여 시)

}

});

// ★ C2 서버 초기화

o.default.init();

}

}

```

위험성 분석:

· uni.getLocation은 onLoad 시점에 즉시 호출됨

· 사용자에게 별도의 동의 없이 GPS 위치를 수집 시도

· WGS84 좌표계 사용 (전 세계 표준 GPS 좌표)

· 성공/실패 핸들러 모두 빈 함수 → 결과는 별도 메커니즘으로 처리 추정

4.5 모듈 7a02 - index 페이지 (초대코드)

```javascript

// 모듈 7a02: pages/index/index 로직

{

data: function() {

return {

uid: "", // 사용자 식별자

yqm: "", // 邀请码 (초대코드)

isTouch: false // 터치 상태

}

},

onLoad: function(t) {

this.uid = t.uid; // 라우트 파라미터에서 uid 수신

},

methods: {

Input: function(t) {

this.yqm = t.target.value; // 입력값 바인딩

},

set: function() {

// ★ 초대코드를 C2 서버에 전송!!

this.$H.set(this.yqm, this);

}

}

}

```

변수 yqm의 의미: 중국어 "邀请码"(yāoqǐng mǎ)의 병음 약자로, "초대코드"를 의미한다.

4.6 모듈 b0f3 - HTTP 요청 유틸리티

```javascript

// 모듈 b0f3: utils/request.js

var r = require("a5fe"); // ★ 암호화된 설정 (app-confusion.js에서 로드)

exports.request = function(method, path, data) {

data = data || {};

return new Promise(function(resolve, reject) {

uni.request({

method: method,

url: r.HTTP.API_HOST + path, // ★ C2 서버 URL + API 경로

data: data,

timeout: r.HTTP.API_TIME_OUT, // ★ 타임아웃 설정

success: function(response) {

console.log(response, " at utils/request.js:11");

if (200 === response.statusCode) {

var code = response.data.code;

switch(code) {

// 응답 코드별 처리 (비어있음 - 모든 코드 통과)

}

resolve(response.data);

} else {

reject(new Error(response.statusCode));

}

},

fail: function(error) {

console.log(error, " at utils/request.js:24");

reject(error);

}

});

});

};

```

핵심 포인트:

1. r.HTTP.API_HOST - C2 서버 URL은 모듈 a5fe에서 제공하며, 이 모듈은 app-confusion.js에서 복호화된다

2. timeout: 60000 (60초) - app-config-service.js의 networkTimeout.request 값

3. switch(code) {} - 응답 코드 분기가 비어있어 모든 서버 응답을 그대로 전달

4. 에러 핸들링이 최소한 → 실패해도 조용히 무시

4.7 모듈 a5fe - 암호화된 C2 설정

```

모듈 a5fe는 app-confusion.js에서 로드됨

├─ app-confusion.js: 완전 암호화된 바이너리 데이터 (약 2KB)

├─ 복호화: lib39285EFA.so 네이티브 엔진이 런타임에 수행

└─ 복호화 결과 (추정):

{

HTTP: {

API_HOST: "[비공개]]/[PATH]",

API_TIME_OUT: 60000

}

}

```

C2 URL을 암호화하는 이유:

· 정적 분석으로 C2 서버를 직접 식별하는 것을 방지

· APK를 디컴파일해도 서버 주소가 노출되지 않음

· 런타임에만 복호화되므로 시그니처 기반 탐지 회피

4.8 모듈 11dd - $H 헬퍼 (핵심 C2 통신)

$H 헬퍼는 Vue 프로토타입에 바인딩되어 모든 컴포넌트에서 접근 가능하다:

```javascript

// 엔트리포인트 (모듈 4113)

var a = require("11dd");

Vue.prototype.$H = a.default; // ★ 전역 $H 헬퍼 등록

```

$H는 최소 2개의 메소드를 노출한다:

· $H.init() - C2 서버 초기화 (home 페이지 onLoad에서 호출)

· $H.set(yqm, context) - 초대코드를 C2 서버에 전송 (index 페이지에서 호출)

모듈 11dd의 실제 구현은 app-confusion.js 또는 BaiduProtect에 의해 암호화된 영역에 존재할 가능성이 높으며, 정적 분석만으로는 전체 로직을 확인할 수 없다.

4.9 app-config-service.js 분석

```javascript

// UniApp 설정

var __uniConfig = {

pages: ["pages/home/home", "pages/index/index"],

window: {

navigationBarTextStyle: "black",

navigationBarTitleText: "클래스룸", // ★ 위장 제목

navigationBarBackgroundColor: "#F8F8F8",

backgroundColor: "#F8F8F8"

},

darkmode: false,

nvueCompiler: "uni-app",

renderer: "auto",

splashscreen: {

alwaysShowBeforeRender: true,

autoclose: false

},

appname: "앨범저장소", // ★ 실제 앱 이름

compilerVersion: "4.45",

entryPagePath: "pages/home/home",

networkTimeout: {

request: 60000, // 60초 요청 타임아웃

connectSocket: 60000,

uploadFile: 60000,

downloadFile: 60000

}

};

본 보고서는 주식회사 아크링크 Deep-Coding 보안연구소가 피싱·몸캠피싱 피해자 구제를 목적으로 작성했습니다. 전체 열람·피해 상담: arklink.co.kr