检测并禁用隐藏服务

文章属性:原创
文章提交:linux2linux (linux2linux_at_163.com)

隐藏服务的概念是由hxdef 和rootkit这些后门工具提出的。这些后门工具通过挂钩系统本地调用来隐藏自己,原本通过调用Windows API调用查看系统服务的企图都是徒劳的。所以这时的系统是不可靠的,不值得信任的。目前针对查找隐藏服务的工具已经有很多,比如IceSword,knlsc,FHS等等。虽然这些软件都是免费的,但是它们到目前为止都不是开源,所以将自己的实现版本展示出来,正如knlsc的作者所说的那样,这是一个简单的小程序。

Knlsc是通过将%SystemRoot%/System32/Config/System这个Hive文件转储出来,提取出ControlSet001/Services的子项再与RegEnumKeyEx的输出结果进行比对,发现若是在RegEnumKeyEx的输出结果中没有的子项就可以认为是一个隐藏的服务。当然knlsc还认为隐藏服务必须同时拥有ImagePath,Start,Type三个键值。据说knlsc运行时还将从资源段中放出一个驱动程序,但是估计这个驱动是假的。将knlsc托壳后用VC从资源段中导出的文件是一个没有EntryPoint但有MZ标志的驱动,没有办法进行反汇编。或许作者使用了SMC技术,放出资源文件后在进行修改,在执行文件中也有NtLoadDriver的调用片段,但是同一作者的knlps中的资源驱动却未作任何的处理。要实现检测隐藏服务的功能其实没有必要使用驱动程序,即使可以验证knlsc驱动的真实性。直接对Hive文件的转储也不是必须的,虽然这只要通过修改Gary Nebbett的示例代码就可做到。

Hive文件的转储可以通过RegSaveKey函数来进行,rootkitrevealer就是使用这个API的扩充函数RegSaveKeyEx工作的,至少到目前为止还没有挂钩这类函数的后门,但是世上没有永远的安全,在理论上是可行的,可能不得不对该函数的输出文件进行处理,这将在一定程度上影响该函数的执行时间。使用该函数时还必须赋予调用进程以SE_BACKUP_NAME权限。

在实现中将“HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services”的子项都转储为Hive格式文件(使用DumpServiceInfo函数),存放在C:\tmp.hive,在C盘下不可有同名文件,否则会发生Dump错误。现在的问题是如何对Hive格式文件进行处理,在这一点上必须感谢Petter Nordahl-Hagen所写的NT Registry Hive access library,它是The Offline NT Password Editor的一部分。本人的实现很大程度上就是参照他的程序,然而这个库工作在Linux环境,但是它向VC编译器移植的工作量是极少的,只需稍加修改。

1.将 #include  去掉

2.将程序中三处的

#define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs )

改为

#define D_OFFS(o) ( (int *)&(key->o)-(int *)hdesc->buffer-vofs )

因为在VC中无法打印void * 类型,只得改为int * 。

3.将程序中唯一的一处使用snprintf函数该为_snprintf,即

snprintf(path,maxlen,”(…)%s”,tmp);改为

_snprintf(ptth,maxlen,”(…)%s”,tmp);

4.添加两个VC不支持的函数来使编译通过

void bzero(void *s,int n)

{

memset(s,0,n);

}

int strncasecmp(const char *s1, const char *s2, size_t n)

{

return _stricmp(s1,s2);

}

为了表示对Petter Nordahl-Hagen的尊重,我不再修改他的库文件ntreg.c和ntreg.h(除了以上移植的需要),而是将所有需要重写和添加的函数放到KHS.C文件中,这样可以使原来的库文件保持其独立性。

由于在Petter库中openHive函数使用open 和 read 函数来进行hive文件的读取,在VC条件下的read函数有一个问题,每当文件中存在一个0x1a的二进制数值时,read函数就会在那儿终止,这样就会导致hive文件无法完全导入。所以就使用了Windows API重写openHive,命名为My_openHive。相应的还重写了closeHive,writeHive并加上了前缀My_。

随后GetPatterns函数将使用RegEnumKey枚举的服务键值名称保存在pattern的全局变量指针中,为以后的匹配作准备。ShowHideService函数是由nk_ls函数改写的,将由Hive文件导出的buffer中的服务名称与pattern[I]作比较,这个比较过程使用CompareHive函数。若比较结果为相同,CompareHive会将pattern[I]置为NULL,以提高匹配速度,或许有更好的匹配算法,但在这个小程序中也不使用了。若结果不同,则说明该服务是隐藏的,显示出该隐藏服务的名称,文件路径(ShowPathImage是由cat_vk改写的),启动类型和服务类型,并将该隐藏服务的启动类型改为SERVICE_DISABLED。如果存在隐藏服务并且禁止了该隐藏服务(仅在buffer中的修改),通过调用My_writeHive将修改过的hive 的buffer保存在C:\tmp2.hiv文件中,此时提供用户一个选择“是否要禁用隐藏服务”,默认的选择是“否”,如果用户确实想要禁用,可输入“Yes”或“Y",接着使用RestoreServiceInfo函数将C:\tmp2.hiv文件导回原来的%SystemRoot%/System32/Config/System这个Hive文件中,这一步由系统自己来完成,值得一提的是Win32函数RegRestoreKey即使在dwFlags参数中使用了REG_FORCE_RESTORE (8) ,在第一次调用时往往会错误返回,一般在第二次调用就会成功,所以就使用了一个循环直到它成功后为止,并且调用它的进程需要有SE_RESTORE_NAME的权限。

至于让隐藏服务真正失去作用,仍然需要重新启动计算机之后。

下面给出KHS.C的完整源代码:

/*

* KHS.cpp – Kill Hide Services v0.1

* Copyright (c) 2005 linux2linux.

*

* It takes notes from knlsc and FHS.

* Thank you, Petter Nordahl-Hagen, for your “NT Registry Hive access library”

*

* Freely distributable in source or binary for noncommercial purposes.

*

* THIS SOFTWARE IS PROVIDED BY PETTER NORDAHL-HAGEN `AS IS” AND

* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE

* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL

* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS

* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY

* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

* SUCH DAMAGE.

*

*/

#include “ntreg.h”

#include

char **pattern;

int pattern_count;

int nohideservice;

int ischange;

extern char *val_types[REG_MAX+1];

struct hive *My_openHive(char *filename, int mode);

void My_closeHive(struct hive *hdesc);

int CompareHive(char *sample)

{

int i;

for(i = 0; i < pattern_count ; i++)

{

if(pattern[i]!=NULL)

{

if( strcmp ( sample , pattern[ i ] ) == 0 )

{

free(pattern[i]);

pattern[i]=NULL;

return 1;

}

}

}

return 0;

}

//Because read can't work well in windows.

//while it read 0x1a from the opened file, the function read will stop there.

//I don't know the reason why, it work well in linux envoriment.

// read the dumped hive file to fill hive struct.

// return the point

struct hive *My_openHive(char *filename, int mode)

{

HANDLE hFile;

int szread;

struct hive *hdesc;

int vofs;

unsigned long pofs;

char *c;

struct hbin_page *p;

struct regf_header *hdr;

int verbose = (mode & HMODE_VERBOSE);

create(hdesc,struct hive,1);

hdesc->filename = str_dup(filename);

hdesc->state = 0;

hdesc->size = 0;

hdesc->buffer = NULL;

hFile = createFile(hdesc->filename,

GENERIC_READ, // open for reading

0, // do not share

NULL, // no security

OPEN_ALWAYS, // existing file only

FILE_ATTRIBUTE_NORMAL, // normal file

NULL); // no attr. template

if (hFile == INVALID_HANDLE_VALUE)

{

printf(“Could not open hive file.”); // process error

return 0;

}

/* Read the whole file */

hdesc->size = GetFileSize(hFile, NULL);

ALLOC(hdesc->buffer,1,hdesc->size);

ReadFile(hFile, (void *)hdesc->buffer, hdesc->size, &szread, NULL);

CloseHandle(hFile);

if (szread < hdesc->size) {

printf(“Could not read file, got %d bytes while expecting %d\n”,

szread, hdesc->size);

My_closeHive(hdesc);

return(NULL);

}

/* Now run through file, tallying all pages */

/* NOTE/KLUDGE: Assume first page starts at offset 0×1000 */

pofs = 0×1000;

hdr = (struct regf_header *)hdesc->buffer;

if (hdr->id != 0×66676572) {

printf(“openHive(%s): File does not seem to be a registry hive!\n”,filename);

return(hdesc);

}

for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c);

hdesc->rootofs = hdr->ofs_rootkey + 0×1000;

while (pofs < hdesc->size) {

#ifdef LOAD_DEBUG

if (verbose) hexdump(hdesc->buffer,pofs,pofs+0×20,1);

#endif

p = (struct hbin_page *)(hdesc->buffer + pofs);

if (p->id != 0x6E696268) {

printf(“Page at 0x%lx is not ‘hbin’, assuming file contains garbage at end”,pofs);

break;

}

hdesc->pages++;

#ifdef LOAD_DEBUG

if (verbose) printf(“\n###### Page at 0x%0lx has size 0x%0lx, next at 0x%0lx ######\n”,pofs,p->len_page,p->ofs_next);

#endif

if (p->ofs_next == 0) {

#ifdef LOAD_DEBUG

if (verbose) printf(“openhive debug: bailing out.. pagesize zero!\n”);

#endif

return(hdesc);

}

#if 0

if (p->len_page != p->ofs_next) {

#ifdef LOAD_DEBUG

if (verbose) printf(“openhive debug: len & ofs not same. HASTA!\n”);

#endif

exit(0);

}

#endif

vofs = pofs + 0×20; /* Skip page header */

#if 1

while (vofs-pofs < p->ofs_next) {

vofs += parse_block(hdesc,vofs,verbose);

}

#endif

pofs += p->ofs_next;

}

return(hdesc);

}

void My_closeHive(struct hive *hdesc)

{

FREE(hdesc->filename);

FREE(hdesc->buffer);

FREE(hdesc);

}

int My_writeHive(struct hive *hdesc)

{

HANDLE hFile;

DWORD dwBytesWritten;

hFile = createFile(“C:\\tmp2.hiv”,

GENERIC_WRITE, // open for writing

0, // do not share

NULL, // no security

create_ALWAYS, // open or create

FILE_ATTRIBUTE_NORMAL, // normal file

NULL);

if(hFile == INVALID_HANDLE_VALUE)

{ printf(“Can’t open dump file”);

return 0;

}

WriteFile(hFile, hdesc->buffer, hdesc->size,&dwBytesWritten, NULL);

if(dwBytesWritten != hdesc->size)

{

printf(“WriteHive error\n”);

}

CloseHandle(hFile);

return 0;

}

void CleanPatterns()

{

int i;

if(pattern!=NULL)

{

for(i = 0; i < pattern_count; i++)

{

if(pattern[i]!=NULL)

free(pattern[i]);

}

free(pattern);

}

}

void GetPatterns()

{

HANDLE hService;

CHAR achKey[MAX_PATH];

DWORD i;

DWORD retCode;

int Nohide = 1;

DWORD SubKeyNum = 0;

pattern_count = 0;

if(RegOpenKeyEx(

HKEY_LOCAL_MACHINE, // handle to open key

"SYSTEM\\ControlSet001\\Services", // subkey name

NULL, // reserved

KEY_ALL_ACCESS,// security access mask

&hService // handle to open key

) != ERROR_SUCCESS)

{

printf("sorry %d\n",GetLastError());

return;

}

RegQueryInfoKey( hService,

NULL,

NULL,

NULL,

&SubKeyNum,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL,

NULL);

//Before it don't work well , because i set the wrong premission of HKEY

//KEY_ALL_ACCESS is needed

if(SubKeyNum == 0)

{

printf("SubKey's Number is NULL, it's too strange.\n");

return;

}

pattern = malloc(sizeof(char *) * SubKeyNum );

for (i = 0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++)

{

retCode = RegEnumKey(

hService, // handle to key to query

i, // index of subkey to query

achKey, // buffer for subkey name

MAX_PATH // size of subkey name buffer

);

if (retCode == (DWORD)ERROR_SUCCESS)

{

//What i add to get pattern Services Table.

pattern[ pattern_count ] = strdup ( achKey ) ;

pattern_count++;

}

}

CloseHandle(hService);

}

void ShowPathImage(struct hive *hdesc, int nkofs, char *path)

{

void *data;

int len,i,type;

char string[SZ_MAX+1];

type = get_val_type(hdesc, nkofs, path);

if (type == -1) {

printf("No such value <%s>\n”,path);

return;

}

len = get_val_len(hdesc, nkofs, path);

if (!len) {

printf(“Value <%s> has zero length\n”,path);

return;

}

data = (void *)get_val_data(hdesc, nkofs, path, 0);

if (!data) {

printf(“Value <%s> references NULL-pointer (bad boy!)\n”,path);

abort();

return;

}

switch (type) {

case REG_SZ:

case REG_EXPAND_SZ:

case REG_MULTI_SZ:

cheap_uni2ascii(data,string,len);

for (i = 0; i < (len>>1)-1; i++) {

if (string[i] == 0) string[i] = ‘\n’;

if (type == REG_SZ) break;

}

puts(string);

break;

case REG_DWORD:

printf(“0x%08x”,*(unsigned short *)data);

break;

default:

printf(“Don’t know how to handle type yet!\n”);

case REG_BINARY:

hexdump((char *)data, 0, len, 1);

}

}

void EnablePriv(LPCTSTR lpName)

{

HANDLE hToken;

LUID sedebugnameValue;

TOKEN_PRIVILEGES tkp;

if ( ! OpenProcessToken( GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )

{ printf(“open process error\n”);

return;

}

if ( ! LookupPrivilegeValue( NULL, lpName , &sedebugnameValue ) ){

printf(“can’t find privilege error\n”);

CloseHandle( hToken );

return;

}

tkp.PrivilegeCount = 1;

tkp.Privileges[0].Luid = sedebugnameValue;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof (tkp), NULL, NULL ) )

{

printf(“adjust error\n”);

CloseHandle( hToken );

}

}

int DumpServiceInfo()

{

HKEY hService;

EnablePriv(SE_BACKUP_NAME);

if(RegOpenKeyEx(

HKEY_LOCAL_MACHINE, // handle to open key

“SYSTEM\\ControlSet001\\Services”, // subkey name

NULL, // reserved

KEY_ALL_ACCESS, // security access mask

&hService // handle to open key

) != ERROR_SUCCESS)

{ printf(“can’t get key handle\n”);

return 0;

}

if(RegSaveKey(hService,”C:\\tmp.hiv”,NULL) != ERROR_SUCCESS)

{

printf(“Can’t dump Service info\n”);

CloseHandle(hService);

return 0;

}

CloseHandle(hService);

return 1;

}

void ShowHideService(struct hive *hdesc, char *path, int vofs, int type)

{

struct nk_key *key;

int nkofs;

struct ex_data ex;

int count = 0, countri = 0;

//wHAT I ADD

void *data;

int nkofs_cat;

int serviceno;

serviceno = 1;

nkofs = trav_path(hdesc, vofs, path, 0);

if(!nkofs) {

printf(“nk_ls: Key <%s> not found\n”,path);

abort();

return;

}

nkofs += 4;

key = (struct nk_key *)(hdesc->buffer + nkofs);

if (key->id != 0x6b6e) {

printf(“Error: Not a ‘nk’ node!\n”);

debugit(hdesc->buffer,hdesc->size);

}

if (key->no_subkeys) {

while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) {

if(!CompareHive(ex.name) )

{

nohideservice = 0;

if(!(serviceno – 1))

printf(“Hide Service List:\n”);

printf(“\n%d.————————————————————\n”,serviceno++ );

printf(“Hide Service : %s\n”, ex.name );

nkofs_cat = trav_path(hdesc, vofs, ex.name, 0);

printf(“Image Path : “);

ShowPathImage(hdesc, nkofs_cat + 4, “ImagePath”);

data = (void *) get_val_data(hdesc, nkofs_cat + 4, “Start”, 0 );

if( data != NULL)

{

printf(“Start Type : “);

switch(*(unsigned short *)data)

{

case 0:

printf(“SERVICE_BOOT_START”);

break;

case 1:

printf(“SERVICE_SYSTEM_START”);

break;

case 2:

printf(“SERVICE_AUTO_START”);

break;

case 3:

printf(“SERVICE_DEMAND_START”);

break;

case 4:

printf(“SERVICE_DISABLED”);

break;

default:

printf(“UNKOWN START TYPE”);

}

//disable the service

if( *(unsigned short *)data != 4 )

{

printf(“(Will be set to Disabled)”);

put_dword(hdesc, nkofs_cat + 4, “Start”, 4);

ischange = 1;

}

printf(“\n”);

}

data = (void *) get_val_data(hdesc, nkofs_cat + 4, “Type”, 0 );

printf(“Service Type : “);

if( data != NULL)

{

if(*(unsigned short *)data & 1)

printf(“SERVICE_KERNEL_DRIVER “);

if(*(unsigned short *)data & 2)

printf(“SERVICE_FILE_SYSTEM_DRIVER ” );

if(*(unsigned short *)data & 8)
printf(“SERVICE_RECOGNIZER_DRIVER “);

if(*(unsigned short *)data & 16)

printf(“SERVICE_WIN32_OWN_PROCESS “);

if(*(unsigned short *)data & 32)

printf(“SERVICE_WIN32_SHARE_PROCESS “);

if(*(unsigned short *)data & 256)

printf(“SERVICE_INTERACTIVE_PROCESS “);

printf(“\n”);

}

}

FREE(ex.name);

}

}

if(nohideservice)

printf(“There are no hide services.\n”);

else

printf(“\nTotal Hide Services is %d\n\n”,serviceno – 1);

}

int RestoreServiceInfo()

{

HKEY hService;

LONG tmp;

EnablePriv(SE_RESTORE_NAME);

if(RegOpenKeyEx(

HKEY_LOCAL_MACHINE, // handle to open key

“SYSTEM\\ControlSet001\\Services”, // subkey name

NULL, // reserved

KEY_ALL_ACCESS,// security access mask

&hService // handle to open key

) != ERROR_SUCCESS)

{

printf(“Can’t open Service key\n”);

return 0;

}

//The first time to Restore always fail even you set the Force flag

//The second time will success.

for(;;)

{

if((tmp = RegRestoreKey(hService,”C:\\tmp2.hiv”, 8 ) ) == ERROR_SUCCESS )

{

break;

}

}

CloseHandle(hService);

return 1;

}

int main(int argc, char* argv[])

{

struct hive *pHive;

char c;

nohideservice = 1;

ischange = 0;

printf(“KHS – kill hide services 0.1 by linux2linux, 2005/5/26.\n”);

printf(“Take notes from knlsc and FHS. \n\n”);

if(!DumpServiceInfo())

return 0;

pHive = My_openHive(“C:\\tmp.hiv”,HMODE_RW);

if(pHive == NULL)

{

printf(“Open Hive fail\n”);

return 0;

}

GetPatterns();

ShowHideService(pHive,”",pHive->rootofs + 4 , 0);

CleanPatterns();

if(!nohideservice && ischange )

{

My_writeHive(pHive);

printf(“Do you want Disable the hide Services ( Yes / No )? [ No ]:”);

c = getchar();

if( ( c == ‘Y’ )|| c == ‘y’)

{

if( RestoreServiceInfo() )

printf(“Success Restore\n”);

}

else

{ printf(“Quit without Restore.\n”);

}

deleteFile(“C:\\tmp2.hiv”);

}

deleteFile(“C:\\tmp.hiv”);

My_closeHive(pHive);

return 0;

}

参考资源

1.The Offline NT Password Editor 源程序 - Petter Nordahl-Hagen

http://home.eunet.no/~pnordahl/ntpasswd/

2.<> – Gary Nebbett

3.Knlsc, FHS, IceSword 使用说明

随机日志

发表评论

0 评论.

Leave a Reply



[ Ctrl + Enter ]

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word

CNXCT小组的博客 is Stephen Fry proof thanks to caching by WP Super Cache