QR Bank Research

2026-10-18 · Android 정적 분석 · 제품 구현 전 조사

19개 은행 중 17개 APK 확보·매니페스트 해독·서명 검증. REA/JADX로 진입점과 관련 코드를 추적했다. 실기기 실행 검증은 0개다. 17/19는 조사 APK 확보율 89.5%이며 앱 실행 성공률이나 이용자 점유율이 아니다.

목표와 결론

하나의 매장 QR로 공통 웹페이지를 열고, 사용자가 선택한 송금 앱에서 가능한 가장 깊은 단계로 연결한다. 계좌 복사·앱 실행은 최소 기능이며, 은행별로 이체 화면·수취 계좌·금액 입력까지 조사했다. 은행 버튼은 보낼 앱을 선택하는 것이며, 전달하는 수취은행과 계좌는 매장의 값으로 유지한다. 토스·부산에는 수취 계좌와 금액을 처리하는 코드, KB·IBK·신한에는 수취 계좌·금액 전달 후보, 경남에는 수취 계좌 전달 후보가 있다. 카카오뱅크·농협·우리·산업·수협·전북·SC에서도 구체적인 이체 화면 연결 근거를 확보했다. 카카오페이는 은행 19개와 별도로 공식 계좌·금액 앱링크 규약을 확인했다. 나머지도 단순 스킴 목록이 아니라 실제 수신 처리부/화면 전달부 또는 보호 코드 경계까지 기록했다.

수치의 의미

19개 중 외부 앱 진입 근거는 APK 17개와 하나 공식 웹을 합쳐 18개다. 특정 이체 대상까지 조합한 경로는 13개, 이 중 수취계좌 직접 전달 후보는 6개(토스·부산·KB·IBK·신한·경남)다. 코드의 완성도는 표에 따로 구분했다. 산업은행의 구형 화면 클립보드 입력 경로는 별도 대안이며 직접 쿼리 전달 6개에 넣지 않았다. 실기기 성공은 모든 단계에서 0개다. 80% 기준은 최소 16개 은행이 실제로 해당 동작에 성공해야 충족한다.

웹만으로 구성할 수 있는가

Android Chrome은 사용자 클릭으로 BROWSABLE 앱 진입점을 여는 intent 링크와 설치되지 않았을 때의 대체 URL을 지원한다. HTTPS 웹에서 계좌 복사도 가능하다. 따라서 QR→공통 HTTPS 페이지→은행 선택→가장 깊은 확인 경로, 그리고 계좌 복사 기능이라는 구조를 위해 별도 사용자용 앱이 반드시 필요하지는 않다. 그러나 앱 내부에서 받지 않는 수취 인자를 웹이나 별도 앱이 임의로 채울 수 있다는 뜻은 아니다. iOS는 은행별 별도 검증 범위다.

Chrome 공식 Intent 문서 · Clipboard writeText 문서

첨부 토스 QR

supertoss://send?amount=0&bank=신한은행&accountNo=[마스킹]&origin=qr

macOS Vision으로 사용자 첨부 PNG를 해독했다. 토스 앱에서 신한은행 수취 계좌로 보내는 링크다. 원문은 프로젝트 evidence/qr에 보관했고 이 보고서에는 실제 계좌번호를 넣지 않았다. APK 수취 화면에서 accountNo, bank 또는 bankCode, 양수 amount 처리 코드를 확인했다. 금액 0을 양수 금액으로 취급하지 않는 분기도 확인했다.

카카오의 서로 다른 경로

카카오페이 공식 계좌송금 앱링크 가이드에는 계좌+금액, 계좌만, 빈 이체 화면의 세 동작이 명시되어 있다. 알림톡 문서의 alimtalk= 접두어를 일반 브라우저 링크에 그대로 붙이지 않는다. 공식 웹 브리지의 /t/ 경로는 카카오톡 내 카카오페이로 연결한다. 카카오뱅크는 별도 APK에서 widget 및 multiple_transfer 명령을 확인했다.

은행별 결과

‘전달’은 코드상 데이터 흐름을 뜻한다. 서버가 수락하고 실제 화면에 채워지는지는 실행 검증이 필요하다. ‘미확정’은 기능이 없다는 뜻이 아니다.

은행/앱근거확인 깊이진입점/후보남은 경계
KB국민APK이체 화면 + 수취계좌/금액 전달 후보kbbank://call?id=qr&cmd=move_to&url={encoded /mquics?page=D001474}&urlparam={encoded fields}외부 urlparam→Bundle와 클립보드→D001474 공통 경로. 입금계좌번호/입금은행코드/입금은행명/이체금액/pReceiveAmtYn=1 확인. 실제 화면 수락 미검증
신한APK+공식 웹이체 화면 + 수취계좌/금액 전달 후보shinhansol://SM0101S0010T01?receiverNumber={계좌}&receiverBankCode={코드}&receiverBankName={은행명}&receiverName={예금주}&amount={금액}공식 link.html→외부 URI→EntryViewModel JSON까지 확인. 공식 내부 JS의 동일 이체 화면 인자와 조합. startMainActivityWithUri 보호 opcode로 로그인 이후 값 보존 미확정
우리APK+공식 웹이체 화면 후보 NWTSW00001_001Mwooribank://call?JSON_DATA={encoded JSON}ACTION_URL/ACTION_PARAM/ROUTE_DATA 전달; 수취 필드 소비 화면은 추가 검증 필요
하나공식 웹화면 주소 전달hanabank-oqf://신규 com.hanabank.oqf APK 미확보; 공식 oneqplus.jsp에서 앱 연결 확인
NH농협APK이체 serviceId NSFTP0101R + 클립보드 파싱newnhsmartbanking://sbbank?DATA={encoded JSON}DATA 저장→NHMain→JS serviceId/params 전달. 실제 이체 화면은 인증 세션 필요; 직접 계좌 인자 미확정
IBK기업APK+공식 웹이체 화면 + 수취은행/계좌/금액 전달 후보ionebank://openmenu?menuid=trn_trn_000100_1&mnrc_bncd={코드}&mnrc_acno={계좌}&tran_amt={금액}공식 openmenu 생성자 + APK 내장 goLogin 예제 + 공통 MenuData.param 경로. param으로 감싸지 않고 최상위 query로 전달. 현재 서버 메뉴·필수 필드·실제 화면 미검증
SC제일APK+공식 웹계좌이체 경로 + 클립보드 읽기/파서accom.scbank.ma30://ma30sso?t=redirect&goTo=%2Ftransfer%2FMA3TRNSTR001.mnu공식 HTML의 이체 경로와 앱 링크 생성자 결합. 공개 GET은 메뉴 ID 보존 후 로그인/앱 전용 안내. 수취 인자 및 클립보드→이체 필드 연결 미확정
씨티APK외부 URI와 쿼리 5개 파싱citimobileapp://sms 또는 citimobileapp://smartpay키 문자열 보호. smartpay를 일반 계좌이체로 간주하지 않음
KDB산업APK+공식 웹현재 건별이체 화면 + 별도 구형 화면의 클립보드 선입력 후보smartkdb://go_page?COM_ID=MPTFQT04N10현재 화면의 계좌 필드는 PAGE_DATA_MAIN에서 소비되나 외부 쿼리 연결 미확정. 별도 MPTFCO04N10은 비밀번호/계좌 검사와 사용자 확인 후 클립보드 계좌·금액을 입력하는 코드 존재. 구형 경로 실행 가능성은 미검증
Sh수협APK+공식 웹계좌이체 메뉴 PSM00162suhyuppsmb://suhyuplink?menuId=PSM00162공식 전체메뉴에서 계좌이체 ID 확인. linkParam JSON 추가 전달 가능. 공개 GET은 로그인으로 이동; 수취 계좌 필드 미확정
iMAPK외부 진입mbanking://openservice 등 등록 host 필요매니페스트의 진입 클래스 본문을 현재 DEX에서 찾지 못함; 보호 코드 경계
부산APK+공식 웹수취은행/계좌/금액의 이체 화면 소비 코드bslink://call?mnu=MBPTWD1000TWD10&DEPS_BNKCD={코드}&DEPS_ACNO={계좌}&TRNF_AMT={금액}ServiceMenu.parameter→parameter2→LOGIN_MENU_PARAM2→TransferFragment 변수 연결. smali 분기 대조. 로그인·현재 메뉴표·확인 상태 XF1.w() 및 실행 미검증
경남APK+공식 웹이체 화면 + 수취은행/계좌 전달 후보alopex://psb?url={encoded URL}경로 knsb/sbp/trn/SBPTRNS10IPTV00M.html; RCV_BK_CD/RCPE_RCV_ACT_NO. 정적 경로 조합이며 실기기/서버 수락 미검증; 금액 키 미확정
광주APK+공식 웹공식 웹의 화면ID+JSON 전달kjbank://asb?u={encoded path and JSON}APK 진입점은 등록되어 있으나 본문은 패킹 경계. 수취 필드 미확정
전북APK+공식 웹계좌이체 화면 P_M_TRN219001_91jbprivatebank://jbprivate?byPassData={encoded JSON action/data}공식 생성자가 {action,data} 평문 JSON을 인코딩. APK→JS JSON.parse→로그인 검사→form.move. 공개 화면은 SVC_LOGIN_CHECK; 수취 계좌 필드 미확정
제주APK 미확보미확정미확정com.jejubank.smartnew 공개 다운로드 경로 확보 실패; 불가능 판정 아님
카카오뱅크APK로그인 후 계좌이체 화면kakaobank://widget?type=transfer_openwidget/transfer_open 문자열 정적 복호화→ACCOUNT 모드→CanI 접근 검사. 다른 type=transfer 경로도 있으나 raw 계좌번호 전달 확인 못 함; seqNo 의미 미확정
케이뱅크APK+공식 웹메뉴ID와 쿼리를 앱 WebView 주소로 전달ukbanksmartbankweb://?{JSON}DeepLinkViewModel.makeSchemeUrl; 이체 메뉴 및 일부 보호된 JSON 키 미확정
토스사용자 QR+APK수취은행/계좌/금액 소비 코드supertoss://send?bank={은행명}&accountNo={계좌}&amount={금액}&origin=qr사용자 QR 해독. bankCode도 처리. 양수 amount를 처리하는 수취 화면 코드, SendActivity→TransferSendActivity 연결. registry의 SendActivity 연결은 확인했으나 암호화된 send 키 평문 연결은 미완료. 실제 실행 미검증
카카오페이 (별도)공식 가이드+공식 웹 JS수취은행/계좌/금액 선입력 규약https://link.kakaopay.com/t/money/to/bank?bank_code={코드}&bank_account_number={계좌}&amount={금액}공식 문서는 알림톡 범위. 현재 웹 브리지는 kakaotalk://kakaopay/...로 경로/쿼리 유지. 외부 브라우저 실기기 검증 필요

기존 유사 서비스

LinkTo 공개 데모와 공개 transfer-apps 응답·클라이언트 생성자를 확인했다. 토스·카카오페이에는 계좌와 금액 템플릿이 있으나, 다른 항목 상당수는 앱 스킴만 있고 일부는 enabled=true여도 스킴 값이 없다. 표시된 은행 수를 실제 이체 화면 또는 계좌 자동입력 지원 수로 세면 안 된다. 이 조사는 별도로 현재 APK의 host 조건과 실제 소비 코드를 대조했다.

재현과 증거

분석 방법과 한계

REA 6.5.0 + 고정된 JADX headless 0.7.1 JAR + JDK17을 사용했다. 보존된 REA package 결과 13개 중 8개는 성공 Evidence, 5개는 오류 기록이며, 실패 또는 미실행 건은 직접 JADX/AXML 조사와 구분했다. 따라서 17개를 모두 REA로 완전 복원했다는 의미가 아니다. 큰 토스 APK에서 REA 시간 제한과 4GB 메모리 한계를 만났고, 같은 JADX 엔진의 직접 API를 8GB로 사용해 관련 Java와 smali를 추출했다. 직접 추출 결과를 REA Evidence로 표시하지 않았다. 난독화/Java 복원 오류는 원본 smali와 함께 남겼다. 문자열 복호화는 상수와 알고리즘을 별도 코드로 재현했으며 대상 앱 초기화 코드는 실행하지 않았다.

일부 화면은 서버에서 로그인 후 내려오거나 앱이 실행될 때 보호된 코드를 풀어 사용한다. 농협의 정확한 이체 화면 공개 GET은 인증 세션 오류를 반환했다. 해당 화면의 필드 소비 여부는 추측하지 않았다. 하나·제주는 추가 공개 경로 약 30개까지 조사했으나 APK를 확보하지 못했다. 하나의 현재 앱 실행 스킴은 공식 웹에서 별도 확인했다. 제주의 현재 패키지는 확인했으나 실행 스킴은 미확정이다. 사용자 기기 로그인·송금·은행 앱 설치는 수행하지 않았다.

실행 검증 기준

  1. QR→HTTPS 페이지→클릭→Android Chrome에서 앱 실행. ADB 직접 Activity 호출 성공과 구분.
  2. 로그아웃/로그인 상태, 앱 종료/실행 중 상태에서 목적 화면과 전달값 유지 확인.
  3. 수취은행·계좌·금액 표시 확인 후 최종 송금 전 중단. 실제 송금 완료나 자동 송금 기능으로 오인하지 않음.
  4. 미설치 시 스토어 안내, 붙여넣기 실패 시 복사값 유지. 버전별 결과 기록.

구현 시작 전 남은 항목은 bank-matrix.json과 은행별 findings의 remaining/limitations에 기록했다.