隐藏服务的概念是由hxdef和rootkit这些后门工具提出的。这些后门工具通过挂钩系统本地调用来隐藏自己,原本通过调用WindowsAPI调用查看系统服务的企图都是徒劳的。所以这时的系统是不可靠的,不值得信任的。目前针对查找隐藏服务的工具已经有很多,比如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文件的转储也不是必须的,虽然这只要通过修改GaryNebbett的示例代码就可做到。
Hive文件的转储可以通过RegSaveKey函数来进行,rootkitrevealer就是使用这个API的扩充函数RegSaveKeyEx工作的,至少到目前为止还没有挂钩这类函数的后门,但是世上没有永远的安全,在理论上是可行的,可能不得不对该函数的输出文件进行处理,这将在一定程度上影响该函数的执行时间。使用该函数时还必须赋予调用进程以SE_BACKUP_NAME权限。
在实现中将“HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services"的子项都转储为Hive格式文件(使用DumpServiceInfo函数),存放在C:\tmp.hive,在C盘下不可有同名文件,否则会发生Dump错误。现在的问题是如何对Hive格式文件进行处理,在这一点上必须感谢PetterNordahl-Hagen所写的NTRegistryHiveaccesslibrary,它是TheOfflineNTPasswordEditor的一部分。本人的实现很大程度上就是参照他的程序,然而这个库工作在Linux环境,但是它向VC编译器移植的工作量是极少的,只需稍加修改。
1.将 #include 去掉
2.将程序中三处的
#defineD_OFFS(o)((void*)&(key->o)-(void*)hdesc->buffer-vofs)
改为
#defineD_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不支持的函数来使编译通过
voidbzero(void*s,intn)
{
memset(s,0,n);
}
intstrncasecmp(constchar*s1,constchar*s2,size_tn)
{
return_stricmp(s1,s2);
}
为了表示对PetterNordahl-Hagen的尊重,我不再修改他的库文件ntreg.c和ntreg.h(除了以上移植的需要),而是将所有需要重写和添加的函数放到KHS.C文件中,这样可以使原来的库文件保持其独立性。
由于在Petter库中openHive函数使用open和read函数来进行hive文件的读取,在VC条件下的read函数有一个问题,每当文件中存在一个0x1a的二进制数值时,read函数就会在那儿终止,这样就会导致hive文件无法完全导入。所以就使用了WindowsAPI重写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-KillHideServicesv0.1
*Copyright(c)2005linux2linux.
*
*IttakesnotesfromknlscandFHS.
*Thankyou,PetterNordahl-Hagen,foryour"NTRegistryHiveaccesslibrary"
*
*Freelydistributableinsourceorbinaryfornoncommercialpurposes.
*
*THISSOFTWAREISPROVIDEDBYPETTERNORDAHL-HAGEN`ASIS';';AND
*ANYEXPRESSORIMPLIEDWARRANTIES,INCLUDING,BUTNOTLIMITEDTO,THE
*IMPLIEDWARRANTIESOFMERCHANTABILITYANDFITNESSFORAPARTICULARPURPOSE
*AREDISCLAIMED.INNOEVENTSHALLTHEAUTHORORCONTRIBUTORSBELIABLE
*FORANYDIRECT,INDIRECT,INCIDENTAL,SPECIAL,EXEMPLARY,ORCONSEQUENTIAL
*DAMAGES(INCLUDING,BUTNOTLIMITEDTO,PROCUREMENTOFSUBSTITUTEGOODS
*ORSERVICES;LOSSOFUSE,DATA,ORPROFITS;ORBUSINESSINTERRUPTION)
*HOWEVERCAUSEDANDONANYTHEORYOFLIABILITY,WHETHERINCONTRACT,STRICT
*LIABILITY,ORTORT(INCLUDINGNEGLIGENCEOROTHERWISE)ARISINGINANYWAY
*OUTOFTHEUSEOFTHISSOFTWARE,EVENIFADVISEDOFTHEPOSSIBILITYOF
*SUCHDAMAGE.
*
*/
#include"ntreg.h"
#include
char**pattern;
intpattern_count;
intnohideservice;
intischange;
externchar*val_types[REG_MAX+1];
structhive*My_openHive(char*filename,intmode);
voidMy_closeHive(structhive*hdesc);
intCompareHive(char*sample)
{
inti;
for(i=0;i!=NULL)
{
if(strcmp(sample,pattern)==0)
{
free(pattern);
pattern=NULL;
return1;
}
}
}
return0;
}
//Becausereadcan';tworkwellinwindows.
//whileitread0x1afromtheopenedfile,thefunctionreadwillstopthere.
//Idon';tknowthereasonwhy,itworkwellinlinuxenvoriment.
//readthedumpedhivefiletofillhivestruct.
//returnthepoint
structhive*My_openHive(char*filename,intmode)
{
HANDLEhFile;
intszread;
structhive*hdesc;
intvofs;
unsignedlongpofs;
char*c;
structhbin_page*p;
structregf_header*hdr;
intverbose=(mode&HMODE_VERBOSE);
CREATE(hdesc,structhive,1);
hdesc->filename=str_dup(filename);
hdesc->state=0;
hdesc->size=0;
hdesc->buffer=NULL;
hFile=CreateFile(hdesc->filename,
GENERIC_READ,//openforreading
0,//donotshare
NULL,//nosecurity
OPEN_ALWAYS,//existingfileonly
FILE_ATTRIBUTE_NORMAL,//normalfile
NULL);//noattr.template
if(hFile==INVALID_HANDLE_VALUE)
{
printf("Couldnotopenhivefile.");//processerror
return0;
}
/*Readthewholefile*/
hdesc->size=GetFileSize(hFile,NULL);
ALLOC(hdesc->buffer,1,hdesc->size);
ReadFile(hFile,(void*)hdesc->buffer,hdesc->size,&szread,NULL);
CloseHandle(hFile);
if(szreadsize){
printf("Couldnotreadfile,got%dbyteswhileexpecting%d\n",
szread,hdesc->size);
My_closeHive(hdesc);
return(NULL);
}
/*Nowrunthroughfile,tallyingallpages*/
/*NOTE/KLUDGE:Assumefirstpagestartsatoffset0x1000*/
pofs=0x1000;
hdr=(structregf_header*)hdesc->buffer;
if(hdr->id!=0x66676572){
printf("openHive(%s):Filedoesnotseemtobearegistryhive!\n",filename);
return(hdesc);
}
for(c=hdr->name;*c&&(cname+64);c+=2)putchar(*c);
hdesc->rootofs=hdr->ofs_rootkey+0x1000;
while(pofssize){
#ifdefLOAD_DEBUG
if(verbose)hexdump(hdesc->buffer,pofs,pofs+0x20,1);
#endif
p=(structhbin_page*)(hdesc->buffer+pofs);
if(p->id!=0x6E696268){
printf("Pageat0x%lxisnot';hbin';,assumingfilecontainsgarbageatend",pofs);
break;
}
hdesc->pages++;
#ifdefLOAD_DEBUG
if(verbose)printf("\n######Pageat0x%0lxhassize0x%0lx,nextat0x%0lx######\n",pofs,p->len_page,p->ofs_next);
#endif
if(p->ofs_next==0){
#ifdefLOAD_DEBUG
if(verbose)printf("openhivedebug:bailingout..pagesizezero!\n");
#endif
return(hdesc);
}
#if0
if(p->len_page!=p->ofs_next){
#ifdefLOAD_DEBUG
if(verbose)printf("openhivedebug:len&ofsnotsame.HASTA!\n");
#endif
exit(0);
}
#endif
vofs=pofs+0x20;/*Skippageheader*/
#if1
while(vofs-pofsofs_next){
vofs+=parse_block(hdesc,vofs,verbose);
}
#endif
pofs+=p->ofs_next;
}
return(hdesc);
}
voidMy_closeHive(structhive*hdesc)
{
FREE(hdesc->filename);
FREE(hdesc->buffer);
FREE(hdesc);
}
intMy_writeHive(structhive*hdesc)
{
HANDLEhFile;
DWORDdwBytesWritten;
hFile=CreateFile("C:\\tmp2.hiv",
GENERIC_WRITE,//openforwriting
0,//donotshare
NULL,//nosecurity
CREATE_ALWAYS,//openorcreate
FILE_ATTRIBUTE_NORMAL,//normalfile
NULL);
if(hFile==INVALID_HANDLE_VALUE)
{printf("Can';topendumpfile");
return0;
}
WriteFile(hFile,hdesc->buffer,hdesc->size,&dwBytesWritten,NULL);
if(dwBytesWritten!=hdesc->size)
{
printf("WriteHiveerror\n");
}
CloseHandle(hFile);
return0;
}
voidCleanPatterns()
{
inti;
if(pattern!=NULL)
{
for(i=0;i!=NULL)
free(pattern);
}
free(pattern);
}
}
voidGetPatterns()
{
HANDLEhService;
CHARachKey[MAX_PATH];
DWORDi;
DWORDretCode;
intNohide=1;
DWORDSubKeyNum=0;
pattern_count=0;
if(RegOpenKeyEx(
HKEY_LOCAL_MACHINE,//handletoopenkey
"SYSTEM\\ControlSet001\\Services",//subkeyname
NULL,//reserved
KEY_ALL_ACCESS,//securityaccessmask
&hService//handletoopenkey
)!=ERROR_SUCCESS)
{
printf("sorry%d\n",GetLastError());
return;
}
RegQueryInfoKey(hService,
NULL,
NULL,
NULL,
&SubKeyNum,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);
//Beforeitdon';tworkwell,becauseisetthewrongpremissionofHKEY
//KEY_ALL_ACCESSisneeded
if(SubKeyNum==0)
{
printf("SubKey';sNumberisNULL,it';stoostrange.\n");
return;
}
pattern=malloc(sizeof(char*)*SubKeyNum);
for(i=0,retCode=ERROR_SUCCESS;retCode==ERROR_SUCCESS;i++)
{
retCode=RegEnumKey(
hService,//handletokeytoquery
i,//indexofsubkeytoquery
achKey,//bufferforsubkeyname
MAX_PATH//sizeofsubkeynamebuffer
);
if(retCode==(DWORD)ERROR_SUCCESS)
{
//WhatiaddtogetpatternServicesTable.
pattern[pattern_count]=strdup(achKey);
pattern_count++;
}
}
CloseHandle(hService);
}
voidShowPathImage(structhive*hdesc,intnkofs,char*path)
{
void*data;
intlen,i,type;
charstring[SZ_MAX+1];
type=get_val_type(hdesc,nkofs,path);
if(type==-1){
printf("Nosuchvalue<%s>\n",path);
return;
}
len=get_val_len(hdesc,nkofs,path);
if(!len){
printf("Value<%s>haszerolength\n",path);
return;
}
data=(void*)get_val_data(hdesc,nkofs,path,0);
if(!data){
printf("Value<%s>referencesNULL-pointer(badboy!)\n",path);
abort();
return;
}
switch(type){
caseREG_SZ:
caseREG_EXPAND_SZ:
caseREG_MULTI_SZ:
cheap_uni2ascii(data,string,len);
for(i=0;i<(len>>1)-1;i++){
if(string==0)string=';\n';;
if(type==REG_SZ)break;
}
puts(string);
break;
caseREG_DWORD:
printf("0x%08x",*(unsignedshort*)data);
break;
default:
printf("Don';tknowhowtohandletypeyet!\n");
caseREG_BINARY:
hexdump((char*)data,0,len,1);
}
}
voidEnablePriv(LPCTSTRlpName)
{
HANDLEhToken;
LUIDsedebugnameValue;
TOKEN_PRIVILEGEStkp;
if(!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
{printf("openprocesserror\n");
return;
}
if(!LookupPrivilegeValue(NULL,lpName,&sedebugnameValue)){
printf("can';tfindprivilegeerror\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("adjusterror\n");
CloseHandle(hToken);
}
}
intDumpServiceInfo()
{
HKEYhService;
EnablePriv(SE_BACKUP_NAME);
if(RegOpenKeyEx(
HKEY_LOCAL_MACHINE,//handletoopenkey
"SYSTEM\\ControlSet001\\Services",//subkeyname
NULL,//reserved
KEY_ALL_ACCESS,//securityaccessmask
&hService//handletoopenkey
)!=ERROR_SUCCESS)
{printf("can';tgetkeyhandle\n");
return0;
}
if(RegSaveKey(hService,"C:\\tmp.hiv",NULL)!=ERROR_SUCCESS)
{
printf("Can';tdumpServiceinfo\n");
CloseHandle(hService);
return0;
}
CloseHandle(hService);
return1;
}
voidShowHideService(structhive*hdesc,char*path,intvofs,inttype)
{
structnk_key*key;
intnkofs;
structex_dataex;
intcount=0,countri=0;
//wHATIADD
void*data;
intnkofs_cat;
intserviceno;
serviceno=1;
nkofs=trav_path(hdesc,vofs,path,0);
if(!nkofs){
printf("nk_ls:Key<%s>notfound\n",path);
abort();
return;
}
nkofs+=4;
key=(structnk_key*)(hdesc->buffer+nkofs);
if(key->id!=0x6b6e){
printf("Error:Nota';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("HideServiceList:\n");
printf("\n%d.------------------------------------------------------------\n",serviceno++);
printf("HideService:%s\n",ex.name);
nkofs_cat=trav_path(hdesc,vofs,ex.name,0);
printf("ImagePath:");
ShowPathImage(hdesc,nkofs_cat+4,"ImagePath");
data=(void*)get_val_data(hdesc,nkofs_cat+4,"Start",0);
if(data!=NULL)
{
printf("StartType:");
switch(*(unsignedshort*)data)
{
case0:
printf("SERVICE_BOOT_START");
break;
case1:
printf("SERVICE_SYSTEM_START");
break;
case2:
printf("SERVICE_AUTO_START");
break;
case3:
printf("SERVICE_DEMAND_START");
break;
case4:
printf("SERVICE_DISABLED");
break;
default:
printf("UNKOWNSTARTTYPE");
}
//disabletheservice
if(*(unsignedshort*)data!=4)
{
printf("(WillbesettoDisabled)");
put_dword(hdesc,nkofs_cat+4,"Start",4);
ischange=1;
}
printf("\n");
}
data=(void*)get_val_data(hdesc,nkofs_cat+4,"Type",0);
printf("ServiceType:");
if(data!=NULL)
{
if(*(unsignedshort*)data&1)
printf("SERVICE_KERNEL_DRIVER");
if(*(unsignedshort*)data&2)
printf("SERVICE_FILE_SYSTEM_DRIVER");
if(*(unsignedshort*)data&8)
printf("SERVICE_RECOGNIZER_DRIVER");
if(*(unsignedshort*)data&16)
printf("SERVICE_WIN32_OWN_PROCESS");
if(*(unsignedshort*)data&32)
printf("SERVICE_WIN32_SHARE_PROCESS");
if(*(unsignedshort*)data&256)
printf("SERVICE_INTERACTIVE_PROCESS");
printf("\n");
}
}
FREE(ex.name);
}
}
if(nohideservice)
printf("Therearenohideservices.\n");
else
printf("\nTotalHideServicesis%d\n\n",serviceno-1);
}
intRestoreServiceInfo()
{
HKEYhService;
LONGtmp;
EnablePriv(SE_RESTORE_NAME);
if(RegOpenKeyEx(
HKEY_LOCAL_MACHINE,//handletoopenkey
"SYSTEM\\ControlSet001\\Services",//subkeyname
NULL,//reserved
KEY_ALL_ACCESS,//securityaccessmask
&hService//handletoopenkey
)!=ERROR_SUCCESS)
{
printf("Can';topenServicekey\n");
return0;
}
//ThefirsttimetoRestorealwaysfailevenyousettheForceflag
//Thesecondtimewillsuccess.
for(;;)
{
if((tmp=RegRestoreKey(hService,"C:\\tmp2.hiv",8))==ERROR_SUCCESS)
{
break;
}
}
CloseHandle(hService);
return1;
}
intmain(intargc,char*argv[])
{
structhive*pHive;
charc;
nohideservice=1;
ischange=0;
printf("KHS-killhideservices0.1bylinux2linux,2005/5/26.\n");
printf("TakenotesfromknlscandFHS.\n\n");
if(!DumpServiceInfo())
return0;
pHive=My_openHive("C:\\tmp.hiv",HMODE_RW);
if(pHive==NULL)
{
printf("OpenHivefail\n");
return0;
}
GetPatterns();
ShowHideService(pHive,"",pHive->rootofs+4,0);
CleanPatterns();
if(!nohideservice&&ischange)
{
My_writeHive(pHive);
printf("DoyouwantDisablethehideServices(Yes/No)?[No]:");
c=getchar();
if((c==';Y';)||c==';y';)
{
if(RestoreServiceInfo())
printf("SuccessRestore\n");
}
else
{printf("QuitwithoutRestore.\n");
}
DeleteFile("C:\\tmp2.hiv");
}
DeleteFile("C:\\tmp.hiv");
My_closeHive(pHive);
return0;
}
参考资源
1.TheOfflineNTPasswordEditor 源程序 -PetterNordahl-Hagen
http://home.eunet.no/~pnordahl/ntpasswd/
2.<>-GaryNebbett
3.Knlsc,FHS,IceSword使用说明 |