이전글에 이어서 계속 합니다.

패치를 위해 코드 여기저기를 헤메고, 아이디어를 확인 하고 정신 없이 진행 하고, 틈틈이 정리는 해 두었는데 - 역시나 글로 다시 깔끔하게 정리 하려니 시간과 노력이 많이 필요 하군요.


자~ 어제글에 이어 오늘은 M480,M4800(미라지)에서 맵피(Mappy)를 정상적으로 사용하기 위한 나머지 내용을 정리, 작성 하겠습니다.

패칭을 위해서는 여러 지식도 필요로 하지만 - 원본 code없이 assembly로만 구성된 기계어 수준의 코드에서 어떠한 기능을 찾는 것은 - 나름의 "아이디어"가 있어야 합니다.
이번일도 이러한 "아이디어"를 생각하고 해당하는 code를 찾고, 그 code를 이리저리 바꾸어 보는 것으로 완료를 했다고 할 수 있습니다.

어제까지는 Mappy화면을 제대로 띄우는 동작 까지 했습니다.

그런데 문제가 있다고 언급 드렸죠!
네! - 어제 수정된 내용 - 즉 강제로 320 x 240으로 화면 크기를 조절하는 것으로 대부분의 동작은 만족하나, GPS정보 화면이 표시되지 않으며, 더구나 GPS 연결이 되고 난 직후에 역시 화면이 먹통이 되는 문제가 있다고 했습니다.

이 문제를 해결하려고 합니다.


다시금 문제를 분석 해 봅니다.
알고보면 현상이 처음 맵피가 실행 된 직후의 증상과 다른 것이 없습니다.
즉 분명 터치 입력은 되고 있으나, 지도 화면 등이 먹통으로 내용이 표시 되지 않는 다는 것이죠.

그렇다면 첫번째 해결한 내용과 동일한 문제가 있다는 것이죠.
즉 특정 상황에서 320x240으로 유지되던 화면이 320x320 혹은 240x320 등등으로 320x240이 아닌 화면으로 변형 되었을 것이라고 가정을 할 수 있습니다.

그런데 이미 320x240으로 설정 하게끔 몇개의 code를 patch를 했습니다.
그런데도 동일 문제가 있다는 것입니다.
난감하죠... 첫번째 해결책이 모자라는 결론이기 때문이죠.

그래서 GPS 설정 버턴 [나침반 아이콘 누른 직후, 위성 아이콘 누르기/혹은 위성 아이콘 바로 누르기] 결과로 보이는 "먹통"화면에 대해서 터치를 여기저기 눌러 보았습니다.
즉 눈에는 보이지 않으나 분명 touch에 반응하는 부분이 있을 것이라 생각을 한 것입니다.

넵! 역시나 맞습니다. 눈에는 보이지 않으나 분명 GPS 정보 화면이 표시되고 있었습니다.
다만 그 형태가 최초의 320x240이 아니라 240x320 형태로 기대와 다른 엉뚱한 위치에 버턴들이 위치해 있더군요.

자~ 여기서 단서를 하나 잡았습니다.
특정 상황에서 Window의 크기를 임의로 수정하는 부분이 있다는 것입니다.

[아래 내용은 Visual Studio Remote Tools에 있는 "원격 감시" 프로그램으로 확인 할 수 있습니다.]
실제 Mappy에 대해서 window 구조를 확인 해 보았는데 - 단지 두개의 window만 사용 하는 것으로 보였습니다.
심지어 전체 화면을 구성하는 dialog에서도 말이죠!
즉 지도화면의 window에서 특정 전체 화면을 이루는 dialog 화면은 새로운 window를 띄우는 것이 아니라 기존의 지도화면을 이루고 있는 window를 공유 하고 있는 것이죠.

자 이제 이야기가 맞아 들어 갑니다.
Mappy는 지도 View를 구성하는 Window가 있는데, 다른 전체 화면의 dialog도 이 window를 공유하고, M480x와 같은 320x320 해상도의 환경에서는 특정 code부분에 의해서 임의로 240x320 등으로 Window 크기를 임의로 변형하는 문제가 있다는 것입니다.


우선 GPS정보 화면을 바로 보기 위해서 assembly code에서 해당 code 부분을 찾아 보았습니다.
그냥 보아서 신이 아닌이상 어떻게 이 많은 code에서 GPS정보 화면 부분을 찾겠습니까?

또 단서를 확인 해야 합니다.
GPS정보 화면은 아래와 같은 내용이 표시 됩니다.


단서들이 주렁주렁 달려 있습니다!
수신기 상태, 설정, 정보에 있는 string 값들이 바로 단서들입니다.

한글은 유니코드라 IDA에서 보기 힘들므로 영문과 숫자 출력 부분이 만만한 넘들입니다.
즉 아래와 같은 string을 예상 할 수 있습니다.
  ON, OFF, 1D, 2D, 3D, %d km/h, %d:%2d:%2d

감이 안오신다구요? 감이 오셔야 직접 패칭 작업을 하실 수 있습니다. 혹시 직접 해 보실 분은 감이 오도록 '정성'을 들이 실 필요가 있을 것 같습니다.

자~ 그리고 그 부분을 제대로 찾았습니다.

그리고 해당 code 부분의 앞뒤를 마구 뒤져 봅니다~ 쭈욱 봅니다. 함수 이름에만 집중 해서 말이죠...
무언가 감흥이 오는 함수가 있을까 하고...
보았더니 OnInitDialog 부분이 눈에 보입니다.
넵 - MFC로 된 Dialog이므로 위 함수가 당연히 있다는 것이죠.

그리고 또 주변을 둘러 봅니다.
SHInitDialog / SHFullScreen 등의 함수 호출이 눈에 띄입니다.
그리고 그 주변에 불쑥 나타나는 GetWindowRect, MoveWindow가 제 눈안을 파고 듭니다!!!
눈이 또릿또릿 해 지더군요! 단서 포착입니다!

해당 부분을 리스팅 하면 아래와 같습니다 - 몇몇 부분은 미리 뒤져서 이름 수정 한 부분도 있습니다.
[지금 언급 드리는 내용은 전체 삽질에서 성공한 삽질 위주로 설명 드리는 것임을 감안 해 주세요]
.text:000276E4 MAIN_DIALOG_GPS_INFORMATION
.text:000276E4
.text:000276E4 nHeight         = -0x50
.text:000276E4 bRepaint        = -0x4C
.text:000276E4 var_48          = -0x48
.text:000276E4 var_44          = -0x44
.text:000276E4 var_40          = -0x40
.text:000276E4 var_3C          = -0x3C
.text:000276E4 var_38          = -0x38
.text:000276E4 pshidi          = -0x34
.text:000276E4 X               = -0x24
.text:000276E4
.text:000276E4                 STMFD   SP!, {R4-R7,LR}
.text:000276E8                 SUB     SP, SP, #0x3C   ; nHeight
.text:000276EC                 MOV     R6, R0
.text:000276F0                 BL      _OnInitDialog_CDialog__UAAHXZ ;
                             CDialog::OnInitDialog(void)
.text:000276F4                 BL      _AfxGetModuleState__YAPAVAFX_MODULE_STATE__XZ ; AfxGetModuleState(void)
.text:000276F8                 LDR     R1, [R0,#4]
.text:000276FC                 LDR     R0, [R1,#0x20]
.text:00027700                 BL      CFrameWnd__GetActiveView
.text:00027704                 LDR     R5, [R0,#0x48]
.text:00027708                 MOV     R1, #0x2340
.text:0002770C                 MOV     R2, #0
.text:00027710                 ORR     R1, R1, #8
.text:00027714                 MOV     R0, R5
.text:00027718                 BL      sub_11097C
.text:0002771C                 MOV     R1, R0
.text:00027720                 ADD     R0, R6, #0xD8
.text:00027724                 BL      _Attach_CGdiObject__QAAHPAX_Z ; CGdiObject::Attach(void *)
.text:00027728                 LDR     R3, [R6,#0x20]
.text:0002772C                 MOV     R0, #0x10       ; SHIDIF_FULLSCREENNOMENUBAR
.text:00027730                 STR     R0, [SP,#0x50+pshidi.dwFlags]
.text:00027734                 MOV     R7, #1
.text:00027738                 ADD     R0, SP, #0x50+pshidi ; pshidi
.text:0002773C                 STR     R7, [SP,#0x50+pshidi]
.text:00027740                 STR     R3, [SP,#0x50+pshidi.hDlg]
.text:00027744                 BL      SHInitDialog
.text:00027748                 LDR     R0, [R6,#0x4C]
.text:0002774C                 MOV     R1, #0          ; fShow
.text:00027750                 LDR     R0, [R0,#0x20]  ; hwndCB
.text:00027754                 BL      CommandBar_Show
.text:00027758                 LDR     R0, [R6,#0x20]  ; hwndRequester
.text:0002775C                 MOV     R1, #0xA        ; dwState
.text:00027760                 BL      SHFullScreen
.text:00027764                 LDR     R0, [R6,#0x20]  ; hWnd
.text:00027768                 BL      SetForegroundWindow
.text:0002776C                 LDR     R0, [R6,#0x20]  ; hwnd
.text:00027770                 MOV     R1, #1          ; nCmdShow
.text:00027774                 BL      ShowWindow
.text:00027778                 LDR     R0, [R6,#0x20]  ; hwnd
.text:0002777C                 ADD     R1, SP, #0x50+X ; prc
.text:00027780                 BL      GetWindowRect
.text:00027784                 LDR     R0, [SP,#0x38]
.text:00027788                 LDR     R4, [SP,#0x30]
.text:0002778C                 ADD     R2, R0, #0x1A
.text:00027790
.text:00027790 loc_27790                          ; DATA XREF: sub_1812B4+1AC
.text:00027790                 LDR     R1, [SP,#0x50+X] ; X
.text:00027794                 LDR     R3, [SP,#0x34]  ; nWidth
.text:00027798                 STR     R2, [SP,#0x50+nHeight]
.text:0002779C                 SUB     R2, R4, #0x1A   ; Y
.text:000277A0
.text:000277A0 loc_277A0                          ; DATA XREF: sub_17FDDC+6E4
.text:000277A0                                    ; sub_17FDDC+8C0
.text:000277A0                 LDR     R0, [R6,#0x20]  ; hWnd
.text:000277A4                 STR     R7, [SP,#0x50+bRepaint]
.text:000277A8
.text:000277A8 loc_277A8                         ; DATA XREF: sub_17FDDC:loc_1805A4
.text:000277A8                 BL      MoveWindow
.text:000277AC                 MOV     R1, #1
.text:000277B0                 MOV     R0, R6
.text:000277B4                 BL      sub_27930
.text:000277B8                 MOV     R0, R6
.text:000277BC
.text:000277BC loc_277BC                         ; DATA XREF: sub_1C2B14+358
.text:000277BC                                   ; sub_1C2B14+37C
.text:000277BC                 BL      GPS_Information
...

눈여겨볼 부분이 3군데 있습니다. 아래 함수 입니다.
  SHInitDialog
  GetWindowRect
  MoveWindow

공부차원에서 SHInitDialog를 MSDN에서 찾아보면 아래와 같습니다.
SHInitDialog
This function is primarily used to create a full-screen dialog box with the OK button in the navigation bar. The user interface for Smartphone uses many full-screen dialog boxes. Because the navigation bar has been moved to the top of the screen, the Done button has been visually removed from full-screen dialog boxes and replaced with an OK button in the navigation bar.
BOOL SHInitDialog (
  PSHINITDLGINFO pshidi
);

PSHINITDLGINFO 는 아래값을 사용하는 것으로 확인을 했습니다.
SHIDIF_FULLSCREENNOMENUBAR
  Sizes the dialog box FULL screen. Does not leave room at the bottom for a menu bar

말은 길었는데, 결국 화면 전체를 덮어 버리는 FullScreen처리를 하는 API호출입니다.
우리가 문제가 되었던 그 dialog를 설정하는것이 확실 함을 판단 할 수 있습니다.

자  다시 본래의 문제를 깨우치면 - 화면이 유지되지 못하고 다른 크기로 window가 '변신'한다는 것이죠!
넵 그 변신 code는 위에서 언급한 나머지 두 함수 호출에 의해서 행해짐을 추정할 수 있습니다.
GetWindowRect와 MoveWindow죠.

그냥 DialogBox만 띄울것이지 왜! 왜! Window크기를 막 돌려 버리냐는 것이죠! 왜!
앞서 언급 드렸지만 맵피는 지동 출력용 window를 dialog에서도 공유한다고 했습니다. 이런 공유자원을 이 dialog code에서 맘대로 MoveWindow를 하는 code가 있다는 것이 문제라는 것이죠.

자 그러면 해결 방법이 보입니다.
MoveWindow를 없애 버리면 되는 것이죠.

ARM에서 아쉬운 것이 x86에서 매우 유용한 NOP 명령어가 없습니다. NOP의 경우 1byte 쨔리라 땜방 처리에는 아주 편한 명령어 입니다.
그래서 ARM에서는 의미없는 연산으로 대체 해야 합니다. 아래 명령으로 대체 하면 됩니다.
ORR  R0, R0, #0   ==  00 00 80 E3

위에 나열된 리스트에서 MoveWindow를 없애 버립니다.
.text:000277A8           BL      MoveWindow  --> ORR R0, R0, #0 으로 패치
.text:000277AC           MOV     R1, #1

Patch를 위한 원본 Hex data는 아래와 같습니다. (유일하므로 찾아서 수정 하면 됩니다)
  A7 23 08 EB 01 10 A0 E3 

아래처럼 patch 합니다.
  00 00 80 E3 01 10 A0 E3

아래 부분만 수정하면 이번 설명에서 해야할 패치는 모두 다 입니다.
[A7 23 08 EB 01 10 A0 E3 ] --> [ 00 00 80 E3 01 10 A0 E3 ]


자~ 그리고 다시금 수정한 파일을 M480x 혹은 에뮬레이터에서 실행 해 봅니다.

냅!
예상한 대로 잘 표시 됩니다.
 앞서 캡쳐한 화면은 에뮬레이터에서 수정한 파일 실행 후 표시된 내용입니다.

이제 모든것이 다 된것 같습니다.
이제 GPS인식이 남았습니다.

그런데 역시나 맵피의 시리얼 포트 설정 문제인지 아무리 아무리 GPS설정을 잘해 주어도 내장 GPS를 제대로 잡지 못합니다. (iNavi도 이런 증상은 동일하다고 하더군요)

그래서 유명한 GpsGate라는 프로그램을 동원 해야 합니다.
이부분 까지는 맵피를 직접 수정하는 것이 쉽지 않기에 - 그냥 GpsGate를 사용 하도록 합니다.
(저도 그냥 GpsGate 사용! - 소스 없이 이런 버그 까지 고치는 것은 너무나 괴로울 것 같습니다)

그리고 맵피를 실행하고, GPS를 잡아 봅니다!
넵! GPS 잘 잡히더군요.

하지만 또 다른 문제가 등장 했습니다.
GPS인식 직후 또 다시 동일 문제 - 즉 화면 먹통이 되어 버렸습니다 @#!%#^$!%^
이쯤되면 짜증이 막~ 밀려 오죠 - 넵 진짜 짜증 '지대로' 입니다.
그러나 화면 먹통 문제는 앞서 언급한 부분에서 우리가 한번 해결 했습니다.

네! MoveWindow를 없애 버리면 해결 했던거죠.


이 부분은 또다시 별도의 글로 계속 정리 하도록 하겠습니다.
헥헥 - 이번글은 예상보다 길어진듯 합니다. 물론 실제 삽질은 몇배는 길었습니다.

----- TO BE CONTINUE~
다음글 보기


참고 자료

Windows CE Remote Spy 결과 - 단지 2개의 Window만 존재 확인


+ Recent posts