EncodePointer

Discussion in 'Реверсинг' started by 0x0c0de, 19 Nov 2008.

  1. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    395
    Reputations:
    297
    [EncodePointer].

    *my system: Windows XP SP3 [тестила и на win xp2 то же]

    Пишу сию небольшую заметочку про EncodePointer. Статьей это => не является.

    Некогда довелось мне заниматься VEH и писать плаг под Olly, перебирающий все обработчики в дебажимом процессе. И функция EncodePointer создала мне на системах с SP (в виндах без СП, как выяснилось этой функции нет вовсе) некоторые проблемы и при получении адреса вех-обработчика я извращалась с трейсом.
    Как вы можете прочесть в msdn

    Здесь все предельно понятно. И вех-обработчики в системах с SP как раз-таки подвергаются такой обработке.
    Функция шифрует адрес Ptr неким значением, уникальным для каждого процесса. Теперь попробуем разобраться в общих чертах как оно работает и откуда это значение берется.

    Код функции RtlEncodePointer

    Code:
     
    7C9133DF ntdll.RtlEncodePointer            /$  8BFF                      MOV EDI,EDI                                                ;  ntdll.7C910208
    7C9133E1                                   |.  55                        PUSH EBP
    7C9133E2                                   |.  8BEC                      MOV EBP,ESP
    7C9133E4                                   |.  51                        PUSH ECX                                                   ;  ntdll.7C9164EE
    7C9133E5                                   |.  6A 00                     PUSH 0                                                     ; /pReqsize = NULL
    7C9133E7                                   |.  6A 04                     PUSH 4                                                     ; |Bufsize = 4
    7C9133E9                                   |.  8D45 FC                   LEA EAX,[LOCAL.1]                                          ; |
    7C9133EC                                   |.  50                        PUSH EAX                                                   ; |Buffer = NULL
    7C9133ED                                   |.  6A 24                     PUSH 24                                                    ; |InfoClass = 24 (36.)
    7C9133EF                                   |.  6A FF                     PUSH -1                                                    ; |hProcess = FFFFFFFF
    7C9133F1                                   |.  E8 EAA3FFFF               CALL ZwQueryInformationProcess                             ; \ZwQueryInformationProcess
    7C9133F6                                   |.  8B45 FC                   MOV EAX,[LOCAL.1]
    7C9133F9                                   |.  3345 08                   XOR EAX,[ARG.1]
    7C9133FC                                   |.  C9                        LEAVE
    7C9133FD                                   \.  C2 0400                   RET 4
    
    То есть мы вызываем ZwQueryInformationProcess с InfoClass = SystemContextSwitchInformation (36) и ксорим с адресом, переданным функции в качестве аргумента. Ясное дело, что в ntdll более нам делать нечего, надо спускаться в ядро и смотреть откуда берется этот уникальный дворд.
    Загружаем ntoskrnl.exe в IDA и смотрим функцию NtQueryInformationProcess. Весь код функции, приводить, конечно, не буду, ибо нет смысла. Выделю только основные моменты. Найдем то место в функции, которое обрабатывает InfoClass = SystemContextSwitchInformation = 36 = 0x24


    Code:
    PAGE:0049CEFA                 mov     eax, [ebp+ProcessInformationClass]
    PAGE:0049CEFD                 push    16h
    PAGE:0049CEFF                 pop     ecx
    PAGE:0049CF00                 cmp     eax, ecx
    PAGE:0049CF02                 jg      loc_4A0E7F
    PAGE:0049CF08                 jz      loc_4A1379
     
    Ну тут ясно, что в eax в нашем случае (0049CF00) будет 24h, а в ecx 16h. Значит нам по адресу loc_4A0E7F.

    Code:
    loc_4A0E7F:                             ; CODE XREF: NtQueryInformationProcess(x,x,x,x,x)+53
    PAGE:004A0E7F                 add     eax, -17h       ; switch 15 cases
    PAGE:004A0E82                 cmp     eax, 0Eh
    PAGE:004A0E85                 ja      Invalid_InfoClass ; default
    PAGE:004A0E8B                 jmp     ds:off_4A18CA[eax*4] ; switch jump
    
    Мысленно(или на калькуляторе %) ) eax(==24h) +(-17h). Получаем 0D. Далее
    Выполняем джамп (при eax = 0d)

    Code:
    PAGE:004A0E8B                 jmp     ds:off_4A18CA[eax*4]
    
    Попадаем сюда

    Code:
    PAGE:004A0E92 SystemContextSwitchInformation_0:       ; DATA XREF: PAGE:off_4A18CA o
    PAGE:004A0E92                 cmp     edi, edx        ; case 0x24
    PAGE:004A0E94                 jnz     InfoLengthMismatch
    PAGE:004A0E9A                 cmp     [ebp+Handle], 0FFFFFFFFh
    PAGE:004A0E9E                 jnz     not_current_process
    

    Обратите внимание на сделанное мной обозначение. То есть, если передан хендл не текущего процесса, то

    Code:
    PAGE:004A1578 not_current_process:                    ; CODE XREF: NtQueryInformationProcess(x,x,x,x,x)+3FEF j
    PAGE:004A1578                                         ; NtQueryInformationProcess(x,x,x,x,x)+46BA j ...
    PAGE:004A1578                 mov     eax, STATUS_INVALID_PARAMETER
    
    Ну оно и верно. Если бы этот дворд можно было получить из другого процесса, было бы странно. Теперь продолжим. Если мы интересуемся своим процессом

    Code:
    PAGE:004A0EA4                 mov     eax, large fs:124h
    PAGE:004A0EAA                 mov     eax, [eax+44h]
    PAGE:004A0EAD                 mov     [ebp+var_34], eax
    PAGE:004A0EB0 get_dw:                                 ; CODE XREF: NtQueryInformationProcess(x,x,x,x,x)+18E71 j
    PAGE:004A0EB0                 mov     edi, [ebp+PEPROCESS]
    PAGE:004A0EB3                 add     edi, 258h
    PAGE:004A0EB9                 mov     eax, [edi]
    PAGE:004A0EBB                 test    eax, eax
    PAGE:004A0EBD                 jz      get_time
     

    Некоторым первые строки покажутся очень знакомыми. В eax после их исполнения окажется указатель на структуру EPROCESS текущего процесса. Далее мы добавляем к началу EPROCESS 258h. И берем по этому адресу dword.

    Если значение нулевое, то

    Code:
    PAGE:004A0EBB                 test    eax, eax
    PAGE:004A0EBD                 jz      get_time
    
    Получаем системное время

    Code:
    PAGE:004B5CF6 get_time:                               ; CODE XREF: NtQueryInformationProcess(x,x,x,x,x)+400E j
    PAGE:004B5CF6                 lea     eax, [ebp+CurrentTime]
    PAGE:004B5CF9                 push    eax             ; CurrentTime
    PAGE:004B5CFA                 call    _KeQuerySystemTime@4 ; KeQuerySystemTime(x)
    PAGE:004B5CFF                 mov     eax, large fs:_KPRCB
    PAGE:004B5D05                 mov     ecx, [eax+_KPRCB.KeSystemCalls]
    PAGE:004B5D0B                 xor     ecx, [eax+_KPRCB.InterruptTime]
    PAGE:004B5D11                 xor     ecx, [ebp+CurrentTime.HighPart]
    PAGE:004B5D14                 xor     ecx, [ebp+CurrentTime.LowPart]
    PAGE:004B5D17                 xor     eax, eax
    PAGE:004B5D19                 lock cmpxchg [edi], ecx
    PAGE:004B5D1D                 push    4
    PAGE:004B5D1F                 pop     edx
    PAGE:004B5D20                 jmp     get_dw 
    И дубль 2 =) Пробуем снова получить дворд…
    Смотрим описание функции KeQuerySystemTime



    Теперь мы знаем откуда берется это значение и можно написать драйвер, который по id процесса будет возвращать уникальный дворд. Использовать NtQueryInformationProcess мы не можем напрямую, поэтому просто осуществим аналогичные действия (начиная с метки get_timе я опустила, ибо лень, могу сказать, что все корректно определяется). И соответственно программа, которая этот драйвер использует. В самом драйвере пишем

    Code:
    ULONG ProcessDwordByProcessId(ULONG piD)
    {
    ULONG dwDbg;
    PEPROCESS cproc;
    if(NT_SUCCESS(PsLookupProcessByProcessId(piD,&cproc)))
    	 {
    	_asm{
    	mov edi,cproc
    	add edi,0x258 
    	mov edi,dword ptr[edi]
    	mov dwDbg,edi
    	    }
    return dwDbg;
    }
    return 0;
    
    }
    
    В вызывающей программе

    Code:
    DWORD GetPrDword(DWORD pID){
    //Аргумент - id процесса
    DWORD mag=0,BytesReturned=0;
    	if( !DeviceIoControl(hHandle,IOCTL_GET_PROCESS_DWORD,&pID, 4,&mag,4,&BytesReturned,	NULL ))
    	{
    		printf("Error in DeviceIoControl\n");
    		return 0;
    	}
    // возвращаемое значение - process dword
    return mag;
    }
    
    Скрин

    http://img56.imageshack.us/my.php?image=23297865ee9.jpg

    Program

    http://rapidshare.com/files/165383629/tst.rar.html
     
    1 person likes this.