统计在线人数...

检测并禁用隐藏服务

[ 来源:不详 | 作者:neeao | 时间:2005-6-4 10:43:04 | 浏览:统计中... ]


文章来源:安全焦点
文章提交: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 <unistd.h> 去掉

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

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 0x1000 */

 pofs = 0x1000;

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

 if (hdr->id != 0x66676572) {

 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 + 0x1000;

 

 while (pofs < hdesc->size) {

#ifdef LOAD_DEBUG

 if (verbose) hexdump(hdesc->buffer,pofs,pofs+0x20,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 + 0x20; /* 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.<<Windows NT/2000 Native API Reference>> - Gary Nebbett

3.Knlsc, FHS, IceSword 使用说明
共有0人参与评价,平均得分:0分
评论内容只代表网友观点,与本站立场无关! 查看完整内容
   

当前在线人数
QQ:748838 MSN:allen_xia#msn.com E-mail:allenxia666#126.com QQ群:站长联盟北方区-北京(28200145) 站长联盟南方区-上海(67713522)