메신저가 message hook을 하는 이유




요즘 메신저 안 쓰시는 분들은 거의 없을겁니다.
그와 관련하여 자주 듣는 질문 중 하나가, 왜 메신저들이 남의 프로세스에 DLL을
집어넣느냐는
것입니다.

실제로 네이트온 같은 경우도 NateOnHook40u.dll이란 넘을 모든 프로세스에 집어넣죠.

사용자 삽입 이미지


예전에 신영진님과 이거에 대해 간단한 토론?을 한 적이 있는데요,
뭐 일단 그때 영진씨한테 들은 내용을 바탕으로 결론부터 말씀드리면 메신저가 이같이
dll을 넣는 이유는 "자리비움"기능을 구현하기 위해서라고 합니다.
그때 추가로 이루어졌던 토론이 꼭 메시지훅을 해서 자리비움을 구현할수밖에 없느냐...란 얘긴데
그건 결론이 나오진 않았었구요 :p

어쨌든 그렇게 얘기하고 머 멀 넣든 말든 별 문제는 없으니까 이후로도 걍 신경을 껏지만
얼마전에 저희 팀 메일로 메신저 부류가 왜 dll을 넣는지 궁금하다는 문의가 있어서
간만에 옛기억을 떠올려보며 한번 뜯어봤습니다.

간단한 화면캡쳐를 통해 그 원리를 말씀 드려 보겠습니다.

메신저 dll을 IDA로 간단히 본 화면입니다.
2008년도부터 리버스 엔지니어링에 대한 법적인 조항이 생긴다는 얘기가 있어서
무슨 프로그램인지에 대한 언급은 생략하겠습니다 :p

일단 아래 화면은 dll을 넣기 위해 message hook을 하는 코드입니다.
Hook id는 2번과 7번이 들어가는데 이는 각각 키보드 훅, 마우스 훅 입니다.
키보드 입력과 마우스 움직임을 감지하기 위한 것입니다.

#define WH_KEYBOARD         2
#define WH_MOUSE            7

사용자 삽입 이미지


후킹 프로시져인 fn과 sub_10001110은 동일한 기능을 하고 있으므로 하나만 보겠습니다.
마우스 훅에 대한 프로시져인 sub_10001110를 볼까요,

보시면 머 잡다한 짓을 하다가 sub_1000830E 를 부르는데요 쟤가 바로 시간을 계산하는
부분입니다. 안으로 들어가보면 GetLocalTime과 GetSystemTime등으로 난리부르스를 떨고
적절한 상수를 eax에 뱉어내줍니다. 그 값이 time_t 타입의 현재 시간을 나타내는 값입니다.
어쨌든 머 현재 시간을 계산하는 부분이라고 생각하시면 되겠네요

사용자 삽입 이미지


아 참고로 쓸데없는 부연지식을 첨가해보자면, 정확히는 저 부분은 개발자가 구현한 시간 루틴이 아니고
우리가 많이 쓰는 time()함수입니다. 즉, time_t time( time_t *timer ); 이거 입니다.
실제로 time()함수는 리버싱을 해보신분은 아시겠지만 저 함수가 실제로 있는것이 아니고
컴파일러가 GetLocalTime과 GetSystemTime 등을 조합해서 만들어주는 함수입니다.
따라서 빌드 후에 확인해보면 dll로 점프하지 않고 exe내부의 call문으로 생성되죠.

궁금하신 분들은 MSDN의 time() 예제코드를 빌드해보시고 직접 리버싱 해보세요 :p
참고로 IDA는 똘똘이스머프이기 때문에 그 call문을 time()이라고 완벽히 해석해주지만
OllyDBG는 바보이기 때문에 걍 흔해빠진 call문중의 하나로 보여줍니다 ㅋ

사용자 삽입 이미지


어쨌든 훅을 걸고 시간을 계산했습니다. 그리고 time_t 형태의 리턴값을 받았구요
위 이미지를 보시면 걔를 전역변수 어딘가에 넣어줍니다. 이곳은 어디일까요

네~ 정답은 쉐어드 섹션이 되겠습니다. 다른 프로세스에서(정확히는 메신저의 exe인
메인 모듈에서) 타 영역의 프로세스에도 접근하기 위하여 이같은 섹션을 만들어 두고
거기에 현재 시간 값을 가져와 비교하는 것으로 확인됩니다.


사용자 삽입 이미지

그럼 정리해 보겠습니다.

1) 메시지 훅을 걸어 모든 프로세스에 DLL 주입
2) 사용자가 마우스를 움직여서 훅이 걸리면 현재의 시간 shared section에 기록.
3) exe에서 shared section을 읽어와서 사용자가 마지막으로 마우스를 움직였을때의 시간과
   현재 시간을 비교
4) 그 시간이 3분 등 자리비움 설정 시간을 넘어서면 현재 상태를 "자리 비움"으로 변경  
5) 다른 어떤 프로세스가 포그라운드로 오게 되어도 DLL이 들어가 있으므로 역시
   마우스 움직임 캐취 가능

자 대충 이정도 알고리즘이 되겠네요 :p

여기서 수정 또는 덧붙힐 내용이 있습니다.

먼저 exe에서는 키보드 훅과 마우스 훅 외에 WH_CBT 훅까지 걸고 있습니다.
WH_CBT 훅은 윈도우가 생성/소멸/활성화 될때의 훅 체인을 걸 수 있습니다.
따라서 새 프로그램이 실행되어도 dll 을 주입할 수 있는 이유는 이 훅 때문이겠죠.

하지만 이로써 알 수 있는 부분은, 초반에 "모든 프로세스에 DLL을 주입.."이라고 했는데
CBT Hook에 의한 것이므로 정확히는 "윈도우를 가진 모든 프로세스"가 되겠죠 :)
따라서 윈도우가 없는 프로세스는 dll이 들어가지 않을거구요 :p

대충 이정도로 정리를 해보았습니다.

그런데 지금 글을 쓰면서 확인해보니 MSN 8.1은 더이상 남의 프로세스에 dll을 집어넣지 않네요?
예전에는 분명히 넣었던 것을 확인했는데 현재 dll 리스트를 보니 확실히 아무것도 없습니다.
MSN 8.1은 영진씨와 고민했던 "메시지훅을 하지 않고 자리비움 기능을 구현" 과제를 해결했나 보네요 :p

나중에 시간이 날 때 msn도 한번 살펴보도록 하겠습니다(혹시 이미 알고계신 분 있으시면 알려주세요)


2007년 12월.


Posted by window31


댓글을 달아주세요

  1. s
    2007.12.28 08:05
    댓글 주소 수정/삭제 댓글
    블로그에 맨날 어려운 얘기밖에 없삼 :(
    어제 디지털 포트리스 라는 책 보는데 창문 생각이 났3.ㅋㅋ
    그리고 예쁜거 당신이 짱먹으3-_-;;
    • 2007.12.28 16:46
      댓글 주소 수정/삭제
      디지털 포트리스? 너무 승격시켜주는거아냐 ㅋ
      난 일개 쪼렙이 불과하3
  2. 2007.12.28 08:19
    댓글 주소 수정/삭제 댓글
    안녕하세요. 첫 댓글 다는 것 같네요. 재밌게 잘 봤습니다. 말씀대로 네이트온의 저 dll은 자리비움을 위해서 있는 것이 맞습니다. 그리고 CBT 훅까지 거는 이유는 아마도 전체화면 프로그램이 떴을 때 자동으로 busy로 바꿔주기 위해 넣었을 것 같습니다. 그래서 윈도우를 만드는 (즉, 메세지 큐가 생성되어 마우스/키보드 입력을 받는) 프로세스에게 다 dll을 찔러넣는 것 같네요.

    그러나 Windows 2000 이후에서는 GetLastInputInfo 라는 함수로 간단히 자리비움을 구현할 수 있습니다. Windows 98에서는 이 함수가 없어서 어쩔 수 없이 훅을 써야만 했죠. 그래서 MSN은 이걸 썼나 봤는데 아무리 찾아봐도 이 함수를 쓰지는 않았네요. 그리고 네이트온처럼 dll을 돌리지도 않고... 좀 들여다 봤는데 저는 리버스엔지니어링에 대한 경험이 없어서 잘 모르겠습니다~

    3~4년 전의 네이트온의 경우에는 이 hook dll이 문제가 있어서 제가 만든 프로그램 위에서 WM_MOUSE* 메세지를 처리할 때 지나치게 CPU 사용률이 올라가기도 했답니다. 한참을 고민하다가 혹시~ 하면서 네이트온의 hook dll을 없애고 해보니 잘 되더라는 ㅎㅎ 그 이후로 한 동안 네이트온은 쳐다보지도 않았습니다 --;
    • 2007.12.28 17:02
      댓글 주소 수정/삭제
      유명하신 블로거 님께서 방문해주셨네요^^
      댓글 매우 감사드립니다 ㅎㅎ
      GetLastInputInfo 이런 좋은 API가 있었네요
      방금 MSN을 살펴보니 역시 GetLastInputInfo()로 자리비움을 구현해 놨습니다.
      msnmsgr.exe 메인 모듈의 (build : Sat Jan 20 05:43:22 2007) 0x7470C0 번지에서
      해당 함수를 사용합니다.
      함수 포인터를 중간에 GetProcAddress로 얻어서 사용하기 때문에
      PE의 Import Table 정보에는 나오지 않는거 같네요 :p
      어쨌든 지금 현재 MSN은 마소에서 2000이하는 지원을 중단 선언한 관계로
      9x에 대한 망설임 없이 이 기능으로 구현을 해놓은 것 같습니다.
      (MSN홈피를 보니 최소 환경이 XP이상으로 되어 있군요 ㅋㅋ)
      저도 예전에 메신저들의 메시지훅으로 문제가 많다는 얘길 들은적은 있는데
      제가 당해본적은 별로 없어서 ㅎㅎ 그닥 중요하게 생각진 않았었습니다.
      걍 개인적인 생각으론 네이트온이 GetLastInputInfo를 쓰지 않는 이유는
      아직 9x도 지원하기 때문에 메시지훅으로 해놓은 거라 예상됩니다
      9x쓰려면 어파치 메시지훅을 해야 하고 개발자가 괜히 os가려서 각각 다른 짓 하긴시러서
      그렇게 코딩해놓은 거라고 생각됩니다
      경험상으로도 그럴 거 같고요 ㅋ
      아무튼 정보 감사드립니다 ^^
  3. seyool
    2007.12.28 09:38
    댓글 주소 수정/삭제 댓글
    DllAdmin은 직접 개발하신 툴인가 보네요 ㅎㅎ
    • 2007.12.28 17:03
      댓글 주소 수정/삭제
      네, 한 2년반전쯤에 만든거 같은데....
      원랜 Dll injector를 사용하고 잇엇지만,,
      백신들이 그넘을 하도 바이러스로 잡아싸서
      짱나서 하나 만들었습니다.
      또 그당시에는 IceSword의 존재도 몰랐기 때문에
      free기능까지 같이 넣었구요 ㅋ
  4. 2007.12.28 17:20
    댓글 주소 수정/삭제 댓글
    오.. 감사합니다. 그렇죠.. dll을 나중에 얻어서 GetProcAddress로 구현할 수가 있다는 것을 깜빡했네요~ 그나저나 그렇다면 전체화면이 되었을 때 busy가 되는 것은 어떻게 구현할까요? 흠.. 훅을 쓰면 전체 윈도우에서 날라오는 WM_SIZE 같은 메세지를 들여다보면 될 것 같은데 궁금하네요..
    • 2007.12.29 19:56
      댓글 주소 수정/삭제
      네 한번 나중에 분석해볼께요 ㅎㅎ
  5. 3456
    2009.05.15 04:13
    댓글 주소 수정/삭제 댓글
    안녕허세요
  6. 3456
    2009.05.15 04:13
    댓글 주소 수정/삭제 댓글
    안녕허세요
  7. 2010.11.03 10:18
    댓글 주소 수정/삭제 댓글
    오 재밌다..
    이런게 재밌는걸 보면 참 적성에 맞는거 같기도 하고.

    디버깅 중에 위DLL이 호출되서 뭔짓인가 하고 검색해 봤는데
    잼나네요.

    글 잘 보고 갑니다. 2007년도 글이군요.
  8. 2020.03.10 14:08 신고
    댓글 주소 수정/삭제 댓글
    무려 13년 전 글이군요 아직도 재미있게 읽고 지나갑니다

BLOG main image
by window31

카테고리

분류 전체보기 (285)
Reverse Engineering (22)
C, C++ (20)
Kernel (8)
Guitar (19)
잡담 (79)
etc (8)
who am i (8)
보안 이야기 (89)
Tools (3)
월간 마이크로소프트웨어/그.. (28)

글 보관함