创建时间:2005-05-30 文章属性:原创 文章提交: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 使用说明
-- ※ 链接: http://www.nsfocus.net/index.php?act=sec_doc&do=view&doc_id=961 ※ 出处:http://www.xfocus.net/articles/200505/802.html
|