브라더(Brother) 라벨 프린터는 무려 3가지 App.이 제공됩니다.

PC용 1종, Mobile 용 2종 입니다.

** Android 기준으로 설명합니다. iPhone은 제가 가지고 있지 않고 수정 방법도 모릅니다.

 

그런데 Mobile용에는 한글 글꼴이 단 2종 - 네! 단 2종 / 다른 Mobile app인 iPrint&Label 은 단 1종만 제공이 됩니다.

놀랍지 않나요?

당연하지만 PC용 App.은 시스템 설치된 글꼴 모두 사용할 수 있고, 고해상도 출력도 지원이 됩니다(일부 모델)

여러 이유가 있겠지만 저는 PC App 사용이 더 나을 듯 하지만, 그래도 Mobile app. 사용도 아쉬워 작업을 해 보았습니다.

구글 플레이 사용기에 보면 - 추가 글꼴 요청이 연이어 등록되었지만 제대로 된 답변이 없는 상황입니다. 이전 버전(1)에서는 되었는데 2가 나오면서 막힌듯 하더군요.

 

결과를 먼저 소개하면 아래와 같이 추가 글꼴 사용은 가능합니다

당연히 출력도 확인이 되었습니다.










 

개선해 볼 App.은 P-touch Design&Print 2 입니다

과정은 아래와 같이 요약이 되고, 직접 재 수정하실 분은 알아서 잘 하실 것으로 보입니다.

1. APK 추출

SAI(Split APKs Installer) 등을 통해서 이미 설치한 "Design&Print 2"를 "백업" 기능을 통해 추출합니다.

이때 4개 파일 모두 선택 후 / 백업 진행.

 > Design&Print 2 / 기본 APK를 위한 arm64_v8a 코드 / xxhdpi (480DPI) resoucesfor base APK / 한국어 언어

최종으로 Desing&Print 2...... apks 파일이 생성됩니다.

 

2. 단일 APK 생성

1번 과정에서 생성된 apks 파일을 PC로 가져오고, 확장자를 zip으로 수정 후, 내용물을 압축 해제합니다.

아래 URL로 가셔서 단일화 도구 - "SAP (Split APKs Packer) v6.9.0 (Convert Apks To APK)" 를 받습니다.

https://apk4all.com/android/apps/sap-split-apks-packer-windows-linux/ 

적절히(!!) 설치하여 guide 설명과 같이 합치기 과정을 진행합니다.

단! 분명 최종 과정 전에 error가 날 것이고, 그 상태에서 아래 파일만 수정 진행합니다.

파일: AndroidManifest.xml

삭제 문자열: android:localeConfig="@xml/locales_config" 

 

3. 글꼴 파일 추가

시대적 유행으로 무료 글꼴을 여러 단체에서 제공해 주고 있습니다. 저는 아래 사이트에서 미리 보기 후 제가 마음에 드는 글꼴을 다운로드하여 추가했습니다. - 개인 사용에 문제없고, 기왕이면 이상한 제약 없는 글꼴을 사용하세요.

눈누https://noonnu.cc/ 

확인한 결과 TTF / OTF 상관없이 모두 잘 동작 하기에 적절히 선택하셔서 포함하면 됩니다.

앞서 단일 APK 생성 시점에 만들어진 풀어진 package의 아래 위치로 저장하시면 됩니다(다른 기본 font 파일이 존재)

 

\assets\fonts

⛔주의 사항!

App 제약으로 두께는 기본과 BOLD 정도만 구별이 되어서 글꼴 정보에 의해서 추가 두께로 구성한 글꼴이 보이지 않을 수 있습니다. 어떤 경우는 정보가 겹쳐서 한벌만 보이는 현상이 있어서 직접 넣고 확인해야 합니다.

 

4. 재패키징 (디컴파일 하지 않았으므로 재컴파일 단어를 사용하지 않았습니다)

apktool을 이용하여 앞서 단일화 처리된 패키지를 새롭게 APK로 생성합니다.

앞서 단일 APK 생성 과정에서는 decompile을 수행하지 않고 original ***.dex 를 그대로 사용합니다.

apktool 사용이 부담스러우면 여러 GUI frontend를 사용하여 패키징을 완성하면 됩니다.

  *이 과정은 너무나 자세하고 상세한 설명이 인터넷에 있으므로 추가로 언급드리지 않습니다.

 

5. 설치

패키징 결과를 Mobile phone에 설치합니다.

단! - 앞서 재패키징 단계에 의해서 서명 단계를 건너뛰거나 임의 서명을 사용했으므로 기존 설치된 App.은 삭제 후 진행하셔야 합니다!

 

 

일반 사용자를 위한 글꼴이 추가된 sample - apk 패키징 파일

⛔주의 사항!

본 패키지 사용은 개인 사용자를 위해 편의를 위해 sample로 올려 두는 것이며, 이 파일 사용에 대한 결과에 대해서 그 어떠한 보장을 제공하는 것이 아닙니다. 

본 패키지 이용하여 발생하는 법적인 문제에 대해서는 사용하는 당사자들의 책임임을 밝혀 둡니다.
아래 파일에 대해서는 질문에 대해서도 답변을 드리지 않습니다.

 

분할 압축 파일을 모두 받은 후 압축 해제, 생성된 apk 파일을 설치하면 sample을 사용할 수 있습니다.

본 sample은 글꼴 파일 추가 이외에는 일체의 수정 사항이 없습니다.

AdditionalFontsMod.z01
19.00MB
AdditionalFontsMod.z02
19.00MB
AdditionalFontsMod.z03
19.00MB
AdditionalFontsMod.z04
19.00MB
AdditionalFontsMod.z05
19.00MB
AdditionalFontsMod.z06
19.00MB
AdditionalFontsMod.z07
19.00MB
AdditionalFontsMod.zip
4.40MB

 

추가 글꼴(삼립 호빵체)을 이용한 출력 sample

 

Android Brother label printer app. How to add fonts

나름 깔끔한 날씨 프로그램인 eWeather HD를 Android P OS까지 문제없이 사용하고 있었습니다.

그런데 Galaxy S9을 최신 Q OS 업그레이드 직후 eWeather HD 실행, 화면이 보이는 동시에 crash가 나고 죽어 버립니다.

 

죽는 위치는 dump 하면 쉽게 확인됩니다.

JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c

from java.lang.String com.skt.arm.ArmManager.ARMPluginMakeChallenge(java.lang.String)

 

crash log 중요 내용은 아래와 같습니다.

02-17 02:25:19.945 10426 13450 16754 F nt.WeatherCloc: java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c
02-17 02:25:19.945 10426 13450 16754 F nt.WeatherCloc: java_vm_ext.cc:570]     from java.lang.String com.skt.arm.ArmManager.ARMPluginMakeChallenge(java.lang.String)
...
...
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] Runtime aborting...
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] Dumping all threads without mutator lock held
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] All threads:
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] DALVIK THREADS (17):
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] "AsyncTask #1" prio=4 tid=7 Runnable
02-17 02:25:20.220 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | group="" sCount=0 dsCount=0 flags=0 obj=0x12e5fe20 self=0xd6567800
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | sysTid=16754 nice=10 cgrp=default sched=0/0 handle=0xc3208230
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | state=R schedstat=( 33144883 457271 28 ) utm=1 stm=1 core=5 HZ=100
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | stack=0xc3105000-0xc3107000 stackSize=1040KB
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   | held mutexes= "abort lock" "mutator lock"(shared held)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #00 pc 002fe4ef  /apex/com.android.runtime/lib/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+78)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #01 pc 003a768b  /apex/com.android.runtime/lib/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+358)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #02 pc 003a3e63  /apex/com.android.runtime/lib/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+34)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #03 pc 003bc485  /apex/com.android.runtime/lib/libart.so (art::DumpCheckpoint::Run(art::Thread*)+576)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #04 pc 003b70c7  /apex/com.android.runtime/lib/libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+354)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #05 pc 003b67ad  /apex/com.android.runtime/lib/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool)+1416)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #06 pc 00376953  /apex/com.android.runtime/lib/libart.so (art::Runtime::Abort(char const*)+1058)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #07 pc 0000855f  /system/lib/libbase.so (android::base::LogMessage::~LogMessage()+406)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #08 pc 0028806f  /apex/com.android.runtime/lib/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+1194)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #09 pc 00288211  /apex/com.android.runtime/lib/libart.so (art::JavaVMExt::JniAbortF(char const*, char const*, ...)+64)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #10 pc 003ac06f  /apex/com.android.runtime/lib/libart.so (art::Thread::DecodeJObject(_jobject*) const+538)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   native: #11 pc 00417905  /apex/com.android.runtime/lib/libart.so (_ZN3artL37JniMethodEndWithReferenceHandleResultEP8_jobjectjPNS_6ThreadE.llvm.5386863320803738609+36)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.ArmManager.ARMPluginMakeChallenge(Native method)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.ArmManager.a(unavailable:-1)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.f.a(unavailable:-1)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at com.skt.arm.f.doInBackground(unavailable:-1)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at android.os.AsyncTask$3.call(AsyncTask.java:378)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.util.concurrent.FutureTask.run(FutureTask.java:266)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
02-17 02:25:20.221 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641]   at java.lang.Thread.run(Thread.java:919)
02-17 02:25:20.230 10426 13450 16754 F nt.WeatherCloc: runtime.cc:641] 
02-17 02:25:20.230 10426 13450 16754 F nt.WeatherCloc: runtime.cc:649] JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c
02-17 02:25:20.230 10426 13450 16754 F nt.WeatherCloc: runtime.cc:649]     from java.lang.String com.skt.arm.ArmManager.ARMPluginMakeChallenge(java.lang.String)
...
...
--------- beginning of crash
02-17 02:25:20.230 10426 13450 16754 F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 16754 (AsyncTask #1), pid 13450 (nt.WeatherClock)
02-17 02:25:20.251  1000  4997  5086 E libprocessgroup: Failed to kill process cgroup uid 10158 pid 13776 in 206ms, 1 processes remain
02-17 02:25:20.279 10426 16786 16786 E crash_dump32: unknown process state: t
02-17 02:25:20.290 10426 16786 16786 I crash_dump32: obtaining output fd from tombstoned, type: kDebuggerdTombstone
02-17 02:25:20.295  1058  4758  4758 I /system/bin/tombstoned: received crash request for pid 16754
02-17 02:25:20.296  1000  4777  5396 D bauth_FPBAuthService: pcf : 0x1012, 0 ,2 ,0 ,0 ,0 ,2, 5.0.0.0
02-17 02:25:20.296  1000  4777  5396 D bauth_FPBAuthService: thread id : 2, preenroll_flag : 0, nd cnt : 1, cso : 0, et : 0
02-17 02:25:20.296  1000  4777  5396 D bauth_FPBAuthService: FPBAuthService, 10979
02-17 02:25:20.296 10426 16786 16786 I crash_dump32: performing dump of process 13450 (target tid = 16754)
02-17 02:25:20.307 10426 16786 16786 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Build fingerprint: 'samsung/starlteks/starlteks:10/QP1A.190711.020/G960NKSU2DTAB:user/release-keys'
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Revision: '26'
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : ABI: 'arm'
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Timestamp: 2020-02-17 02:25:20+0900
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : pid: 13450, tid: 16754, name: AsyncTask #1  >>> com.Elecont.WeatherClock <<<
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : uid: 10426
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
02-17 02:25:20.308 10426 16786 16786 F DEBUG   : Abort message: 'JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0xc30e076c

대충 보면 skt에서 제공하는 so library의 특정 native 함수에서 죽는 문제입니다.

업데이트가 매우 오래전에 멈추어진 App.이라 그럴 수도 있겠죠.

 

핵심 호출 부분은 아래입니다.

at com.skt.arm.f.a(unavailable:-1)

at com.skt.arm.f.doInBackground(unavailable:-1)

 

즉 doInBackground 안에서 호출하는 com.skt.arm.f.a 호출 흐름에서 죽습니다.

이걸 해결할 방법은 없고 대신 호출을 막습니다.

그리고 test하니 - SKT 서비스가 없다면서 App. 을 강제 종료 해 버립니다 우쓰!

 

그래서 doInBackground 종료 후 호출되는 onPostExecute 의 주요 호출을 막으니, 정상 동작이 됩니다.

아마다 예전 SKT 관련 서비스 유무 확인 혹은 여러 내용(라이센?!)을 확인하는 루틴이 Q OS에서 동작이 되지 않은 듯 합니다.

 

어찌 되었건 문제는 해결되었습니다.

비록 오래된 App.이지만 더 이상 update 되지 않는 버전이지만 문제만 해결해서 계속 사용하게 되었습니다.

** 해당 App. 은 Play store에 최신 버전의 App. 이 제공되지만 예전 T Store 와는 다른 signature 사용으로 인해 업그레이드를 할 수 없습니다.

 

주요 수정 point는 아래와 같이 요약할 수 있습니다. 단순히 smali 수준에서 문제 함수를 막아서 해결했습니다.

맨 앞에 # 한 부분입니다.

# virtual methods
.method protected final varargs synthetic doInBackground([Ljava/lang/Object;)Ljava/lang/Object;
    .locals 1

#    invoke-direct {p0}, Lcom/skt/arm/f;->a()Ljava/lang/String;

#    move-result-object v0
	const-string v0, " Not ACTIVITY"

    return-object v0
.end method

.method protected final synthetic onPostExecute(Ljava/lang/Object;)V
    .locals 1

    check-cast p1, Ljava/lang/String;

    invoke-super {p0, p1}, Landroid/os/AsyncTask;->onPostExecute(Ljava/lang/Object;)V

#    iget-object v0, p0, Lcom/skt/arm/f;->a:Lcom/skt/arm/e;

#    invoke-static {v0}, Lcom/skt/arm/e;->a(Lcom/skt/arm/e;)Lcom/skt/arm/ArmManager;

#    move-result-object v0

#    invoke-static {v0}, Lcom/skt/arm/ArmManager;->b(Lcom/skt/arm/ArmManager;)V

    return-void
.end method

위 내용 참고요 스크린 샷 (APK Studio)

APK Studio to fix the crash

* 수정 후 정상 동작 스크린 샷~

 

 

** 리버스 엔지니어링은 더이상 업그레이드가 제공되지 않는 S/W 에 대한 호환성 확보를 위한 목적에는 적법합니다.

컴퓨터프로그램 보호법 - 역분석 관련 법

제12조의2 (프로그램코드역분석)
①정당한 권원에 의하여 프로그램을 사용하는 자 또는 그의 허락을 받은 자가 호환에 필요한 정보를 쉽게 얻을 수 없고 그 획득이 불가피한 경우 당해 프로그램의 호환에 필요한 부분에 한하여 프로그램저작권자의 허락을 받지 아니하고 프로그램코드역분석을 할 수 있다.
②제1항의 규정에 의한 프로그램코드역분석을 통하여 얻은 정보는 다음 각호의 1에 해당하는 경우에는 이를 사용할 수 없다.
   1. 호환 목적외의 다른 목적을 위하여 이용하거나 제3자에게 제공하는 경우
   2. 프로그램코드역분석의 대상이 되는 프로그램과 표현이 실질적으로 유사한 프로그램을 개발·제작·판매하거나 기타의 프로그램저작권을 침해하는 행위에 이용하는 경우
      [본조신설 2001·1·16][[시행일 2001·7·17]]

 

이번글은 일상 Web 사용 중 종종 만나게 되는 어이없는 웹 보안 프로그램을 회피 하는 방법 한가지를 소개 드립니다.

이미 시중에 여러 솔루션이 나와 있는 내용으로, 특정 웹 페이지에 대해서 선택 하여 복사 하거나, 화면 자체를 외부 캡쳐 프로그램으로 담는 것을 막는 이상한 방해 프로그램들이 사용되기 시작 했습니다.

문제는 이런 프로그램 때문에 가상화 컴퓨터(VMWare, VirtualBox, VirtualPC)에서는 아예 웹사이트가 막혀 버리는 상황입니다.
가상화 컴퓨터의 화면은 이것을 실행하는 Host 컴퓨터에서 쉽게 캡쳐가 가능 하기에 나온 '궁여지책'이죠!

그래서 쉽고 간단하게 이러한 이상한 프로그램의 방해를 극복하는 비교적 쉬운 방법을 소개 드립니다.

[2011-03-23]
** 한 웹보안 업체가 Fiddler 프로세스를 인식 하기 시작 했네요!
    제 블로그에 출근 도장란이라도 만들어 드려야 겠습니다.
    미안하지만 아래 내용대로 Fiddler를 먼저 실행하고, IE를 실행하면 Fiddler 인식을 못하는데요?!
    그나저나 고생 많습니다!
    사용자의 고통소리도 듣고 계시죠? 누구는 부팅만 수십번 했다던데요?


 


법적 책임에 대해
아래 내용은 일반적으로 허용된 방법을 포함한 적법한 내용이며,
이 방법을 사용하여 발생하는 결과에 대해서는 개별 사용자의 책임을 알려 드립니다.
혹시나 보안관련회사의 문의, 요청은 적법한 절차를 이용하여 주시길 바랍니다.

예> 컬러 복사기 사용법을 알려 드리는 것이며, 복사기를 이용한 위조지폐 유통은 당사자의 책임입니다.


목적
  웹 보안 프로그램에 의한 캡쳐 방지 극복, 가상화 컴퓨터에서 사용 방지 극복

해결방법
  웹 보안 프로그램의 스크립트 실행을 막음

필요한 솔루션
  웹 디버깅 프락시(Web debugging proxy) 유틸리티인 Fiddler를 사용




1~3은 배경 지식 설명이며
4~5는 최종 공략 입니다. 


1. Fiddler 를 다운로드 받고, 설치 합니다.
사용방법은 해당 웹페이지에 나와있는 동영상 설명과, 관련 예제를 참고 하시기 바랍니다.
유연한 기능이 많으나, 짧은시간에 설명이 힘든 내용이라, 상세 사용법을 여기서 언급 드리지 못합니다.


2. 웹 보안 프로그램의 동작 원리를 파악 합니다.
웹 이란는 환경 자체가 PC/Mac 에서 직접 실행되는 Native code와는 달리 마음대로 개별 컴퓨터를 조작 하는 것은 허용 되지 않습니다. 특히나 Web이라는 환경은 개별 브라우저를 사용하여 접속하기 때문에 PC 보안 프로그램처럼 마음대로 하는 것은 더욱 힘이 듭니다.
그러나 한국에서 아주 일반화된 ActiveX 기술 즉 IE(Internet Explorer)에 Add-on을 이용한 보안 기법을 사용하고 있는데, 역시나 웹 켭쳐 방지 솔루션도 이 ActiveX를 이용하고 있는 경우가 대부분 입니다.

제가 사용 해야하는 사이트에서도 ActiveX를 사용하고(별도의 프로그램을 설치), 이 ActiveX를 이용하여 개별 컴퓨터를 조작 하여 원하는 동작을 수행 하고 있습니다.
이 조작을 막아야 하는 것이 우리가 해야 할 일입니다.

Fiddler2를 설치 하고, Capture된 패턴을 보면서 분석을 해야 하는데 일반인이라면 이 부분은 skip하시고 계속 아래글을 읽어 보세요~


3. 웹 보안 프로그램의 일반적인 패턴
Web이라는 기술에는 script 기술이 허용이 되어, 매우 능동적인 동작이 가능하게 - 즉 프로그래밍 기법이 적용 되고 있습니다.
웹(Web) 보안 프로그램은 이 script를 이용해, 개별 ActiveX를 설치, 설치 여부 확인, 설치한 기능 실행을 수행 합니다.
자 그렇다면, 명확합니다. 이 요상한 웹 보안 프로그램의 설치, 설치여부 확인, 설치 기능 실행을 막으면 우리가 원하는 목적을 달 성 할 수 있습니다.
제가 대상으로 하는 웹 보안 프로그램은 하나의 함수만 실행을 막아 버리면, 설치확인, 설치까지 막히더군요.
왜냐면 script가 있고, 결국 이 script가 실행 되어야 하는데, 이 실행 기회를 막아 버리면 아주 쉽게 목적이 달성 된다는 것입니다.

여기서 중요한 내용하나를 언급 드리면~
HTML은 code가 위에서, 아래로 쭈욱~ 실행이 됩니다.
즉 Script가 위치하고, 호출 하는 부분이 있다면, 해당 부분의 스크립트가 실행이 되고 난 뒤에 아래에 나오는 HTML 혹은 추가 스크립트가 실행이 됩니다.
웹 보안 프로그램은 보통 BODY 가 처리 되기 전 단계인 HEAD 혹은 BODY 맨 윗 부분에 위치하여 해당 page가 보이기 전에, 막음 처리 등등을 하게 됩니다.


4. 공략 대상의 관련 코드와 구성
저의 공략대상에 대해서 HTML을 뒤져 보았습니다.
보통, 기존에 잘 돌아가는 웹 페이지에, 추가로 설치하는 방식이라 이전과 비교 혹은 비 보안 페이지와 소스를 살짝만 비교해도 금방 찾아 낼 수 있더군요.

기존 HTML 이라면 보통 아래 정도의 구성을 가집니다.
<HTML>
<HEAD>
<SCRIPT> ~ </SCRIPT>
</HEAD>
<BODY>
~
~
</BODY>
</HTML>


여기에 웹기반 보안프로그램이 포함된다면 아래와 유사하게, HEAD section 혹은 BODY section 에 '괴물 code'가 위치 하게 됩니다. 대충 아래 처럼 말이죠 (은행 등등의 솔루션은 별도 script URL에서 읽어 옵니다. 결국 구성은 비슷 합니다)

<HTML>
<HEAD>
<SCRIPT> ~ </SCRIPT>
</HEAD>
<BODY>
~
<SCRIPT>
  웹 기반 보안 프로그램을 위한 기괴한 스크립트들이 와장창 위치 합니다.
  괴물용-변수선언
  괴물용-변수선언들 ...
  ......
  괴물용-함수선언
  괴물용-최초동작을위한 함수
  괴물용-함수선언들 ...
  ......
  괴물용-최초동작을위한 함수호출!      [--> 위에 등장한 '괴물용-함수선언' 중의 하나로 호출 합니다.]
</SCRIPT>

~
</BODY>
</HTML>



5. Fiddler2를 이용한 실시간 HTML 변경 처리 하기
앞서 이론적인 부분이 지겨웠을 텐데요~ 결국 우리가 해야 할 일은 한줄 요약이 가능합니다.
웹보안 기동 함수를 찾아서 해당 함수 호출을 막아 버리면 됩니다.
앞서 언급한 "괴물용-최초동작을 위함 함수호출1" 이 부분만 확인하고 Fiddler2에 "Rule"을 추가하여 '없는 것으로' 처리 하면, 아주 깔끔하게 마무리가 됩니다.

저의 공략 대상 보안프로그램은 이 함수면이 아래와 '유사' 합니다.
  SecurePage();  

즉 위 함수가 <BODY> 아래 부터 나오는 요상한 스크립트에 정의 되어 있고, 해당 스크립터 덩어리의 맨 아래 부분에 보면 호출 하는 것으로 끝을 맺게 되는 것이죠.
최신 IE 등 에서 제공하는 스크립터 디버깅 기능을 이용해서 의심가는 함수에 break를 걸고, skip하여 동작을 사전 확인도 가능 합니다.

말이 길었습니다. 아래 처럼 Fiddler2 실행 상태에서 조작을 하세요

5.1. 'Ctrl'+'R' 을 누르거나 메뉴의 'Rules' - 'Customize Rules...' 를 선택합니다
   메모장이 실행이 되고, 'CustomRules.js' 파일이 열려지게 됩니다.

5.2. static function OnBeforeResponse(oSession: Session) 섹션으로 이동 합니다.
OnBeforeResponse는 WebBrowser에서 개별 웹 요청에 대한 결과를 받기 직전에 수행되어, 필요에 의해서 특정 부분을 바꾸거나 다른 결과를 브라우저로 전달 하게 됩니다.

SecurePage();를 없애 버리는 것이 목적이므로 아래처럼 OnBeforeResponse 맨 아래에 추가 하시면 됩니다.
"www.공략대상.사이트" 는 'SecurePage();' 함수가 위치한 HTML code의 host입니다.
Fiddler 캡쳐 내용에서 Ctrl+F 등으로 찾기하시면 개별 Host이름 확인은 바로 가능 합니다.

if (oSession.HostnameIs("www.공략대상.사이트") ) {
   oSession.utilDecodeResponse();
   oSession.utilReplaceInResponse('SecurePage();',' ');
}

결과적으로 "SecurePage();" 는 Fiddler2에 의해 실시간으로 삭제(공백으로 변경)되고 이 내용이 브라우저로 전달됩니다.
결과적으로 브라우저는 해당 함수가 호출되지 않게 되어, 요상한 보안 프로그램의 기동을 막게 됩니다.

위 내용을 추가 하고 저장(Ctrl+S) 하시고, 해당 웹페이지를 다시금 탐색하면, 원하는 목적을 달성 하게 됩니다.


오늘도 즐 역공 되시길 바라며~ 편하고 유용한 웹서핑 되시길!

이 글에서는, 미라지(M480, M4800) 320x320 해상도에서 맵피Go를 사용할 수 있는 패치정보를 제공 해 드립니다.

갱신내역
  [2011-01-29] MappyGo v1.0.13A 용 패치 파일 등록




맵피 Go! - 새로운 버전인 맵피가 나온지도 꾀 되었습니다.

저도 개인적으로는 차량용 전용 내비게이션을 사용하고 있는 관계로 M480, M4800용 맵피고가 나옴에도 불구하고 신경을 쓰지 못했습니다.

참고로 차량용 내비로는 파인디지탈 IQ 3D 1000 을 사용 중입니다. 이전에 IQ Blue를 사용하다 한번 더 갈아 탓는데요. Blue도 경품 당첨!, 3D 1000은 물건 구입에 대한 할인개념으로 받은 것이라~ ㅎㅎ
잡설이 길었습니다.


이제는 iPhone, Android phone이 '스마트 폰'의 기본이 되어버린 시대에 미라지(M480, M4800)사용자도 급감하고 있는 것 같더군요.
저 역시 차에서는 전용 내비를 사용중이긴 하나, 휴대폰은 아직도(!) M4800 미라지를 사용 중입니다. 여러 벌레도 많고, 불편한것이 많으나 갤럭시S 사기에는 너무 늦은 시점이라... 갤럭시S 2 나오기 전 까지는 이용 중입니다.
역시나 잘설(2)도 길었네요 ㅎㅎ.


그리하여~ 맵피Go 에 대해서도 미라지에서 사용하기 위해 약간의 연구, 분석을 했습니다.

이전과 완전히 달라진 프로그램인 맵피Go 답게, 이전과는 완전히 다른 방식으로 code가 구성이 되어, 약간의 삽질이 필요 했습니다.
처음에는 너무 급하게, 쉽게 패치 하려다가 엉뚱한데서 돌고 돌았습니다.

하지만 이번에도 2가지 패치를 거쳐서 미라지에서도 맵피Go를 어정쩡 하지만, 답답한 대로 사용 할 수 있게 되었습니다.


이번 맵피Go는 해상도를 96 DPI(강제호환 설정) / 128 DPI(미라지 기본) 설정함에 관계없이 동작은 합니다.
다만 기본 상태에서는 메뉴바가 화면 한가운데를 가로 질러가 버리는 문제가 있고, 이 때 화면 터치시에 가로는 맞으나, 세로가 보는 것과 터치가 다른 문제가 있습니다.
가장 큰 문제는 메뉴바가 사라져 버리고, 이후로 메뉴를 접근할 수 없는 아주 심각한 문제가 발생 합니다.

이 문제의 메뉴바를 아래에 내려 놓으면 좋겠지만....
불행하게도 이전과 같이 GetDeviceCaps 함수는 눈 닦고 봐도 보이지 않습니다.
대신에 화면 해상도에 따른, 화면 구성파일 2개 중 하나를 이용해서 화면을 구성하는 형식이더군요.
파일로는 mappysl240.art 파일 혹은 mappysl320.art 파일이 화면을 구성하는 파일이고, 이 둘중 하나 선택 하는 정도는 프로그램적으로 접근이 가능하나, 이 파일을 분석하는 것은 현재로서는 제 능력 밖인 듯 합니다.

그래서, 쉽고도 간단하게 문제가 되는 320 X 240 인식이 아닌(미라지에서 이렇게 인식 됨), 240 X 320으로 강제 인식하도록 수정하는게 이번 패치의 주된 일입니다.
다만 이로 인해 우측의 남는 부분 (320-240) 80 pixel 만큼의 영역은 최초 배경이 보이는 문제가 있습니다. 이런 현상을 감안 하고 사용 하실 분은 아래 제공 해 드리는 패치 파일로 꺼져가는 미라지에 대한 사랑을 조금이라도 살려 보시길 바랍니다.


관련 힌트 디스어셈블리 코드
.text:0001B4E0                 ADD     R12, SP, #0x420+var_220
.text:0001B4E4                 STRH    R3, [R12,#8]
.text:0001B4E8                 BL      memset
.text:0001B4EC                 LDR     R4, =unk_14DC5C
.text:0001B4F0                 LDR     R1, =aSSMappysl320_a
.text:0001B4F4                 LDR     R3, =aImage
.text:0001B4F8                 LDR     R2, [R4]
.text:0001B4FC                 ADD     R0, SP, #0x420+var_420
.text:0001B500                 ADD     R2, R2, #0x218
.text:0001B504                 BL      swprintf
.text:0001B508                 LDR     R2, [R4]
.text:0001B50C                 LDR     R1, =aSSMappysl240_a
.text:0001B510                 LDR     R3, =aImage
.text:0001B514                 ADD     R2, R2, #0x218
.text:0001B518                 ADD     R0, SP, #0x420+var_218
.text:0001B51C                 BL      swprintf
.text:0001B520                 LDR     R0, [R5,#0x5C]

다만 code를 직접 건드리는 것은 여러 귀찮은 문제가 있으므로, 위 파일명이 저장된 위치에서 "mappySl320.art" 부분을 찾아서 "mappySl240.art" 로 수정 합니다.
아래 위치입니다.

즉 Sl320 으로 된 부분을 걍 Sl240 으로 32 대신 24로 수정 하는 작업 입니다.
이것 만으로, 미라지에서 강제로 240 X 320 해상도로 고정을 해 버리고, 화면이 보기 좋지 않으나, 적어도 실행 중에 메뉴가 사라지는 문제는 없앨 수 있었습니다.

000fe2b0h: 76 00 00 00 25 00 73 00 5C 00 25 00 73 00 5C 00 ; v...%.s.\.%.s.\.
000fe2c0h: 6D 00 61 00 70 00 70 00 79 00 53 00 6C 00 32 00 ; m.a.p.p.y.S.l.2.
000fe2d0h: 34 00 30 00 2E 00 61 00 72 00 74 00 00 00 00 00 ; 4.0...a.r.t.....
000fe2e0h: 49 00 6D 00 61 00 67 00 65 00 00 00 25 00 73 00 ; I.m.a.g.e...%.s.
000fe2f0h: 5C 00 25 00 73 00 5C 00 6D 00 61 00 70 00 70 00 ; \.%.s.\.m.a.p.p.
000fe300h: 79 00 53 00 6C 00 32 00 34 00 30 00 2E 00 61 00 ; y.S.l.2.4.0...a.

위 내용 보시고 직접 patch 하셔도 되구,
아래 파일을 이용해서 패치 하셔도 됩니다.

[안내]
아래 제공되는 파일은 맵피 PDA 라이센스를 소유 하고 있으나 M&Soft 에서 이해할 수 없는 정책을 주장하여M480, M4800 에서는 사용 할 수 없는 호환성을 해결하고자 개인적으로 만든 파일 입니다.
관련 법을 준수하시기 바랍니다.
컴퓨터프로그램 보호법 - 역분석 관련 법

제12조의2 (프로그램코드역분석)
①정당한 권원에 의하여 프로그램을 사용하는 자 또는 그의 허락을 받은 자가 호환에 필요한 정보를 쉽게 얻을 수 없고 그 획득이 불가피한 경우 당해 프로그램의 호환에 필요한 부분에 한하여 프로그램저작권자의 허락을 받지 아니하고 프로그램코드역분석을 할 수 있다.
②제1항의 규정에 의한 프로그램코드역분석을 통하여 얻은 정보는 다음 각호의 1에 해당하는 경우에는 이를 사용할 수 없다.
   1. 호환 목적외의 다른 목적을 위하여 이용하거나 제3자에게 제공하는 경우
   2. 프로그램코드역분석의 대상이 되는 프로그램과 표현이 실질적으로 유사한 프로그램을 개발·제작·판매하거나 기타의 프로그램저작권을 침해하는 행위에 이용하는 경우
      [본조신설 2001·1·16][[시행일 2001·7·17]]

이 파일을 이용해서 맵피를 사용 하기 위해서는 
반드시 맵피 PDA 라이센스를 소유 하고 있으며, 또한 Mappy 전체 이미지를 정상적으로 download하신 분 만이 사용이 가능 합니다.

여기서 안내드리는 내용은 직접 patch를 할 수 있는 정보와 patch용 요약자료만 공유 합니다.
패치 프로그램과 Mappy라이센스, Mappy 인스톨 파일은 개인 소유의 것을 이용하셔야 합니다.


Patch 파일은 Free software인 VPatch 를 사용 했습니다. Google 등의 검색엔진을 이용하셔서 개인이 구하셔야 합니다. 또한 별도 패치용 실행 파일도 제공을 합니다.
이 프로그래과 제공하는 data를 이용하여 발생하는 법적인 문제에 대해서는 사용하는 당사자들의 책임임을 밝혀 둡니다.

아래 파일에 대해서는 그 어떠한 질문에 대해서도 답변을 드리지 않습니다.

패치요약 파일:

[2011-01-29] MappyGo v1.0.13A



위 파일은 VPatch 라는 프로그램을 설치하고난 뒤에 전용 프로그램을 이용하시면 됩니다.

*당연히 PDA(WM5.0)용 맵피Go 1.0.12 (파일 날짜 2010-11-22) 를 소유해야 합니다.
*Patch 실행 법은 VPatch 설명서를 참고 하세요.
>  
VPATCHPROMPT.EXE PatchData_MappyGo_101222_For_M430x (sourcefile) (outputfile)

혹은 아래 실행 파일을 이용하셔도 됩니다.

VPatch_MappyGo_101222_For_M430x (Original) (Output)

그럼 미라지와 함께 즐 맵피Go 하시길 바라며~ 오늘도 이정도로 글을 끝낼까 합니다.
맵피Go가 큰 변동 없다면 한동안 업데이트 할 것 같으나... 저도 미라지를 떠나게 되면 어떻게 될지는 장담 할 수 없을 것 같습니다.

추가로 - GPS 잡은 모습 보여 드립니다.
이전 버전과 달리 자동 탐색하면 제대로 잡아 주고, 사용에도 문제 없는 듯 합니다. 다만 미리 External GPS 데이타는 한번 받아 주세요.




쫓고 쫓기고, 막고 찌르고, 궁극의 공격과 궁극의 방어는 사실 존재 하지 않는 것 같습니다.
특히나 DRM 이라는 솔루션은, 돈을 벌기 위한 방어 수단으로서 등장 했다고 해도 과언이 아닌 것 같습니다.
이제는 DRM이라는 것이 알게 모르게 생활 속 깊숙한 곳에 파고 들었습니다.

a bike that can only run on special roads.
a bike that can only run on special roads. by vrogy 저작자 표시

하지만 이 DRM 솔루션 이라는 것이 결국 자유로운 생활에 하나의 가시방석 같은 존재로서 느껴지는 것은 왜일까요?

특정 사이트에 접근 하는 것 만으로도 이 이상한 DRM 솔루션이 깔려버리고, 이후에 PC가 이상 동작하는 것은 또 왜일까요?
아! MS Windows가 아니면 그러한 DRM 매체를 사용 하는 것 조차 접근 못할 수가 있습니다.

서론이 길었습니다.
많은 DRM기술이 있지만... 여기서 다루어 보고자 하는 내용은 PC의 성능을 엄청나게 갈가 먹어 버리는 API Hooking 기반 DRM, 보안 솔루션 속을 좀 벌려 볼까 합니다.

여기서 혹자는 질문을 할 수 있겠습니다.
  왜! 보안을 해치는 짓을 하냐고?
  너 나쁜 놈이지?
  잡아 넣고 말겠어!

그러면 저는 무어라 답을 할까요?
  ㅎㅎ 저는 보안을 해치지도 않고, 나쁜놈도 아니고,
  어디에 들어갈 위인도 아닙니다.

  다만 그들의 이상하고 무식한 솔루션을 좀더 이상적이고, 무난하게 바꾸었으면 할 뿐입니다.

저를 나쁘다고 하기 전에! - 그들의 솔루션을 '제대로' 만들길 바랄 뿐입니다.
  치사하게 URL Blocking 하지 마시구요.
  Hooking을 좋아 하시더니 Blocking도 사랑 하시는 듯!



1. API Hooking
API Hooking 이라는 것을 DRM 솔루션에 한정하여 이야기 해 보겠습니다.(다른 보안 솔루션에서도 절찬리 채택 중이랍니다. 쩝.)
DRM의 본연의 역할이 파일 등의 매체로 저장 될 때 원본과 달리 권한, 암호와 같은 부가 정보와 함께, 원본을 암호화(Encryption)하게 됩니다. 그리고 다시 읽어 들이게 되면, 부가 정보를 확인 하여 권한이 있는 사용자일 경우 암호화된 부분을 원복(Decryption)하여 문서를 볼 수 있게 됩니다.
이러한 과정은 결국 매체(HDD 혹은 네트웍 드라이브 등등)로의 파일 저장 혹은 파일 읽기로 요약 될 수 있습니다.

그런데 API Hooking 기반 DRM은 이러한 과정에서 필수 적으로 사용되는 OS API를 가로채기를 하여 OS에서 만들어진 동작을 하는게 아니라 자기의 부가 동작을 하게 됩니다.
즉 워드프로세스에서 저장을 하게 되면 파일로 문서가 저장이 되는데, 이 파일 저장 과정에 가로채기를 하여 원본 자료가 아닌 다르게 변화가 된 자료가 저장 되게 되는 것입니다.



하지만 이 API Hooking에는 많은 '비용'을 지불하게 됩니다.
대부분 Hooking 기법에는 부가적인 DLL 사용이 이루어지게 되고, 이로인해 시스템 메모리 증가, 특히 성능이 낮은 PC에서는 엄청난 부하로서 동작을 하게 됩니다.

한가지 더 슬픈 소식은, 의외로 많은 '보안'관련 솔루션이 앞다투어서 API Hooking을 행한다는 사실!
그래서 솔루션끼라 박치기 하는 일도 흔하게 되어, PC환경이 먹통이 되기도 합니다.

API Hooking은 인터넷에 많은 자료가 이미 공게 되어 있습니다. 구글 검색 하기로 가 보시면 좀더 넓은 세상을 보실수 있습니다.


2. Overhead
API Hooking 만으로도 무언가 복잡하고 문제가 있다는 것은 쉽게 이해 갈 듯 합니다 ^^
그러나 실체는 더욱더 엄청난 문제를 가지고 있습니다.
API Hooking을 하게 되면 기존 API 동작으로 그대로 흘러 갈지,  부가 동작여부를 반드시 판단 해야 합니다. 여기서 판단 루틴에 의해 기존 동작에 비해서 느려 질 수 밖에 없습니다.

더구나 이 Hooking 함수의 코드를 살짝만 봐서 너무나 많은 부가 동작을 하고 있습니다. 특히 파일 관련 hooking 함수를 보면 경악 할 수 밖에 없습니다. 많은 솔루션이, 파일명을 통째로 복사를 하거나 파일명에 대한 스트링 확인을 행하는데, "최적화"의 '최' 자도 생각 하지 않은 code가 대부분이었습니다.

그냥 "느리게" 살면 되겠습니다만. 그러기에는 PC 성능 저하가 너무나 심하다는 것에 화가 날 수 밖에 없더군요.


3. 광고만큼 보안을 지키지는 못한다
저마다 자신의 솔루션이 최고라고 자랑들을 많이 합니다. 몇몇 업체는 어찌나 WWW 에서 "얼굴" 알리기를 열심히 하시던지 놀라울 따름입니다!
하지만 그들이 자랑하는 그 솔루션에는 자랑과 함께 '구멍'이 있다는 것을 알아야 합니다. 아니 이미 알고 있습니다.
모든 PC에 대해서 자동으로 철통 보안이라 자랑 하지만 - 특히 API Hooking 기번으로 흥한 자는 API Hooking으로 망할 수 밖에 없습니다!
Easy Come Easy Go !

먼 말이냐! 그들이 자랑하는 API Hooking은 쉽게 구멍을 뚫어서 사용 하는 것입니다. 역으로 그들에게도 그 구멍은 똑같이 적용 된다는 것이죠.
Hooking한 루틴을 제 Hooking 하거나 Hooking 동작에 추가 구멍을 뚫을 수 있다는 이야기 입니다.
별도로 언급하겠지만! 이 API Hooking이 그들의 핵심 기반 기술이고 쉽게 이용하는 만큼 남들에게도 동일한 기회가 있다는 것입니다.

훅(Hook?!)~ 하면 다 날아 가버릴 수 있다고 해야 할까요!


4. 복잡하지만 단순하다.
어떤 자동차업체 담당자 인터뷰에서 말하길 '안전장치가 왜 빠졌냐 라고 질문에'  "다르지만 같다"고 하셨는데요 ㅎ~ 동급으로 저는 "복잡하지만 단순하다"를 언급 드릴까 합니다.
각종 DRM 솔수션이 있는데 API Hooking 기반의 경우 기존 Application의 읽고 쓰기 동작을 최대한 막아서 호작질을 하게 됩니다.
그런데, 아무 App.에 대해서만 호작질을 하게 되면 여러 문제가 생가게 되므로, 감시 대상 App.에 대해서만 이런 '지랄'같은 동작을 합니다.
이 부분은 자세히 살펴 보지 못했지만, 최초 Load시에 판단을 하는 것으로 보아서는 아마도 진입 code등을 call-stack등을 확인 하지 않을까 의심이 들구요~. 일단 감시 대상 App.이 판명나면 온갖 API을 Hooking을 걸어 버립니다.
파일 입출력은 물론이고 OLE관련 사항(문서 포함 기술을 방해 해야 하기에!), 표준 Message 처리 까지... 그 갯수가 너무나 많더군요.
철통 보안이 아닌 철통 Hooking 걸기라고 할까요 ㅠㅠ
하지만! 이러한 규모에 비해 결국 핵심은 Hooking이고, 이 것은 쉽게 확인이 된다는 것이죠.
파일을 원본으로 만들고 싶다면! 저장시에 Hooking걸려 이상하게 변하는 것을 Hooking이전으로 돌리면 된다로 단순화 할 수 있습니다.



5. Hooking으로 흥한자 Hooking으로 훅~ 간다

5.1. 들어가기 전에
자~ 이제 좀더 세부적으로 파헤쳐 보겠습니다.
아래 내용은 Hooking을 이용한 여러 솔루션에 공통 적인 내용입니다. 괜시리 도둑 제발저리시지 마시기를 바랍니다.
제가 경험한 솔루션의 예를 들겠습니다. 다만 직접적으로 모두 까발리지 않고 기본 원리 위주로 설명 하겠습니다. 이 솔루션은 몇몇 공공 사이트에서 사용 중이라 직접 경험도 가능 하겠습니다. 다만, 준-바이러스라 한번 설치하고서 삭제하려면 고생좀 할 수 있는 솔루션이기도 합니다.

5.2. DRM파일을 원본으로 저장하기의 다른 접근
이번 시도도 읽기 권한이 있는 문서 파일에 대해서 DRM 솔루션의 제어를 벗어나, Encrypt되지 않은 원본으로 저장 하려고 합니다.
 *권한 없는 파일에 대해서는 능력 밖의 일이라, 언급 하지 않습니다.
이전에 올린 내용을 보면 Excel VBA를 이용해서 직접적인 파일 Read, Write를 해서 아주 간단하게 DRM 솔루션의 구멍을 이용하는 방법 이었습니다.
그러나 이 방법은 이제 막혀 버렸죠. 하지만 제목 처럼 "Hooking 기법"에 기반한 솔루션의 경우 결국 Hooking 함수에 있을 수 밖에 없는 "구멍"을 다시금 이용 할 수 있습니다.
지난번 처럼 간단하지 않지만, 지난번 처럼 쉽게 막을 수 있는 구멍이 아니기도 합니다.

5.3. DRM파일을 읽을 수 있는 실행 파일이 시작지점
지난번에 Excel을 이용해서 쉽게 구멍을 이용 했는것과 마찬 가지로, 결국 이번에도 시작은 DRM파일을 읽을 수 있는 프로그램에서 시작 해야 합니다.
뭐~ DRM 솔루션을 완전히 속여 버리면 좋겠지만, 그것 분석하는 것은 결국 맞장 뜨자는 이야기가 되어서 출혈도 너무 크고, 이후에 문제가 될 수도 있기에, 기존 App.을 수정 하는 방법으로 접근 합니다.
이번에도 만만한 App.하나 골라 잡았습니다(Excel은 아닙니다 ㅋ~). 당연히 DRM원본을 읽을 수 있는 App.에 한정합니다. (어떤 App. 이냐구요? 비밀 입니다. 그들이 제 블로그에서 정보만 빼 가고, 저는 엿이나 먹어라는 행동을 미리 막기 위함 입니다. 리버스엔지니어링 취미가 있으신 분들께서는 쉽게 시도 가능한 내용으로 판단 됩니다.)

그리고 아래와 같은 전략을 세웠습니다.
 A. 파일 읽는 루틴을 찾는다
 B. 찾은 파일 읽기 루틴 직후에 새로이 파일 저장 루틴을 만든다 [이것으로 끝나길 바랬습니다]
 C. 파일 저장 루틴이 동작 되기 전에 Hooking을 막게끔 처리한다.
 D. 파일 저장 후, Hooking을 다시금 원복 처리 한다.

위와 같이 정리 될 수 있는데, 실제 행해야 할 일은 참으로 많습니다. 왜냐면 읽기 루틴을 손상을 주지 않고 쓰기 동작을 추가 해야 하는 어려움이 있습니다.
그러기 위해서는 Disassembly를 해야 합니다.
대표적은 Tool인 IDA를 이용해서 이 과정을 수행 합니다.

5.4. 기존 App.의 파일 Open 후 Read 루틴 직후 버퍼에서는 원본을 볼 수 있다
당연히 기대한 대로 App.에서 DRM원본 파일을 읽은 직후 해당 버퍼를 확인 하면, 원본을 그대로 확인 할 수 있답니다.(뻔~) App.은 encrypt된 내용을 직접 해석하지 못하므로 후킹꾼이 ReadFile 중간에서 원복 해 주는 것이죠.
결론은 나온듯 합니다. 이 원본 내용을 저장만 다시 하면 되는 것이죠. [실은 말만 쉽다고...]

5.5. 원본을 파일로 저장
공략 대상 App.을 Disassemble 하고, Create, WriteFile을 구성하기 위한 code를 새로이 만드는 과정은 약간의 노가다와 Assembly과정 그리고 그것을 Hex code로 새로이 이식하는 과정이 필요 합니다. 이 부분은 가능하면 공략 대상에 이미 존재하는 code를 최대한 활용하여 자르고 붙여서 이식을 했습니다.

그리하여, 파일을 Create 하고, WriteFile을 했습니다. 그런데 @@. 생각과 달리 파일명, 확장자를 다른 것으로 바꾸고 쇼쇼쇼~ 를 했지만, 결국 원본이 아닌 Encrypt된 파일로만 출력이 되더군요.
휴... 지난번 Excel구멍을 보고 그들이 제대로 막은 듯 합니다.
그래서 한참을 다른 방법으로 파 보았지만... 쉽지 않더군요.
알고보니 이 망할 넘이 거의 모든 File I/O API는 물로 OLE API 까지 다 Hooking으로 말아 먹었더군요!
그렇습니다. 결국 이 보안모듈에 의해서 모든 파일 I/O가 통제 되는 상황에서 좀더 고급 방법을 사용 하지 않고 파일 저장은 불가 합니다.
간단하게는 PIPE를 사용 해서 다른 파일로 전송 후 저장하는 방법도 시도 하려 했으나... 너무나 귀찮다는 것입니다. 또한 실제 사용도 거추장 스럽죠!
저의는 원본을 Drag & Drop으로 해제된 파일을 얻고자 했습니다. 그 꿈을 접기에는 아쉽다는 것이죠.

관련 작업 내용을 살짝 언급하면 아래와 같습니다.
걍 힌트일 뿐 직접 이용은 불가능 합니다.
낑구기(?!ㅎ) 소스 구성 -수도 코드 구성
아래 코드의 회색 부분에 해당하는 code를 모두 NOP에 준한 처리를 하고, 아래 code를 만들어 넣는다!
    //--- Saving용 파일명 준비
    nChars = lstrlen( sz );
    sz[nChars] = '_';
    sz[nChars+1] = 0;

    //--- 파일 생성  ?? 0100★★
    fp = CreateFile( sz, .... -> 함수 SaveFile의 두번째 argument를 그대로 이용)

    //--- 기록
    WriteFile( fp, lpBuf, len, &nBytesRead, NULL);    // nBytesRead unused !!!
        // 혹은 옆에껄로        &nChars  --> 주소 넘겨야 함!

    //--- 닫고 끝내기
    CloaseHandle( fp );
     fp=INVALID_HANDLE_VALUE;

    //--- 아무일 없었는 것 처럼 끝내기
   UnmapViewOfFile( B );
    return FALSE;

실제 assembly 코드 만들기 과정
file path 수정 수행.
nChars = lstrlen( sz );






sz[nChars] = '_';
sz[nChars+1] = 0;



mov     edi, [ebp+sz]
push     edi
ds:lstrlenW
mov     ebx, eax
mov     [ebp+nChars], ebx
mov     ecx, eax

shl     ecx, 1    ; wide 이므로!
add      edi, ecx
mov     ebx, 0x5F
mov     [edi], ebx

8B 7D ★
57
FF 15 ★★★★
8B D8
89 9D ★★★★
8B  C8

C1 E1 01
03 F9
BB 5F 00 00 00
89 1F


파일 생성, fp 받아오기
fp = CreateFile( sz,










xor      esi, esi
push    esi    ;NULL
push    80h   ;FILE_ATTRIBUTE_NORMAL
push    4      ;OPEN_ALWAYS
  --> 2 CREATE_ALWAYS 로 바꾸자
push    esi    ;NULL
push    3      ;~READ|~WRITE
push    0C0000000h ;~READ|~WRITE
push    [ebp+sz]
call    ds:CreateFileW
mov     fp, eax
33  F6
56
68 80 00 00 00
6A 04

56
6A 03
68 00 00 00 C0
FF 75 ★
FF 15 ★★★★
A3 ★★★★

esi 는 이미 0 상태 (위 코드에서 이어지므로)
파일로 기록, WriteFile( fp, lpBuf, len, &nBytesRead, NULL);
WriteFile(fp, ~







push    esi    ;NULL
lea     eax, [ebp+nChars]
push   eax
push    [ebp+len]  ; len은 file size OK
push    [ebp+B]
push    fp
call    ds:WriteFile

56
8D 85 ★ FD FF FF
50
FF B5 ★ FD FF FF
FF B5 ★ FD FF FF
FF 35 ★★★★
FF 15 ★★★★


닫 고 끝내기 CloaseHandle( fp );   fp=INVALID_HANDLE_VALUE;
CloaseHandle( fp );
fp=
INVALID_HANDLE_VALUE;

push    fp
call    ds:CloseHandle
or      fp, 0FFFFFFFFh

FF 35 ★★ 00 01
FF 15 ★★★★
83 0D ★★ 00 01 FF


아무일 없었는 것 처럼 끝내기     UnmapViewOfFile( lpBuf );    return FALSE;


push   [ebp+B]
call    ds:UnmapViewOfFile
jmp     loc_★★★★ ; 0 set, return
FF B5 ★★ FF FF
FF 15 ★★ 00 01
E9 ★ 04 00 00
* 해당 jmp 명령★★★★ - address - 5 해야 함!

★ 처리 한 것은, 더이상 그들에게 편리를 주지를 않기 위해서 입니다.


5.6. Hooking 을 뒤집어라!
결국 호랑이를 잡기 위해 호랑이 굴로 들어갈 수 밖에 없습니다.
즉 CreateFile, WriteFile에 대해서 Hooking동작을 살펴 보아야 한다는 것입니다. 왜냐면 CreateFile, WriteFile 흐름에서 이 보안 모듈에서 Hooking으로 '호작질'을 하고 있기 때문입니다. 이 호작질을 훌쩍 넘겨 버리고, 기존의 system 함수를 그대로 이용 하게 한다면, 고생끝 행복 시작이 가능 한 것입니다.

다만 호랑이 잡으려다 보니 IDA 디버깅은 죽어 버립니다. 나름 보안장치를 하셨군요! 대신 국민 디버거인 Visual C++ 는 멀쩡히 돌아 가는 군요!
우선 수술한 App.을 실행 시킵니다.
그리고 강제로 Process를 VC에 Attach 시킨 후 우선 Pause 합니다.
그리고 이식 수술한 CreateFile위치에 break point를 설정 합니다.
그리고 Go~
DRM 파일을 하나 Drag & Drop 합니다.
곧 앞서 걸어 두었던 break point가 걸리고, Step을 밟고 들어 갑니다.! 호랑이 굴에 들어 가는 것이죠!
두둥! 넵 진입 했습니다.
그리고 CallStack을 보고, DLL 모듈이 어느 것이지 확인 합니다. 물론 주소값도 미리 기록 합니다.
'역공'의 핵심은 기록입니다.

★c.dll 임을 확인 되었습니다. 이넘이 바로 Hooking을 밥먹듯이 걸어 버리는 주체입니다. 이 넘의 배를 갈라볼 필요가 있는 것이죠!
다시금 IDA를 통해서 Disassembly를 합니다. 그리고 앞서 기록한 주소값에 해당하는 code를 찾습니다.
이넘이 바로 그 CreateFile을 대신하여 Hooking 하는 본체인 것이죠.
그리고 살펴 봅니다.

ㅎㅎ 역시 Hooking함수에서 필연적으로 나타 날수 밖에 없는 패턴이 보입니다.
'호작질' 동작을 계속 할 것인지, Hooking전의 원본 동작을 할 것인지 판단을 하고, 분기 하는 logic이 나타 납니다.
이 logic은 있을 수 밖에 없는  루틴 입니다. 특히나 CreateFile이라면, 모든 파일 I/O 전에서 호출 되어야 하는데... 실제 파일 I/O는 온갖파일이 다 지나가게 되고, 관심 대상인지 아닌지를 판단해야만 합니다.
바로 이 판단하는 부분을 먹통을 만들면, 만사 형통이 되겠습니다.
호랭이 잡는 다는 것이죠!
간단히 디버거에서 해당 판단 루틴을 뒤집어서, 저~ 밑으로 가는 흐름으로 돌려 보았습니다. 슝~
가서 파일 기록 까지 끝내고 나니!!!

5.7. Hooking 함수는 막장 쓰레기!
여기서 잠깐 - 이 함수의 첫 시작이, 파일명을 몽창 변환하는 logic이 등장합니다. 뭐 하긴 제가 살펴본 다른 보안 모듈도 비슷했습니다. 쓸대없이 파일명 복사해다가 파일명을 뒤져서 다른 판단하더군요.
만일 file path가 엄청 길다면 더욱더 끔찍합니다. 단지 파일면 열었을 뿐인데 Hooking함수에서 쓸데없는 시간을 다 잡아 먹어 버리게 됩니다.

5.8. 뒤집고 얻은 원본
파일 기록까지 시켜 보니 원본이 얻어 졌습니다. ㅠㅠ 감동의 결과 입니다.
Hooking 루틴의 특정 흐름만 뒤집으면 그냥 보안 동작 대신 원래 동작으로 원본 파일을 얻을 수 있다는 것입니다.
자! 그렇다면, 이 Hooking 함수 흐름을 수정하여, 원래 동작을 하는 방법을 찾아야 합니다.

의외로 간단합니다.
앞서 언급했지만 Hooking 함수에는 특정 flag혹은 이와 동등한 동작을 하는 메모리 값이 있습니다. 이 메모리 값을 공략대상 App.에서 살짝 다른 값으로 바꾸었다가, 원복 해 주면 됩니다.
이 과정은 단지 메모리 값을 빼와서 보관, 수정, 파일 저장, 원복으로 해결 할 수 있습니다.
더군다나 Hooking DLL은 공략 대상에 기생하는 DLL입니다. 그래서 어려움 없이 동일 process 이므로 걍 메모리 직접 읽고, 쓰기가 되어서 쉽게 원하는 동작이 가능합니다.
그래서 Hooking으로 흥하면 Hooking으로 망한다고 할 수 있겠죠 ㅎㅎ
자~ 위 동작으로 어렵게 다시 Assembly code로 만들고 Hex code로 patch합니다.
그리고, 최종 완성한 우리만의 App.이 만들어 졌습니다.
그리고 원본 파일을 Drag&Drop! - 그러면 원복된 파일이 만들어 지는 것이죠 ㅎㅎ
꿈에 그리던, 원본 던지면, 결과물이 원본 위치에 생성 됩니다.

관련 잡업 내용을 소개하면 아래와 같습니다.
★에서 ★c.dll 의 특정 변수값을 임시 보관 후, 무조건 분기하도록 조건을 수정 한 값을 넣고,
Write작업 후 해당 변수값을 복원하자!
특히나 ★c.dll 은 고정된 메모리값을 가지는 것으로 보인다!
이 변수값은  dword_61★★★ 로 사용 중 void* 로도 사용 중으로 IDA분석 됨
    
    mov    eax, dword_61★ ★★    // [61F4AFA8] A1 61
    push    eax                             // 보관하기! 50    
    mov    dword_61★★★★, 0     // ebx가 이미 0 이므로 mov mem, ebx 를 찾아보자
                                                  // 89 1D 61
    ..... Create file기존 patch code ...            

    pop     eax                             //  58
    mov    dword_61★★★, eax    // A3 61

    ..... Create file 이후 patch code


6. Easy Come Easy Go
컴퓨터가 굴러가는 세상에서, 기술은 많습니다. 그중 MS계열에서는 API Hooking 기술도 있는 것입니다.
하지만 이 API Hooking은 쉽게 이용 할 수 있는 만큼, 쉽게 역으로 재이용 당할 수도 있습니다.
Easy Come Easy Go - 바로 API Hooking에서도 적용 된다는 사실.
잊지 않았으면 합니다.



지난 번 처럼 자료를 직접 공유 드리지 못해서 죄송 합니다.
하지만, 위에 언급 드린 내용 만으로도, 관심 있는 매니아들에게는 출발점을 충분히 전달 드렸다고 봅니다.
저는 보안 모듈 자체를 거부하지는 않습니다. 다만 얕은 기술과 외형만 중시하는 일부 몰지각한 솔루션에 대해서 제대로된 기술을 가질 것을 바랄 뿐입니다.
특히나 컴퓨터 성능을 잡아 먹는 그런 솔루션은 없어져야 할 것입니다.

+ Recent posts