심플스 - 프로그램과 책 이야기로 가득한 곳, (Simples.Kr)

  


   심플스 배너



HexRay는 IDA플러그인으로써 지금까지 디스어셈블리 된 코드로만 리버싱을 했어야 했는데 HexRay에 의해서 C언어로 코드를 볼 수 있게 되었습니다.

거 기다가 디컴파일 된 코드를 그대로 옮긴 후 컴파일을 하면 실제로 돌아가는 경우도 많이 있어서 상당히 대단하긴 한데 만약 특정 타켓 프로그램을 완전히 리버싱해야 할 경우 ( 어떠한 프로그램에 기능을 내 프로그램에서 쓰고 싶을 경우 ) 디컴파일에 많은 부분에 의존하여 해당 코드를 자신의 프로그램으로 옮기게 되는데 이 때 다음과 같은 부분에 주의해야 합니다.

다음은 SCSI Miniport 드라이버 코드를 디컴파일 한 것입니다.

void __stdcall sub_10546(PVOID HwDeviceExtension, int a2)
{
  int v2; // ebx@1
  unsigned __int8 v3; // al@7
  PVOID v4; // edi@7
  char v5; // zf@13
  signed int v6; // ST04_4@15
  void *v7; // ST08_4@15
  char v8; // ST0C_1@15
  ULONG v9; // ecx@16
  char v10; // al@25
  int v11; // edx@26
  int v12; // ecx@26
  int v13; // ecx@11
  int v14; // ecx@12
  int v15; // ecx@21
  int v16; // ecx@22
  int v17; // edx@26
  unsigned __int8 v18; // [sp+5h] [bp-67h]@17
  int v19; // [sp+Ch] [bp-60h]@17
  char v20; // [sp+4h] [bp-68h]@17
  int v21; // [sp+10h] [bp-5Ch]@17
  int v22; // [sp+14h] [bp-58h]@17
  int v23; // [sp+18h] [bp-54h]@17
  int v24; // [sp+1Ch] [bp-50h]@17
  int v25; // [sp+20h] [bp-4Ch]@17
  signed int v26; // [sp+24h] [bp-48h]@17
  signed int v27; // [sp+64h] [bp-8h]@18
  signed int v28; // [sp+68h] [bp-4h]@18

  v2 = a2;
  if ( !*(_BYTE *)(a2 + 5) )
  {
    if ( *(_BYTE *)(a2 + 6) )
    {
      LOBYTE(a2) = 33;
      goto LABEL_32;
    }
    if ( *(_BYTE *)(a2 + 7) )
    {
      LOBYTE(a2) = 32;
      goto LABEL_32;
    }
    v4 = ScsiPortGetLogicalUnit(HwDeviceExtension, 0, 0, 0);
    v3 = *(_BYTE *)(v2 + 48);
    if ( (signed int)v3 > 37 )
    {
      if ( v3 != 40 )
      {
        if ( v3 != 42 )
        {
          v15 = v3 - 47;
          if ( v15 )
          {
            v16 = v3 - 53;
            if ( !v16 )
              goto LABEL_28;
            v5 = v3 == 59;
LABEL_24:
            if ( !v5 )
              goto LABEL_25;
LABEL_28:
            LOBYTE(a2) = 1;
            goto LABEL_32;
          }
        }
      }
      BYTE3(v17) = 0;
      *(_WORD *)((char *)&v17 + 1) = *(_BYTE *)(v2 + 50);
      LOBYTE(v17) = *(_BYTE *)(v2 + 51);
      v11 = *(_BYTE *)(v2 + 53) | ((*(_BYTE *)(v2 + 52) | (v17 << 8)) << 8);
      BYTE3(v12) = 0;
      *(_WORD *)((char *)&v12 + 1) = *(_BYTE *)(v2 + 55);
      LOBYTE(v12) = *(_BYTE *)(v2 + 56);
      if ( (unsigned int)(v12 + v11) > 0x4000 )
        goto LABEL_25;
      if ( v3 == 47 )
        goto LABEL_28;
      v9 = v12 << 9;
      v8 = v3 == 40;
      v7 = (char *)v4 + 512 * v11;
    }
    else
    {
      if ( v3 == 37 )
      {
        v8 = 1;
        v7 = &v27;
        v27 = -12648448;
        v28 = 131072;
        v6 = 8;
      }
      else
      {
        if ( !v3 )
          goto LABEL_28;
        if ( v3 == 18 )
        {
          memset(&v18, 0, 0x5Fu);




첫 번째로 바로 위에 부분 memset이 있는데 v18이라는 변수는
unsigned __int8 v18; // [sp+5h] [bp-67h]@17
이와 같이 선언되어 있습니다.

당연히 memset을 할 경우 1바이트 짜리 변수이므로 에러가 발생하게 됩니다.
사 실 보시다시피 [sp+5h]로 되어 있는데 v18은 1byte 짜리 변수가 아니라 스택의 0x5F 크기의 배열로 선언되어 있는 변수일 가능성이 높습니다. 또한 v18밑에 선언 된 변수들은 각각의 변수들이 아니라 v18에 포함되는 변수입니다.

다시 한 번 본다면..

  unsigned __int8 v18; // [sp+5h] [bp-67h]@17
  int v19; // [sp+Ch] [bp-60h]@17
  char v20; // [sp+4h] [bp-68h]@17
  int v21; // [sp+10h] [bp-5Ch]@17
  int v22; // [sp+14h] [bp-58h]@17
  int v23; // [sp+18h] [bp-54h]@17
  int v24; // [sp+1Ch] [bp-50h]@17
  int v25; // [sp+20h] [bp-4Ch]@17
  signed int v26; // [sp+24h] [bp-48h]@17
  signed int v27; // [sp+64h] [bp-8h]@18
  signed int v28; // [sp+68h] [bp-4h]@18

변수의 선언은 틀리지만 뒤에 주석이 처리 된 [sp+5h], [sp+Ch].......는 맞을 가능성이 높습니다.
하지만 실제로 저렇게 변수를 선언한다고 해서 WDK 컴파일러에서 저런 스택을 만들지도 확실하지 않으므로 만약 v18에 경우에는
배열로 선언되어야 할 것입니다.
위에 상황과 프로그래머들이 작성하는 코드 스타일을 본다면

BYTE pBuffer[0x5F];
......
memset(pBuffer, 0, sizeof(pBuffer)); 가 원본 코드일 가능성이 높습니다. ( 물론 배열이 아니라 구조체일가능성도 있겠죠.. 그러한 부분은 버퍼를 어떻게 사용하느냐에 따라 다른것이고.. )



두 번째로... Signed와 Unsigned 와 관련해서 문제가 있을 수 있습니다.

위에 코드에서..

      BYTE3(v17) = 0;
      *(_WORD *)((char *)&v17 + 1) = *(_BYTE *)(v2 + 50);
      LOBYTE(v17) = *(_BYTE *)(v2 + 51);
      v11 = *(_BYTE *)(v2 + 53) | ((*(_BYTE *)(v2 + 52) | (v17 << 8)) << 8);
      BYTE3(v12) = 0;
      *(_WORD *)((char *)&v12 + 1) = *(_BYTE *)(v2 + 55);
      LOBYTE(v12) = *(_BYTE *)(v2 + 56);
      if ( (unsigned int)(v12 + v11) > 0x4000


이 부분은 밑에 디스어셈블리 코드와 같습니다.

.text:00010678                 xor     edx, edx
.text:0001067A                 mov     dh, [ebx+32h]
.text:0001067D                 mov     dl, [ebx+33h]
.text:00010680                 shl     edx, 8
.text:00010683                 or      edx, ecx
.text:00010685                 movzx   ecx, byte ptr [ebx+35h]
.text:00010689                 shl     edx, 8
.text:0001068C                 or      edx, ecx
.text:0001068E                 xor     ecx, ecx
.text:00010690                 mov     ch, [ebx+37h]
.text:00010693                 mov     cl, [ebx+38h]
.text:00010696                 mov     esi, ecx
.text:00010698                 lea     ecx, [esi+edx]
.text:0001069B                 cmp     ecx, 4000h

그리고 디컴파일 된 코드에서는 형변환을 다음과 같이 하고 있습니다.
*(_BYTE *)

_BYTE는 IDA에 HexRay에서 정의한 것인데 plugins\defs.h 파일에 정의되어 있으며

#define _BYTE  char

위와 같이 선언되어 있습니다.

즉, *(_BYTE *) = *(char *) 입니다.
이것은 Sigined입니다.
그러나 디스어셈블리 된 코드에서는 일부 데이터를 이동 시 movzx 명령어를 사용하고 있습니다.
movzx명령어는 부호가 없는 Unsigined를 처리하므로 이러한 경우 문제가 발생합니다.

실제로 저 코드를 WDK로 컴파일 후 WinDbg로 디스어셈블링 해보면
movsx로 변환됩니다. 따라서 잘 못 된 결과를 초래할 수 있습니다.

따라서 저런 경우에는 강제적으로 *(unsigned char *)로 바꿔서 컴파일해야 합니다.

세번째로 간혹 디컴파일을 하게 되면 콜링 컨벤션이 제대로 안 나오는 경우가 있는데 다음과 같은 경우입니다.

char __userpurge sub_10486<al>(int a1<esi>, char a2)

이 러한 경우에는 stdcall 도 아니고 fastcall도 아니고 사용자 지정 콜링 컨벤션이므로 레지스터를 이용해 넘기는건지 어떻게 파라메터를 전달해야 하는지 봐야합니다. 일단 대충 봐서는 esi레지스터를 사용해서 넘긴다는 것인데 웃긴건 함수호출하는 부분에서도 esi레지스터에 어떤값이 들어갈지도 신경써야 한다는 겁니다. 즉, 디컴파일을 하면서 디스어셈블리 된 코드도 자세하게 봐야 된다는거죠....


하여튼 저같은 경우에 다른 드라이버를 리버싱할 경우 이런적이 있었습니다.
적어도 두번째에 경우인 movsx, movzx에 따라서 형변환 관련 디컴파일 부분은 버그로 간주해야 될꺼 같습니다.

HexRay 플러그인에 너무 기대는 부분도 있지만 그만큼 가치가 있는 플러그인입니다.

profile

esniper

2010.09.05 21:17:54

HexRay가 버전에 따라서 차이가 많이 나던데, 이런 부분은 업그레이드 되면서 차차 개선이 될 것 같네요.

암튼 일일이 눈으로 보는걸 많이 개선해주니 정말 편리하죠~

List of Articles
번호 제목 글쓴이 날짜 조회 수
163 문서자료 json 문법 체크 사이트 Lyn 2012-02-28 4553
162 Windows Research SCSI Miniport - IOCTL_SCSI_PASS_THROUGH_DIRECT ( Windows ... lain 2011-06-11 7870
161 Windows Research 커널레벨에서 IAT Hook lain 2011-06-11 9232
160 Windows Research DEVICE_OBJECT에 대한 보안 디스크립터 변경 lain 2011-06-11 7001
159 Windows Research IE Cache 경로 변경하기 lain 2011-06-09 25636
158 Windows Research 32Bit 윈도우즈에서 실제 물리메모리크기 얻어오기 lain 2011-06-09 7275
157 Windows Research VMWare 탐지 기법 우회 - 2 [2] lain 2011-04-04 17372
156 Linux Tip 우분투에 IRC서버 설치후 닉네임 길이와 동접자 조절. [1] 오랑캐꽃 2010-11-26 10287
155 Linux Tip VirtualBox에 우분투 설치 후에 내부 네트워크 접속 설정하기 file esniper 2010-09-10 24247
154 Linux Tip VirtualBox에서 오른쪽 CTRL키 사용하기(VitualBox 호스트키) file [2] esniper 2010-09-10 11509
153 Linux Tip 우분투 설치 후 putty에서 한글 안 깨지도록 설정하기 file esniper 2010-09-10 10980
152 Linux Tip 우분투 ssh or mysql 서버 접속지연이 있는 경우 해결책 esniper 2010-09-10 10616
151 문서자료 고품질의 무료 아이콘들 [3] esniper 2010-09-08 12850
150 Windows Research Sysinternals - Filemon source code file [5] lain 2010-09-04 9827
149 Windows Research Sysinternals - Regmon source code file [1] lain 2010-09-04 9448
148 Windows Research PostQueuedCompletionStatus 함수에 실패 여부도 고려해야 할까? lain 2010-09-04 8292
147 Windows Research SetEvent 함수는 언제 실패할까? lain 2010-09-04 9808
146 Windows Research 커널모드 드라이버에서 사용하는 시간관련 매크로 lain 2010-09-04 7911
145 Windows Research 화면캡쳐방지는 어떻게 구현될까? [3] lain 2010-09-04 16624
144 Windows Research IRP Completion Hook file lain 2010-09-04 7824

  • 이용약관
  • 개인정보취급방침
  • 사이트맵