Jump to content

Nytro

Administrators
  • Posts

    18725
  • Joined

  • Last visited

  • Days Won

    706

Everything posted by Nytro

  1. Am uitat sa specific cate ceva legat de teorema: "Hackerii sunt tineri". Pe scurt e falsa. Da, daca iei "hacker" in sensul mass-media, atunci da, hackerii sunt persoane tinere. Dar tind mult spre script-kiddism. Foarte mult. Doar nu te astepti ca o persoana de 14 ani sa gaseasca o modalitate de a incarca un driver printr-un cod in assembler non-API (exemplu aiurea) sau mai stiu eu ce. Da, se intampla si ca persoane foarte tinere sa aiba idei foarte bune, idei geniale si sa decopere lucruri noi, dar asta se intampla extrem de rar. Hacker in sensul in care vad eu acest atribut, e o persoana trecuta prin viata, care a petrecut mai mult de 2 ore incercand sa invete CUM se intampla anumite lucruri, sa le inteleaga, apoi sa poata reusi sa faca bypass la un mecanism de securitate de exemplu. Pe langa timpul petrecut invatand si explorand, o persoana mai "in varsta" are ceva extrem de important: o etica foarte dezvoltata. Da, spargi un site la 15 ani sa te lauzi prietenilor, dar la 30 de ani deja ti se pare o mare prostie, o copilarie, si chiar daca ti-ar fi extrem de usor sa faci asta, nu o sa o faci pentru ca nu ti se pare ceva destul de complicat pentru tine (experienta vine in timp). Si vei incerca sa faci lucruri mult mai complicate, insa acele lucruri nu vor aparea in mass-media, pentru ca acestia se adreseaza persoanelor de rand, persoane care nu stiu de exemplu care e arhitectura unui sistem de operare si care nu o sa inteleaga cum sta treaba. Ei vor ramane mereu cu chestiile de suprafata, usor de inteles pentru toata lumea, iar cei mai in varsta "se vor pierde" dar nu prea. Adica nu va sti toata lumea de tine daca vei face lucruri complicate, sa zicem daca ai scrie device drivere, insa va sti cine trebuie: o firma care se ocupa cu asa ceva, si vei castiga destul de bine din asta.
  2. Ca sa evit generalizari, voi spune doar asta: hacker e un fel de mic geniu al informaticii, sa zicem un mic "inventator", cineva care a facut ceva pentru acest domeniu. Ca sa dau niste exemple: Pitagora/Fermat/LaGrange si altii (nu am idei acum) sunt "hackeri ai matematicii", asta e modul in care vad eu lucrurile. Sunt persoane care au adus contributii importante in matematica, fie ele lucruri noi sau nu, sunt oricum lucruri care necesita cunostinte solide de matematica. Aplic acelasi principiu si in informatica. Hackeri ar fi: - muts - Pentru Backtrack - Fyodor - Pentru Nmap - HD Moore - Pentru Metasploit Cam asta ar fi ideea. Hackeri sunt persoanele care pe langa faptul ca au descoperit lucruri interesante: BOF-urile, SQL Injection-ul, LFI to RCE de exemplu, ASLR/DEP bypass si altele, ei sunt hackeri, nu cei care citesc 2 tutoriale si obtin acces undeva cu ele, si pe langa faptul ca au descoperit lucruri noi, au si avut grija sa le faca publice si sa ofere informatia obtinuta de ei publicitatii. Dar lumea nu se gandeste la asta, si il ia pe Vasile care citeste 2 articole despre SQL Injection, "sparge" un site si gata, el e hackerul... De asemenea conteaza si nevelul de cunostinte necesare pentru o anumita actiune. De ce nu i se spune "hacker" lui Tavis Ormandy , Ramon Valle sau Jon Oberheide care au gasit Local Root Privilege Escalation Exploit-uri in kernelul de Linux? E simplu, lumea nu intelege chestiile complicate si de suprafata, nu poate vedea practic ce au facut ei. In schimb vad o imagine pe o un site "care a fost spart" si il considera un zeu pe autor. E ca si cum ai merge la un concurs cu un modul de kernel care face cine stie ce lucruri complicate si vine unul cu un site in Flash cu beculete roz si stralucitoare. El va castiga clar concursul. Cred ca intelegeti ce vreau sa spun.
  3. Cred ca e spam, deci e posibil sa fie vorba de programe infectate.
  4. In fine, ca sa inteleaga si persoane mai inapte care inca considera ca sunt gabor: nu sunt, in cel mai rau caz as fi la Academia de Politie unde am dat doar probele fizice sau la Academia Tehnica Militara unde am luat probele fizice, dar matematica si fizica de acolo nu sunt de mine. Deci sunt un simplu student.
  5. Da, poza de la CV-ul de pe bestjobs... Cand am fost la un interviu, cel cu care vorbeam avea CV-ul meu in fata si a taiat poza asta, a zis ca nu e poza de CV.
  6. Era cand am facut poze pentru albumul clasei. Uite, sa nu mai comenteze lumea: // Removed Am scos burta si suncile in Photoshop, ochelarii nu ii port si cosurile de pe fata nu mi se vad.
  7. Erau vreo 2 poze mai vechi cu mine, dar nu sunt ala de mai sus @ adi123456789 Am pus aici, anul trecut o poza in costum. Poate nu a fost stearsa. Sapati.
  8. Nu e Pax, e unul care inca crede ca sunt gabor Daca veti cauta prin toate posturile mele, pe langa faptul ca imi veti afla numele, veti afla si cum sta treaba cu "locul meu de munca". Iar tu, stimate autor al acestui post mirific, esti un gunoi pentru acest forum, ban!
  9. Mie nu imi place cu tutorialele asa din urmatorul motiv: tutorialele sunt principala sursa de informatie aici, dupa mine, Tutoriale Romana/Engleza/Video si Programare sunt cele mai importante cateogrii, si trebuie sa apara toate pe index. Nu sa stau sa dau la Tutoriale, apoi la ce categorie s-a postat...
  10. Pune link catre profilul utilizatorului Nytro creat.
  11. This will be picked up as a virus(rootkit), most rootkits use ssdt hooking to hide files\processes. I had to configure my AV. Source Files: http://www.ucdownloads.com/downloads...o=file&id=4211 If there is anything you'd like me to explain more, please post about it. Writing drivers to perform kernel-level SSDT hooking The goal of the reader is to perform the the below points while following the tutorial. The tutorial describes how to do this step by step. Write a console application to load, and send commands to the driver Have the driver process commands Have the driver perform according SSDT hooks, or execute the received command. There are several chapters to this tutorial. The are as described below. Installing the Driver Development Kit, and setting up your working folder. Creating a basic console application to load and start driver services, as well as perform IO to IO Devices. Building a basic driver Setting up driver major functions and basic IO Creating a message protocol. Getting access to protected memory using MDLs. Writing the hook. Before we begin the tutorial please note the following: If you get any linking errors regarding unresolved external symbols, assure you have all your source files defined in SOURCES(you will learn about SOURCES later in this tutorial) For debugging the driver, run the drivers in a VM, setting up DbgView client on VM, and then connecting to it on the host machine. This will give you the last debug messages recived before the driver crashed the host machine. And you can easily narrow down errors this way. This is not worth your time if you plan to write a simple hook with it. This driver is written in C, there are some big differences between C and C++, they will be noted as you follow the tutorial. If you know C++, you should have no problems following this tutorial. Any errors, overflows, exceptions, etc will cause a BSOD, and could possibly damage your OS. And thus, when these drivers are being tested it is highly recommended you do testing on a virtual machine. All drivers written with this tutorial should NOT be installed as services that startup with the operating system. If there are errors in the driver, it can prevent you from starting your computer, to fix this you can boot in to safe mode and manually remove the service. The drivers you write using this tutorial will most likely be picked up as rootkits, disable your anti-virus during the whole tutorial, or configure it accordingly. This is because hooking the service dispatch table is a common technique used by rootkits to hide processes or files. I don't know what UC's status is on including drivers in your hacks, so be sure to consult them about it before using this technique in hacking. Lastly, for whatever you're trying to achive with a driver, will probably be achiveable by user-level hooking, so be sure you take advantage of user-level hooks before kernel-level hooks. Installing the Driver Development Kit, and setting up your working folder Now lets start to setup our DDK, and our development environment. We're actually going to be installing WDK, which contains the DDK. The DDK contains many useful headers and libraries, as well as the binaries we're going to be using to build our driver. I will start off by saying, Microsoft has successfully made this thee most difficult development kit to acquire(thanks Microsoft). How to Get the WDK 1. Register a hotmail account if you don't already acquire one. 2. Login at Microsoft Products Accepting Bugs and Suggestions | Microsoft Connect 3. After logging in, there will be tabs on the top of the page, select Connection Directory. 4. To the left of the page, there is a navigation menu, select Developer Tools. 5. Search for : Windows Driver Kit (WDK) and Windows Driver Framework (WDF) and apply. 6. Now select 'Your Dashboard' on the top navigation menu. 7. Select Windows Driver Kit (WDK) and Windows Driver Framework (WDF). 8. Finally, press the download link under the text "The download package for this release is:" 8. After that download, either mount the downloaded ISO file, or extract it. Then install it. After the installation, if you go to start->All Programs->Windows Driver Kits->WDK (version number)->Build Environments->Your OS-> You will see two executables. One is a free build environment, and the other is checked. If you build your driver in the checked environment, your driver will not be optimized, and if it is done in a free environment, it will be optimized. Optimization can sometimes cause errors. And thus drivers should be built in checked, then when they're completed, tested, built, and distributed in free. Throughout this tutorial we'll be using a checked environment. And there you go, DDK should be located in InstalledDrive:\WinDDK\VersionNumber\ You can setup the DDK to MSVS, but it's not recommended, the staff over at MSDN are saying it wasn't built to compile drivers. There really isn't a need to use VS anyway, the console and notepad will do just fine. I tried setting it up with MSVS and I got a ton of errors. Not worth it for me. Create a folder in either of your harddisks. Make it easy to access and without spaces. Ex: C:\hack\, not C:\my hack\. Also make it short, because you'll be navigating to it quite oftenly. In that folder you'll need to create two files: SOURCES MAKEFILE No extensions. SOURCES should contain the following: Code: TARGETNAME=MYDRIVER TARGETPATH=OUTPUT TARGETTYPE=DRIVER TARGETLIBS= $(BASEDIR)\lib\w2k\i386\ndis.lib SOURCES= TARGETNAME: Name of file to output file, excluding .sys extention. TARGETPATH: The path, from the current directory, where the compiled file will be stored. TARGETTYPE: The type of file source code is being compiled into. TARGETLIBS: A list to all the libraries to include in the project. Where $(BASEDIR) is the base directory of wherever you installed the DDK. Usually this is InstalledDriver:\WinDDK\VersionNumber\ SOURCES: defines all the c files to compile and link in the project. Be sure to update this when you create a new source file. This is currently blank, and will be filled in later on in this tutorial. And MAKEFILE should contain: Code: !INCLUDE $(NTMAKEENV)\makefile.def Which essentially redirects to the makefile provided in the ddk. All drivers will share the same makefile. Last but not least, download DebugView, which will allow you to view debug output messages from your driver. When you're debugging, you'll want to be viewing the output remotely. You can do this by starting the DebugView client(giving DebugView /c command argument when starting it) on the VM you'll be testing the driver is, then starting DebugView without any arguments on the host machine, and connecting to the local client by going to computer->connect-> and inserting the target machine's IP Address. After you've got that, you're ready to continue to the next step. Creating a basic console application to load and start driver services, as well as perform IO to IO Devices This is probably the easiest part of the tutorial and won't be explained too much. For this part of the tutorial we'll be using C++. To keep your code neat and organized, start by creating a source file, called serviceTools.h. We want to have a function load a driver using two parameters, the service name, and the path of it's corresponding executable. Notice this code may be modifed by it's parent tags, and thus refer to download for valid source file. PHP Code: #pragma once #include <stdio.h> #include <windows.h> int createService(char* serviceName, char* executablePath, bool relative); int startService(char* serviceName); int stopService(char* serviceName); int deleteService(char* serviceName); And the corresponding source file: Notice this code may be modifed by it's parent tags, and thus refer to download for valid source file. PHP Code: #include "stdafx.h" #include "serviceTools.h" int createService(char* serviceName, char* executablePath, bool relative) { char pathBuffer[200]; if(relative) { GetCurrentDirectory(200, pathBuffer); strcat(pathBuffer, "\\"); } strcat(pathBuffer, executablePath); printf("Creating service %s ", serviceName); SC_HANDLE sh = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if(sh == INVALID_HANDLE_VALUE) { printf("Error opening handle to Service Manager.\n"); return -1; } SC_HANDLE hService = CreateService(sh, serviceName, serviceName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, pathBuffer, 0, 0, 0, 0, 0); CloseServiceHandle(sh); if(hService == 0) { printf("Error creating service.\n"); return -1; } printf("Service has been created under the display name of %s\n", serviceName); CloseServiceHandle(hService); return 1; } int startService(char* serviceName) { SC_HANDLE hService; SC_HANDLE sh = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if(!sh) { printf("Error opening service handler handle\n"); return -1; } hService = OpenService(sh, serviceName, SERVICE_ALL_ACCESS); CloseServiceHandle(sh); if(!hService) { printf("Error opening service handle\n"); return -1; } if(StartService(hService, 0, 0) != 0) printf("Service started.\n"); else { unsigned long eCode = GetLastError(); printf("Error starting service.(%d)\n", eCode); CloseServiceHandle(hService); return -1; } CloseServiceHandle(hService); return 1; } int stopService(char* serviceName) { SC_HANDLE hService; SC_HANDLE sh = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if(!sh) { printf("Error opening service handler handle\n"); return -1; } hService = OpenService(sh, serviceName, SERVICE_ALL_ACCESS); CloseServiceHandle(sh); if(!hService) { printf("Error opening service handle\n"); return -1; } SERVICE_STATUS srvStatus; if(ControlService(hService, SERVICE_CONTROL_STOP, &srvStatus) != 0) printf("Service stopped.\n"); else { printf("Error stopping service.\n"); CloseServiceHandle(hService); return -1; } CloseServiceHandle(hService); return 1; } int deleteService(char* serviceName) { SC_HANDLE hService; SC_HANDLE sh = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if(!sh) { printf("Error opening service handler handle\n"); return -1; } hService = OpenService(sh, serviceName, SERVICE_ALL_ACCESS); CloseServiceHandle(sh); if(!hService) { printf("Error opening service handle\n"); return -1; } if(DeleteService(hService) != 0) printf("Service deleted.\n"); else { printf("Error deleting service.\n"); CloseServiceHandle(hService); return -1; } CloseServiceHandle(hService); } The above code is pretty simple, but I will go over createService, after that, the rest are pretty much the same. First we notify the user a driver is being loaded printf("Creating service %s ", serviceName); Then we can go ahead and open a handle to the service control manager. SC_HANDLE sh = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); The first two parameters are optional, the first being the name of the target machine, in our case we want to open the SC Manager of the local machine, and thus this can be set to null. The second parameter is the name of the SC Manager database. MSDN states this should be set to SERVICES_ACTIVE_DATABASE definition, if it is null it is set to this by default. Now we can create our service. Code: SC_HANDLE hService = CreateService(sh, serviceName, serviceName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, pathBuffer, 0, 0, 0, 0, 0); I don't plan to go through all of these parameters, they're all defined at MSDN library. Notice though, that the dwServiceType parameter is set to SERVICE_KERNEL_DRIVER definition. This API will create our service, and then return a handle to the newly created service. Then we dispose of the handles using CloseServiceHandle, CloseHandle cannot be used when it comes to service related handles, instead CloseServiceHandle is used. Now that that's over with, lets start writing the IO portion of the code. The objective of this code is to open handles to an IO device, and write to it. Create a new header file, call it deviceIo.h. We want a method to open a handle to an IO device, another to write to it, we wont need to read from the IO device in this tutorial. PHP Code: #pragma once #include <windows.h> HANDLE GetHandleToIo(char* deviceName); int CloseIoHandle(HANDLE hIo); int WriteToDevice(HANDLE hDevice, char* inBuffer); And the corresponding source file PHP Code: #include "stdafx.h" #include "deviceIo.h" HANDLE GetHandleToIo(char* deviceName) { return (HANDLE)CreateFile(deviceName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); } int CloseIoHandle(HANDLE hIo) { if(hIo == INVALID_HANDLE_VALUE) return -1; else CloseHandle(hIo); return 1; } int WriteToDevice(HANDLE hDevice, char* inBuffer) { WriteFile(hDevice, inBuffer, strlen(inBuffer) + 1, 0, 0); return 1; } Pretty simple. No need to go into any details. If you need help with this portion of code, just say so and I will document it a bit more. Lastly, we need to process all the commands thrown into our console. To do so, we're going to create yet another file called commandProcessor.h. PHP Code: #pragma once #define CMD_UNKNOWN_COMMAND 0 #define CMD_SETUP_SERVICE 1 #define CMD_DELETE_SERVICE 2 #define CMD_START_SERVICE 3 #define CMD_STOP_SERVICE 4 #define CMD_WRITE 5 #define CMD_OPEN 6 #define CMD_CLOSE 7 #include "serviceTools.h" #include "deviceIo.h" unsigned int translateCommand(char* command); int processCommand(char* command); And the corresponding source file: PHP Code: #include "stdafx.h" #include "commandProcessor.h" const unsigned long CMD_ID_TABLE[] = {CMD_SETUP_SERVICE, CMD_DELETE_SERVICE, CMD_START_SERVICE, CMD_STOP_SERVICE, CMD_WRITE, CMD_OPEN, CMD_CLOSE, CMD_QUIT}; const char* CMD_NAME_TABLE[] = {"ss" , "ds" , "start", "stop" , "write", "open", "close", "quit"}; unsigned int translateCommand(char* command) { int tblCmdSize = sizeof(CMD_ID_TABLE) / sizeof(unsigned long); int tblCmdName = sizeof(CMD_NAME_TABLE) / sizeof(char*); for(unsigned int i = 0; i < tblCmdSize && i < tblCmdName; i++) if(!strcmp(command, CMD_NAME_TABLE[i])) return CMD_ID_TABLE[i]; return CMD_UNKNOWN_COMMAND; } int processCommand(char* command) { static HANDLE hFileHandle = INVALID_HANDLE_VALUE; char buffer[200]; ZeroMemory(&buffer, 200); switch(translateCommand(command)) { case CMD_SETUP_SERVICE: scanf("%s", buffer); createService("MYDRIVER", buffer, true); break; case CMD_DELETE_SERVICE: scanf("%s", buffer); deleteService(buffer); break; case CMD_START_SERVICE: scanf("%s", buffer); startService(buffer); break; case CMD_STOP_SERVICE: scanf("%s", buffer); stopService(buffer); break; case CMD_OPEN: scanf("%s", buffer); hFileHandle = GetHandleToIo(buffer); break; case CMD_CLOSE: CloseIoHandle(hFileHandle); break; case CMD_WRITE: scanf("%[^\n\t]s", buffer); WriteToDevice(hFileHandle, buffer); break; case CMD_QUIT: return 0; break; default: printf("Unknown command inserted..\n"); } return 1; } Lets tie this all together into main.cpp PHP Code: // tutDriverConsole.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "commandProcessor.h" int main(int argc, char* argv[]) { char buffer[200]; int ret; do { printf("\n >>"); scanf("%200s", buffer); ret = processCommand(buffer); }while(ret); return 0; } Commands for console: Quote: ss- Setup Service ds- Delete service start - start service stop - stop service open - open handle to IO device close - close current IO handle write - write to opened IO handle quit - quit Be sure to close your handle to the IO device, before attempting to stop and delete the service. And that's about it for the console application. Now lets dive right into creating our first driver. Este doar o parte din tutorial. Articolul complet: [Tutorial] Writing drivers to perform kernel-level SSDT hooking. PDF: MEGAUPLOAD - The leading online storage and file delivery service Download Writing drivers to perform kernel-level SSDT hooking.pdf for free on uploading.com 2shared - download Writing drivers to perform kernel-level SSDT hooking.pdf
  12. Invisibility on NT boxes, How to become unseen on Windows NT (Version: 1.2) Holy_Father 29A magazine #7 August 2003 [Back to index] [Comments (0)] Contents 1. Contents 2. Introduction 3. Files 3.1 NtQueryDirectoryFile 3.2 NtVdmControl [*]4. Processes [*]5. Registry 5.1 NtEnumerateKey 5.2 NtEnumerateValueKey [*]6. System services and drivers [*]7. Hooking and spreading 7.1 Rights 7.2 Global hook 7.3 New processes 7.4 DLL [*]8. Memory [*]9. Handle 9.1 Naming handle and getting type [*]10. Ports 10.1 Netstat, OpPorts on WinXP, FPort on WinXP 10.2 OpPorts on Win2k and NT4, FPort on Win2k [*]11. Ending 2. Introduction This document is about technics of hiding objects, files, services, processes etc. on OS Windows NT. These methods are based on hooking Windows API functions which are described in my document "Hooking Windows API". Everything here was get from my own research during writing rootkit code, so there is a chance it can be written more effectively or it can be written much more easily. This also involve my implementation. Hiding arbitrary object in this document mean to change some system functions which name this object in the way they would skip its naming. In the case this object is only return value of that function we would return value as the object does not exist. Basic method (excluding cases of telling different) is that we would call original function with original arguments and then we would change its output. In this version of this text are described methods of hiding files, processes, keys and values in registry, system services and drivers, allocated memory and handles. 3. Files There are serveral possibilities of hiding files in the way OS would not see it. We would aim only changing API and leave out technics like those which play on features of filesystem. It also is much easier because we dont need to know how particular filesystem works. 3.1 NtQueryDirectoryFile Looking for a file on wNT in some directory is based on searching in all its files and files in all its subdirectories. For file enumeration is used function NtQueryDirectoryFile. NTSTATUS NtQueryDirectoryFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG FileInformationLength, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan ); Important parameters for us are FileHandle, FileInformation and FileInformationClass. FileHandle is a handle of directory object which can be get from NtOpenFile. FileInformation is a pointer on allocated memory, where this function write wanted data to. FileInformationClass determines type of record written in FileInformation. FileInformationClass is varied enumerative type, but we need only four values which are used for enumerating directory content: #define FileDirectoryInformation 1 #define FileFullDirectoryInformation 2 #define FileBothDirectoryInformation 3 #define FileNamesInformation 12 structure of recoed written in FileInformation for FileDirectoryInformation: typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG Unknown; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; WCHAR FileName[1]; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; for FileFullDirectoryInformation: typedef struct _FILE_FULL_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG Unknown; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaInformationLength; WCHAR FileName[1]; } FILE_FULL_DIRECTORY_INFORMATION, *PFILE_FULL_DIRECTORY_INFORMATION; for FileBothDirectoryInformation: typedef struct _FILE_BOTH_DIRECTORY_INFORMATION { ULONG NextEntryOffset; ULONG Unknown; LARGE_INTEGER CreationTime; LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastWriteTime; LARGE_INTEGER ChangeTime; LARGE_INTEGER EndOfFile; LARGE_INTEGER AllocationSize; ULONG FileAttributes; ULONG FileNameLength; ULONG EaInformationLength; UCHAR AlternateNameLength; WCHAR AlternateName[12]; WCHAR FileName[1]; } FILE_BOTH_DIRECTORY_INFORMATION, *PFILE_BOTH_DIRECTORY_INFORMATION; and for FileNamesInformation: typedef struct _FILE_NAMES_INFORMATION { ULONG NextEntryOffset; ULONG Unknown; ULONG FileNameLength; WCHAR FileName[1]; } FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; This function writes a list of these structures in FileInformation. Only three vairiables are important for us in any of these structure types. NextEntryOffset is the length of particular list item. First item can be found on address FileInformation + 0. So the second item is on address FileInformation + NextEntryOffset of first one. Last item has NextEntryOffset set on zero. FileName is a full name of the file. FileNameLength is a length of file name. If we want to hide a file, we need to tell apart these four types and for each returned record we need to compare its name with the one which we want to hide. If we want to hide first record, we have to move following structures by the size of the first. This will cause the first record would be rewritten. If we want to hide another record, we can easily change the value of NextEntryOffset of previous record. New value of NextEntryOffset would be zero if we want to hide the last record, otherwise the value would be the sum of NextEntryOffset of the record we want to hide and of previous record. Then we should change the value of Unknown of previous record which is prolly an index for next search. The value of Unknown of previous record should have a value of Unknown of the record we want hide. If no record which should be seen was found, we will return error STATUS_NO_SUCH_FILE. #define STATUS_NO_SUCH_FILE 0xC000000F 3.2 NtVdmControl From unknown reason DOS emulation NTVDM can get a list of files also with function NtVdmContol. NTSTATUS NtVdmControl( IN ULONG ControlCode, IN PVOID ControlData ); ControlCode specifies the subfunction which is applied on data in ControlData buffer. If ControlCode equals to VdmDirectoryFile this function does the same as NtQueryDirectoryFile with FileInformationClass set on FileBothDirectoryInformation. #define VdmDirectoryFile 6 Then ControlData is used like FileInformation. The only difference here is that we do not know the length of this buffer. So we have to count it manually. We have to add NextEntryOffset of all records and FileNameLength of the last record and 0x5E as a length of the last record excluding the name of the file. Hiding methods are the same as in NtQueryDirectoryFile then. 4. Processes Various system info is available using NtQuerySystemInformation. NTSTATUS NtQuerySystemInformation( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); SystemInformationClass specifies the type of information which we want to get, SystemInformation is a pointer to the function output buffer, SystemInformationLength is the length of this buffer and ReturnLength is number of written bytes. For the enumeration of running processes we use SystemInformationClass set on SystemProcessesAndThreadsInformation. #define SystemInformationClass 5 Returned structure in SystemInformation buffer is: typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromProcessId; ULONG HandleCount; ULONG Reserved2[2]; VM_COUNTERS VmCounters; IO_COUNTERS IoCounters; // Windows 2000 only SYSTEM_THREADS Threads[1]; } SYSTEM_PROCESSES, *PSYSTEM_PROCESSES; Hiding processes is similiar as in the case of hiding files. We have to change NextEntryDelta of previous record of that we want to hide. Usually we will not want to hide the first record here because it is Idle process. 5. Registry Windows registry is quite big tree structure containing two important types of records for us which we could want to hide. First type is registry keys, second is values. Owing to registry structure hiding registry keys is not as trivial as hiding file or process. 5.1 NtEnumerateKey Owing to its structure we are not able to ask for a list of all keys in the specific part of registry. We can get only information about one key specified by its index in some part of registry. This provides NtEnumerateKey. NTSTATUS NtEnumerateKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_INFORMATION_CLASS KeyInformationClass, OUT PVOID KeyInformation, IN ULONG KeyInformationLength, OUT PULONG ResultLength ); KeyHandle is a handle to a key in which we want to get information about a subkey specified by Index. Type of returned information is specified by KeyInformationClass. Data are written to KeyInformation buffer which length is KeyInformationLength. Number of written bytes is returned in ResultLength. The most important think we need to perceive is that if we hide a key, indexes of all following keys woould be shifted. And because we are able to get information about a key with higher index with asking for key with lower index we always have to count how many records before were hidden and then return the right one. Let's have a look on the example. Assume we have some keys called A, B, C, D, E and F in any part of registry. Indexing starts from zero which mean index 4 match E key. Now if we want to hide B key and the hooked application call NtEnumerateKey with Index 4 we should return information about F key because there is an index shift. The problem is that we don't know that there is a shift. And if we didn't care about shifting and return E instead of F when asking for key with index 4 we would return nothing when asking for key with index 1 or we would return C. Both cases are errors. This is why we have to care about shifting. Now if we counted the shift by recalling the function for each index from 0 to Index we would sometimes wait for ages (on 1GHz processor it could take up to 10 seconds with standard registry which is too much). So we have to think out more sophisticated method. We know that keys are (except of references) sorted alphabetically. If we neglect references (which we don't want to hide) we can count the shift by following method. We will sort alphabetically our list of key names which we want to hide (RtlCompareUnicodeString can be used), then when application calls NtEnumerateKey we will not recall it with unchanged arguments but we will find out the name of the record specified by Index. NTSTATUS RtlCompareUnicodeString( IN PUNICODE_STRING String1, IN PUNICODE_STRING String2, IN BOOLEAN CaseInSensitive ); String1 and String2 are strings which will be compared, CaseInSensitive is True if we want to compare with neglecting character case. Function result describes relation between String1 and String2: result > 0: String1 > String2 result = 0: String1 = String2 result < 0: String1 < String2 Now we have to find a border. We will compare alphabetically the name of the key specified by Index with the names in our list. The border would be the last lesser name from our list. We know that the shift is at most the number of the border in our list. But not all items from our list have to be a valid key in the part of registry we are in. So we have to ask for all items from our list up to border if they are in this part of the registry. This can be done using NtOpenKey. NTSTATUS NtOpenKey( OUT PHANDLE KeyHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes ); KeyHandle is a handle of superordinate key. We will use the value from NtEnumerateKey for it. DesiredAccess are access rights. KEY_ENUMERATE_SUB_KEYS is the right value for it. ObjectAttributes describes subkey which we want to open (including its name). #define KEY_ENUMERATE_SUB_KEYS 8 If the result of NtOpenKey is 0 opening was successful which mean this key from our list exists. Opened key should be closed via NtClose. NTSTATUS NtClose( IN HANDLE Handle ); For each call of NtEnumareteKey we will count the shift as a number of keys from our list which exist in the given part of registry. Then we will add this shift to Index argument and finally call the original NtEnumerateKey. For getting name of the key specified by Index we will use the value KeyBasicInformation as a KeyInformationClass. #define KeyBasicInformation 0 NtEnumerateKey returns this structure in KeyInformation: typedef struct _KEY_BASIC_INFORMATION { LARGE_INTEGER LastWriteTime; ULONG TitleIndex; ULONG NameLength; WCHAR Name[1]; } KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; Only thing we need here is Name and its length NameLength. If there is no entry for shifted Index we will return error STATUS_EA_LIST_INCONSISTENT. #define STATUS_EA_LIST_INCONSISTENT 0x80000014 5.2 NtEnumerateValueKey Registry values are not alphabetically sorted. Luckily the number of values in one key is quite small, so we can use recall method to get the shift. API for getting info about one value is called NtEnumerateValueKey. NTSTATUS NtEnumerateValueKey( IN HANDLE KeyHandle, IN ULONG Index, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, OUT PVOID KeyValueInformation, IN ULONG KeyValueInformationLength, OUT PULONG ResultLength ); KeyHandle is again a handle of superordinate key. Index is an index to the list of values in given key. KeyValueInformationClass describes a type of information which will be stored into KeyValueInformation buffer which is long KeyValueInformationLength bytes. Number of written bytes is returned in ResultLength. Again we have to count the shift but according to the number of values in one key we can recall this function for all indexes from 0 to Index. The name of the value can be get when KeyValueInformationClass is set to KeyValueBasicInformation. #define KeyValueBasicInformation 0 Then we will get following structure in KeyValueInformation buffer: typedef struct _KEY_VALUE_BASIC_INFORMATION { ULONG TitleIndex; ULONG Type; ULONG NameLength; WCHAR Name[1]; } KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION; Again we are interested only in Name and NameLength. If there is no entry for shifted Index we will return error STATUS_NO_MORE_ENTRIES. #define STATUS_NO_MORE_ENTRIES 0x8000001A 6. System services and drivers System services and drivers are enumerated by four independent API functions. Their connections is different in each Windows version. That's why we have to hook all four functions. BOOL EnumServicesStatusA( SC_HANDLE hSCManager, DWORD dwServiceType, DWORD dwServiceState, LPENUM_SERVICE_STATUS lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned, LPDWORD lpResumeHandle ); BOOL EnumServiceGroupW( SC_HANDLE hSCManager, DWORD dwServiceType, DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, DWORD dwUnknown ); BOOL EnumServicesStatusExA( SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCTSTR pszGroupName ); BOOL EnumServicesStatusExW( SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCTSTR pszGroupName ); The most important here is lpServices which points on the buffer where the list of services would be stored. And also lpServicesReturned pointing on the number of records in result is important. Structure of data in the output buffer depends on the type of function. For functions EnumServicesStatusA and EnumServicesGroupW is returned structure typedef struct _ENUM_SERVICE_STATUS { LPTSTR lpServiceName; LPTSTR lpDisplayName; SERVICE_STATUS ServiceStatus; } ENUM_SERVICE_STATUS, *LPENUM_SERVICE_STATUS; typedef struct _SERVICE_STATUS { DWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; } SERVICE_STATUS, *LPSERVICE_STATUS; for EnumServicesStatusExA a EnumServicesStatusExW it it typedef struct _ENUM_SERVICE_STATUS_PROCESS { LPTSTR lpServiceName; LPTSTR lpDisplayName; SERVICE_STATUS_PROCESS ServiceStatusProcess; } ENUM_SERVICE_STATUS_PROCESS, *LPENUM_SERVICE_STATUS_PROCESS; typedef struct _SERVICE_STATUS_PROCESS { DWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; DWORD dwProcessId; DWORD dwServiceFlags; } SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS; We are interested only in lpServiceName which is the name of system service. Records have static size, so if we want to hide one we will move all following records by its size. Here we have to differentiate between the size of SERVICE_STATUS and SERVICE_STATUS_PROCESS. 7. Hooking and spreading To get the desiderative efect we have to hook all running processes and also all processes which would be created later. New processes should be hooked before running their first instruction of their own code otherwise they would be able to see our hidden objects in the time before they would be hooked. 7.1 Rights At first it is good to know that we need at least administrators rights to get access to all running processes. The best possibility is to run our process as system service which run on user SYSTEM. To install the service we also need special rights. Also getting SeDebugPrivilege is very useful. This can be done using API OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges. BOOL OpenProcessToken( HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle ); BOOL LookupPrivilegeValue( LPCTSTR lpSystemName, LPCTSTR lpName, PLUID lpLuid ); BOOL AdjustTokenPrivileges( HANDLE TokenHandle, BOOL DisableAllPrivileges, PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState, PDWORD ReturnLength ); Neglecting errors the code can look like this: #define SE_PRIVILEGE_ENABLED 0x0002 #define TOKEN_QUERY 0x0008 #define TOKEN_ADJUST_PRIVILEGES 0x0020 HANDLE hToken; LUID DebugNameValue; TOKEN_PRIVILEGES Privileges; DWORD dwRet; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,hToken); LookupPrivilegeValue(NULL,"SeDebugPrivilege",&DebugNameValue); Privileges.PrivilegeCount=1; Privileges.Privileges[0].Luid=DebugNameValue; Privileges.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken,FALSE,&Privileges,sizeof(Privileges), NULL,&dwRet); CloseHandle(hToken); 7.2 Global hook Enumeration of processes is done by already metioned API function NtQuerySystemInformation. There are few native processes in the system, so we will use the method of rewriting first instructions of the function to hook them. For each running process we will do the same. We will allocate a part of memory in target process where we will write our new code for functions we want to hook. Then we will change the first five bytes of these functions with jmp instruction. This jump will redirect the execution to our code. So the jmp instruction will be executed immediately when the hooked function is called. We have to save first instructions of each function which is rewritten. We need them to call original code of the hooked function. Saving instructions is described in chapter 3.2.3 in the document "Hooking Windows API". At first we have to open target process via NtOpenProcess and get the handle. This will fail if we don't have enough rights. NTSTATUS NtOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL ); ProcessHandle is a pointer on a handle where the result will be stored. DesiredAccess should be set on PROCESS_ALL_ACCESS. We will set PID of target process to UniqueProcess part of ClientId structure, UniqueThread should be 0. Open handle can be always closed via NtClose. #define PROCESS_ALL_ACCESS 0x001F0FFF Now we are going to allocate the part of memory for our code. This can be done using NtAllocateVirtualMemory. NTSTATUS NtAllocateVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID BaseAddress, IN ULONG ZeroBits, IN OUT PULONG AllocationSize, IN ULONG AllocationType, IN ULONG Protect ); ProcessHandle is the one from NtOpenProcess. BaseAddress is a pointer on a pointer on the beginning where we want to allocate. Here will be stored the address of the allocated memory. Input value can be NULL. AllocationSize is a pointer on number of bytes we want to allocate. And again it is also used as output value for the real number of allocated bytes. It is good to set AllocationType to MEM_TOP_DOWN in addition to MEM_COMMIT because the memory would be allocated on the highest possible address near DLLs. #define MEM_COMMIT 0x00001000 #define MEM_TOP_DOWN 0x00100000 Then we can write our code there using NtWriteVirtualMemory. NTSTATUS NtWriteVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL ); BaseAddress will be that address returned by NtAllocateVirtualMemory. Buffer points on bytes we want to write, BufferLength is number of bytes we want to write. Now we have to hook single functions. Only library which is loaded to all processes is ntdll.dll. So we have to check if function we want to hook is imported to the process if it is not from ntdll.dll. But the memory where would this function (from another DLL) be could be allocated, so rewriting bytes on its address could easily cause error in target process. This is why we have to check whether library (where function we want to hook is) is loaded to target process. We need to get PEB (Process Environment Block) of target process via NtQueryInformationProcess. NTSTATUS NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ); We will set ProcessInfromationClass to ProcessBasicInformation. Then the PROCESS_BASIC_INFORMATION structure would be returned to ProcessInformation buffer which size is given by ProcessInformationLength. #define ProcessBasicInformation 0 typedef struct _PROCESS_BASIC_INFORMATION { NTSTATUS ExitStatus; PPEB PebBaseAddress; KAFFINITY AffinityMask; KPRIORITY BasePriority; ULONG UniqueProcessId; ULONG InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; PebBaseAddress is what we were looking for. On PebBaseAddress+0x0C is address PPEB_LDR_DATA. This would be get calling NtReadVirtualMemory. NTSTATUS NtReadVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL ); Parameters are similar like in NtWriteVirtualMemory. On PPEB_LDR_DATA+0x1C is address InInitializationOrderModuleList. It is the list of libraries loaded to the process. We are interested only in a part of this structure. typedef struct _IN_INITIALIZATION_ORDER_MODULE_LIST { PVOID Next, PVOID Prev, DWORD ImageBase, DWORD ImageEntry, DWORD ImageSize, ... ); Next is a pointer on next record, Prev on previous, last record points on first. ImageBase is an address of module in the memory, ImageEntry is the EntryPoint of the module, ImageSize is its size. For all libraries in which we want to hook we will get their ImageBase (e.g. using GetModuleHandle or LoadLibrary). This ImageBase we will compare with ImageBase of each entry in InInitializationOrderModuleList. Now we are ready for hooking. Because we are hooking running processes there is a possibility that the code we would be executed in the moment we will be rewriting it. This can cause error, so at first we will stop all threads in target process. The list of its threads can get via NtQuerySystemInformation with SystemProcessesAndThreadsInformation class. Result of this function is described in chapter 4. But we have to add the description of SYSTEM_THREADS structure where the information about thread is. typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; KPRIORITY BasePriority; ULONG ContextSwitchCount; THREAD_STATE State; KWAIT_REASON WaitReason; } SYSTEM_THREADS, *PSYSTEM_THREADS; For each thread we have to get its handle using NtOpenThread. We will use ClientId for it. NTSTATUS NtOpenThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ); The handle we want will be stored to ThreadHandle. We will set DesiredAccess to THREAD_SUSPEND_RESUME. #define THREAD_SUSPEND_RESUME 2 ThreadHandle will be used for calling NtSuspendThread. NTSTATUS NtSuspendThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL ); Suspended process is ready for rewriting. We will proceed as it is described in chapter 3.2.2 in "Hooking Windows API". Only difference will be in using functions for other processes. After a hook we will revive all process threads calling NtResumeThread. NTSTATUS NtResumeThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL ); 7.3 New processes Infection of all running processes does not affect processes which would be run later. We could get the process list and after a while get a new one and compare them and then infect those processes which are in second list but not in first. But this method is very unreliable. Much better is to hook function which is always called when new process starts. Because of hooking all running processes on the system we can't miss any new with this method. We can hook NtCreateThread but it is not the easiest way. We will hook NtResumeThread which is also called everytime after the new process is created. It is called after NtCreateThread. The only problem with NtResumeThread is that it is called not only when new process starts. But we can easily get over this. NtQueryInformationThread will give us an information about which process owns the specific thread. The last thing we have to do is to check whether this process is already hooked or not. This can be done by reading first byte of any function we are hooking. NTSTATUS NtQueryInformationThread( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, OUT PVOID ThreadInformation, IN ULONG ThreadInformationLength, OUT PULONG ReturnLength OPTIONAL ); ThreadInformationClass is information class and it should be set in our case to ThreadBasicInformation. ThreadInformation is the buffer for result which size is ThreadInformationLength bytes. #define ThreadBasicInformation 0 For class ThreadBasicInformation is this structure returned: typedef struct _THREAD_BASIC_INFORMATION { NTSTATUS ExitStatus; PNT_TIB TebBaseAddress; CLIENT_ID ClientId; KAFFINITY AffinityMask; KPRIORITY Priority; KPRIORITY BasePriority; } THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION; In ClientId is the PID of which owns the thread. Now we have to infect the new process. The problem is that the new process has only ntdll.dll in its memory. All others modules are loaded immediately after calling NtResumeThread. There are several ways how to handle this problem. E.g. we can hook API called LdrInitializeThunk which is called during process init. NTSTATUS LdrInitializeThunk( DWORD Unknown1, DWORD Unknown2, DWORD Unknown3 ); At first we will run original code and then we will hook all functions we want in this new process. But it will be better to unhook LdrInitializeThunk because it is called many times later and we don't want to rehook all functions again. Everything here is done before execution of the first instruction of hooked application. That's why there is no chance it would call any of hooked functions before we hook it. The hooking in itself is the same as when hooking running process but here we don't care about running threads. 7.4 DLL In each process in the system is the copy of ntdll.dll. That mean we can hook any function from this module in the process init. But how about functions from other modules like kernel32.dll or advapi32.dll? And there are also several processes which has only ntdll.dll. All other modules can be loaded dynamically in the middle of the code after the process hook. That's why we have to hook LdrLoadDll which loades new modules. NTSTATUS LdrLoadDll( PWSTR szcwPath, PDWORD pdwLdrErr, PUNICODE_STRING pUniModuleName, PHINSTANCE pResultInstance ); The most important for us here is pUniModuleName which is the name of the module. pResultInstance will be filled with its address if the call is successful. We will call original LdrLoadDll and then hook all functions in loaded module. 8. Memory When we are hooking a function we modify its first bytes. Via calling NtReadVirtualMemory anyone can detect that a function is hooked. So we have to hook NtReadVirtualMemory to prevent detecting. NTSTATUS NtReadVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL ); We have changed bytes on the begining of all functions we hooked and we have also allocated memory for our new code. We should check whether caller reads some of these bytes. If we have our bytes in the range from BaseAddress to BaseAddress + BufferLength we have to change some bytes in Buffer. If one ask for bytes from our allocated memory we should return empty Buffer and an error STATUS_PARTIAL_COPY. This value says not all requested bytes were copied to the Buffer. It is also used when asking for unallocated memory. ReturnLength should be set to 0 in this case. #define STATUS_PARTIAL_COPY 0x8000000D If one ask for first bytes of hooked function we have to call original code and than we should copy original bytes (we have saved them for original calls) to Buffer. Now the process is not able to detect he is hooked via reading its memory. Also if you debug hooked process debugger will have a problem. It will show original bytes but it will execute our code. To make hiding perfect we can also hook NtQueryVirtualMemory. This function is used to get information about virtual memory. We can hook it to prevent detecting our allocated memory. NTSTATUS NtQueryVirtualMemory( IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN ULONG MemoryInformationLength, OUT PULONG ReturnLength OPTIONAL ); MemoryInformationClass specifies the class of data which are returned. First two types are interesting for us. #define MemoryBasicInformation 0 #define MemoryWorkingSetList 1 For class MemoryBasicInformation is returned this structure: typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; PVOID AllocationBase; ULONG AllocationProtect; ULONG RegionSize; ULONG State; ULONG Protect; ULONG Type; } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; Each memory section has its size RegionSize and its type Type. Free memory has type MEM_FREE. #define MEM_FREE 0x10000 If a section before ours has type MEM_FREE we should add the size of ours section to its RegionSize. If the following section is also MEM_FREE we should add following section size again that RegionSize. If a section before ours has another type we will return MEM_FREE for our section. Its size is counted again according to following section. For class MemoryWorkingSetList is returned structure: typedef struct _MEMORY_WORKING_SET_LIST { ULONG NumberOfPages; ULONG WorkingSetList[1]; } MEMORY_WORKING_SET_LIST, *PMEMORY_WORKING_SET_LIST; NumberOfPages is the number of items in WorkingSetList. This number should be decreased. We should find ours section in WorkingSetList and move following records over ours. WorkingSetList is an array of DWORDs where higher 20 bits specifies higher 20 bits of section address and lower 12 bits specifies flags. 9. Handle Calling NtQuerySystemInformation with SystemHandleInformation class gives us array of all open handles in _SYSTEM_HANDLE_INFORMATION_EX strucure. #define SystemHandleInformation 0x10 typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG ProcessId; UCHAR ObjectTypeNumber; UCHAR Flags; USHORT Handle; PVOID Object; ACCESS_MASK GrantedAccess; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; typedef struct _SYSTEM_HANDLE_INFORMATION_EX { ULONG NumberOfHandles; SYSTEM_HANDLE_INFORMATION Information[1]; } SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; ProcessId specifies the process which owns the handle. ObjectTypeNumber is handle type. NumberOfHandles is number of records in Information array. Hiding one item is trivial. We have to remove all following records by one and decrease NumberOfHandles. Removing all following is needed because handles in array are grouped by ProcessId. That mean all handles from one single process are together. And for one process the number Handle is growing. Now remember structure _SYSTEM_PROCESSES which is returned by this function with SystemProcessesAndThreadsInformation class. Here we can see that each process has an information about its number of handles in HandleCount. If we want to be perfect we should modify HandleCount owing to how many handles we hide when calling this function with SystemProcessesAndThreadsInformation class. But this correction would be very time-consuming. There are many handles opening and closing in very short time during normal system running. So it can easily happend that number of handles is changed in between two calls of this function and we don't need to change HandleCount. 9.1 Naming handle and getting type Handle hiding is trivial but find out which handle to hide is little bit harder. If we have e.g. hidden process we should hide all its handles and all handles which are connected with it. Hiding handles of this process is again trivial. We are only comparing ProcessId of handle and PID of our process and when they equals we hide it. But handles of other processes have to be named before we can compare something. The number of handles in the system is usually very big, so the best we can do is to compare handle type first before trying to name it. Naming types will save a lot of time for handles we are not interested in. Naming handle and handle type can be done via calling NtQueryObject. NTSTATUS ZwQueryObject( IN HANDLE ObjectHandle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG ObjectInformationLength, OUT PULONG ReturnLength OPTIONAL ); ObjectHandle is a handle we want to get info about, ObjectInformationClass is the type of information which will be stored into ObjectInformation buffer which is ObjectInformationLength bytes long. We will use class ObjectNameInformation and ObjectAllTypesInformation. ObjectNameInfromation class will fill the buffer with OBJECT_NAME_INFORMATION structure, ObjectAllTypesInformation class with OBJECT_ALL_TYPES_INFORMATION structure then. #define ObjectNameInformation 1 #define ObjectAllTypesInformation 3 typedef struct _OBJECT_NAME_INFORMATION { UNICODE_STRING Name; } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; Name determines the name of the handle. typedef struct _OBJECT_TYPE_INFORMATION { UNICODE_STRING Name; ULONG ObjectCount; ULONG HandleCount; ULONG Reserved1[4]; ULONG PeakObjectCount; ULONG PeakHandleCount; ULONG Reserved2[4]; ULONG InvalidAttributes; GENERIC_MAPPING GenericMapping; ULONG ValidAccess; UCHAR Unknown; BOOLEAN MaintainHandleDatabase; POOL_TYPE PoolType; ULONG PagedPoolUsage; ULONG NonPagedPoolUsage; } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; typedef struct _OBJECT_ALL_TYPES_INFORMATION { ULONG NumberOfTypes; OBJECT_TYPE_INFORMATION TypeInformation; } OBJECT_ALL_TYPES_INFORMATION, *POBJECT_ALL_TYPES_INFORMATION; Name determines the name of type object which immediately follows each OBJECT_TYPE_INFORMATION structure. The next OBJECT_TYPE_INFORMATION structure follows this Name, starting on the first four-byte boundary. ObjectTypeNumber from SYSTEM_HANDLE_INFORMATION structure is an index to TypeInformation array. Harder is to get the name of handle from other process. There are two possibilities how to name it. First is to copy the handle via NtDuplicateObject to our process and then to name it. This method will fail for some specific types of handles. But it will fail only for few, so we can stay calm and use this. NtDuplicateObject( IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN HANDLE TargetProcessHandle, OUT PHANDLE TargetHandle OPTIONAL, IN ACCESS_MASK DesiredAccess, IN ULONG Attributes, IN ULONG Options ); SourceProcessHandle is a handle of process which owns SourceHandle which is the handle we want to copy. TargetProcessHandle is handle of process where to copy. This will be handle to our process in our case. TargetHandle is the pointer on handle where to save a copy of original handle. DesiredAccess should be set to PROCESS_QUERY_INFORMATION, Attribures and Options to 0. Second naming method which works with any handle is to use system driver. Source code for this is available in OpHandle project on my site http://rootkit.host.sk. 10. Ports The easiest way to enumarate open ports is to use functions called AllocateAndGetTcpTableFromStack and AllocateAndGetUdpTableFromStack, and or AllocateAndGetTcpExTableFromStack and AllocateAndGetUdpExTableFromStack from iphlpapi.dll. The Ex functions are available since Windows XP. typedef struct _MIB_TCPROW { DWORD dwState; DWORD dwLocalAddr; DWORD dwLocalPort; DWORD dwRemoteAddr; DWORD dwRemotePort; } MIB_TCPROW, *PMIB_TCPROW; typedef struct _MIB_TCPTABLE { DWORD dwNumEntries; MIB_TCPROW table[ANY_SIZE]; } MIB_TCPTABLE, *PMIB_TCPTABLE; typedef struct _MIB_UDPROW { DWORD dwLocalAddr; DWORD dwLocalPort; } MIB_UDPROW, *PMIB_UDPROW; typedef struct _MIB_UDPTABLE { DWORD dwNumEntries; MIB_UDPROW table[ANY_SIZE]; } MIB_UDPTABLE, *PMIB_UDPTABLE; typedef struct _MIB_TCPROW_EX { DWORD dwState; DWORD dwLocalAddr; DWORD dwLocalPort; DWORD dwRemoteAddr; DWORD dwRemotePort; DWORD dwProcessId; } MIB_TCPROW_EX, *PMIB_TCPROW_EX; typedef struct _MIB_TCPTABLE_EX { DWORD dwNumEntries; MIB_TCPROW_EX table[ANY_SIZE]; } MIB_TCPTABLE_EX, *PMIB_TCPTABLE_EX; typedef struct _MIB_UDPROW_EX { DWORD dwLocalAddr; DWORD dwLocalPort; DWORD dwProcessId; } MIB_UDPROW_EX, *PMIB_UDPROW_EX; typedef struct _MIB_UDPTABLE_EX { DWORD dwNumEntries; MIB_UDPROW_EX table[ANY_SIZE]; } MIB_UDPTABLE_EX, *PMIB_UDPTABLE_EX; DWORD WINAPI AllocateAndGetTcpTableFromStack( OUT PMIB_TCPTABLE *pTcpTable, IN BOOL bOrder, IN HANDLE hAllocHeap, IN DWORD dwAllocFlags, IN DWORD dwProtocolVersion; ); DWORD WINAPI AllocateAndGetUdpTableFromStack( OUT PMIB_UDPTABLE *pUdpTable, IN BOOL bOrder, IN HANDLE hAllocHeap, IN DWORD dwAllocFlags, IN DWORD dwProtocolVersion; ); DWORD WINAPI AllocateAndGetTcpExTableFromStack( OUT PMIB_TCPTABLE_EX *pTcpTableEx, IN BOOL bOrder, IN HANDLE hAllocHeap, IN DWORD dwAllocFlags, IN DWORD dwProtocolVersion; ); DWORD WINAPI AllocateAndGetUdpExTableFromStack( OUT PMIB_UDPTABLE_EX *pUdpTableEx, IN BOOL bOrder, IN HANDLE hAllocHeap, IN DWORD dwAllocFlags, IN DWORD dwProtocolVersion; ); There is another way to do this stuff. When program creates a socket and starts listening it surely has an open handle for it and for open port. We can enumerate all open handles in the system and send them special buffer via NtDeviceIoControlFile to find out whether the handle is for open port or not. This will also give us information about the port. Because there are a lot of open handles we will test only handles which type is File and name is \Device\Tcp or \Device\Udp. Open ports have only this type and name. When we look to the code of iphlpapi.dll functions above we find out that these functions also calls NtDeviceIoControlFile and sends special buffer to get a list of all open ports in the system. That mean only functions we need to hook for hiding ports is NtDeviceIoControlFile. NTSTATUS NtDeviceIoControlFile( IN HANDLE FileHandle IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength ); Interesting agruments for us now are FileHandle which specify a handle of device to communicate with, IoStatusBlock which points to a variable that receives the final completion status and information about the requested operation, IoControlCode that is a number specifying type of the device, method, file access and a function. InputBuffer contains input data that are InputBufferLength bytes long and similarly OutputBuffer and OutputbufferLength. 10.1 Netstat, OpPorts on WinXP, FPort on WinXP Getting a list of all open ports is the first way which is used by e.g. OpPorts and FPort on Windows XP and also Netstat. Programs calls here NtDeviceIoControlFile twice with IoControlCode 0x000120003. OutputBuffer is filled after a second call. Name of FileHandle is here alwats \Device\Tcp. InputBuffer differs for different types of call: To get an array of MIB_TCPROW InputBuffer looks as follows: first call: 0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 second call: 0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 To get an array of MIB_UDPROW: first call: 0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 second call: 0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 To get an array of MIB_TCPROW_EX: first call: 0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 second call: 0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 To get an array of MIB_UDPROW_EX: first call: 0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 second call: 0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 You can see the buffers are different in few bytes only. We can lucidly recapitulate these: Calls we are interested in have InputBuffer[1] set to 0x04 and mainly InputBuffer[17] on 0x01. Only after these input data we get filled OutputBuffer with desiderative tables. If we want to get info about TCP ports we set InputBuffer[0] on 0x00, or on 0x01 for information about UDP. If we want extended output tables (MIB_TCPROW_EX or MIB_UDPROW_EX) we use Inputbuffer[16] in second call set to 0x02. If we find out the call with these parameters we can change the output buffer. To get number of rows in output buffer simply divide Information from IoStatusBlock by size of the row. Hiding of one row is easy then. Just rewrite it with following rows and delete last row. Don't forget to change OutputBufferLength and IoStatusBlock. 10.2 OpPorts on Win2k and NT4, FPort on Win2k We use NtDeviceIoControlFile with IoControlCode 0x00210012 to determine if the handle of File type and name \Device\Tcp or \Device\Udp is the handle of open port. So at first we compare IoControlCode and then a type and the name of the handle. If it is still interesting then we compare the length of input buffer which should be equal to the length of struct TDI_CONNECTION_IN. This length is 0x18. OutputBuffer is TDI_CONNETION_OUT. typedef struct _TDI_CONNETION_IN { ULONG UserDataLength, PVOID UserData, ULONG OptionsLength, PVOID Options, ULONG RemoteAddressLength, PVOID RemoteAddress } TDI_CONNETION_IN, *PTDI_CONNETION_IN; typedef struct _TDI_CONNETION_OUT { ULONG State, ULONG Event, ULONG TransmittedTsdus, ULONG ReceivedTsdus, ULONG TransmissionErrors, ULONG ReceiveErrors, LARGE_INTEGER Throughput LARGE_INTEGER Delay, ULONG SendBufferSize, ULONG ReceiveBufferSize, ULONG Unreliable, ULONG Unknown1[5], USHORT Unknown2 } TDI_CONNETION_OUT, *PTDI_CONNETION_OUT; Concrete implementation of how to determine the handle is open port is available in source code of OpPorts on http://rootkit.host.sk. We are interested in hiding specific port now. We already compared InputBufferLength and IoControlCode. Now we have to compare RemoteAddressLength. This is always 3 or 4 for open port. The last we have to do is to compare ReceivedTsdus from OutputBuffer which contains the port in network form and our list of ports we want to hide. Differentiate between TCP and UDP is done according to the name of the handle. By deleting OutputBuffer, changing IoStatusBlock and returning the value of STATUS_INVALID_ADDRESS we will hide this port. 11. Ending Concrete implementation of described techniques will be available with the source code of Hander defender rootkit in version 1.0.0 on its homepage http://rootkit.host.sk and on http://www.rootkit.com. It is possible I will add some more information about invisibility on Windows NT in the future. New versions of this document could also contain improvement of described methods or new comments. Special thanks to Ratter who give me a lot of knowhow which was necessary to write this document and to code project Hacker defender. Send all remarks to holy_father@phreaker.net or to the board on http://rootkit.host.sk. Sursa: Holy_Father 'Invisibility on NT boxes, How to become unseen on Windows NT (Version: 1.2)' (VX heavens)
  13. Creating your own driver loader in C | Driver Loader | Source Code | Rootkit January 27, 2011 — genesisdatabase Technically, there’s 2 way of loading a rootkit according to Greg Hoglund when he wrote Rootkits: Subverting the Windows Kernel book. One is called The Quick-And-Dirty Way to Load a Driver. This method allows you to “load a driver into the kernel without having to create any registry keys. “Pageable” refers to memory that can be swapped to disk. If a driver is pageable, any part of the driver could be paged out (that is, swapped from memory to disk). Sometimes when memory is paged out, it cannot be accessed; an attempt to do so will result in the infamous Blue Screen of Death (a system crash)” by using an undocumented API call. A sample loader that uses this method is called migbot where you can find the source code here. //---------------------------------------------------------------- // load a sys file as a driver using undocumented method //---------------------------------------------------------------- bool load_sysfile() { SYSTEM_LOAD_AND_CALL_IMAGE GregsImage; WCHAR daPath[] = L"\\??\\C:\\MIGBOT.SYS"; ////////////////////////////////////////////////////////////// // get DLL entry points ////////////////////////////////////////////////////////////// if(!(RtlInitUnicodeString = (RTLINITUNICODESTRING)GetProcAddress( GetModuleHandle("ntdll.dll"),"RtlInitUnicodeString"))) { return false; } if(!(ZwSetSystemInformation = (ZWSETSYSTEMINFORMATION)GetProcAddress(GetModuleHandle("ntdll.dll"),"ZwSetSystemInformation" ))) { return false; } RtlInitUnicodeString(&(GregsImage.ModuleName),daPath); if(!NT_SUCCESS(ZwSetSystemInformation(SystemLoadAndCallImage,&GregsImage,sizeof(SYSTEM_LOAD_AND_CALL_IMAGE)))) { return false; } return true; } What you see above is the loading code for migbotloader. “Migbot does not offer an unload feature; once it is loaded, it cannot be unloaded until reboot. Think of this as a “fire-and-forget” operation. The advantage to using this method is that it can be stealthier than more-established protocols. The downside is that it complicates the rootkit design. For migbot, this is a good solution; but for complex rootkits with many hooks, this method would require supporting too much overhead.” The other method would be the right way to load a driver! According to Greg Hoglund, “the established and correct way to load a driver is to use the Service Control Manager (SCM). Using the SCM causes registry keys to be created. When a driver is loaded using the SCM, it is non-pageable. This means your callback functions, IRP-handling functions, and other important code will not vanish from memory, be paged out, or cause Blue Screens of Death. This is a Good Thing.” bool _util_load_sysfile(char *theDriverName) { char aPath[1024]; char aCurrentDirectory[515]; SC_HANDLE sh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!sh) { return false; } GetCurrentDirectory( 512, aCurrentDirectory); _snprintf(aPath,1022,"%s\\%s.sys",aCurrentDirectory,theDriverName); printf("loading %s\n", aPath); SC_HANDLE rh = CreateService(sh, theDriverName, theDriverName, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, aPath, NULL, NULL, NULL, NULL, NULL); if(!rh) { if (GetLastError() == ERROR_SERVICE_EXISTS) { // service exists rh = OpenService(sh,theDriverName,SERVICE_ALL_ACCESS); if(!rh) { CloseServiceHandle(sh); return false; } } else { CloseServiceHandle(sh); return false; } } // start the drivers if(rh) { if(0 == StartService(rh, 0, NULL)) { if(ERROR_SERVICE_ALREADY_RUNNING == GetLastError()) { // no real problem } else { CloseServiceHandle(sh); CloseServiceHandle(rh); return false; } } CloseServiceHandle(sh); CloseServiceHandle(rh); } return true; } What you are about to see will make you smash your screen! Generally what you are about to read is the source code that i have created to load your own personal driver loader. This source code has been written and compiled by me a year ago when i first started my venture into rootkit. Generally it is a CLI menu driven loader, you can choose to load, unload, start and stop the driver you have. This source code should be easy to compile in most IDE, the IDE that was used was Microsoft Visual Studio C++ 6.0. Download the compiled source code (.exe) for whatever reason you need! /* * This source code was written by GenesisDatabase * Visit http://genesisdatabase.wordpress.com for more source codes! * * Date of release: 27th January 2011 */ #include <windows.h> #include <stdio.h> #define Cleanup(x, y, z) {x = y; goto z;} #define FLUSH fflush(stdin); #define DRIVER_LOADED 0x00000001 #define DRIVER_STARTED 0x00000002 #define DRIVER_STOPPED 0x00000003 #define DRIVER_UNLOADED 0x00000004 #define DRIVER_CANT_LOAD 0x00000010 #define DRIVER_CANT_START 0x00000020 #define DRIVER_CANT_STOP 0x00000030 #define DRIVER_CANT_UNLOAD 0x00000040 typedef struct { DWORD driverstatus; }DRIVER_LOADER, *PDRIVER_LOADER; int FileExists(const char *driverpath) { FILE *fExists = fopen(driverpath, "r"); if(!fExists) return 0; fclose(fExists); return 1; } int myLoadDriver(const char *drivername, const char *driverpath) { SC_HANDLE hSCManager; SC_HANDLE hService; int ret = 1; if(!FileExists(driverpath)) Cleanup(ret, -1, c); hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!hSCManager) Cleanup(ret, -2, c); hService = CreateService( hSCManager, drivername, drivername, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,//SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driverpath, NULL, NULL, NULL, NULL, NULL); if(!hService) Cleanup(ret, -3, c); c: if(hService) CloseServiceHandle(hService); if(hSCManager) CloseServiceHandle(hSCManager); return ret; } int myStartDriver(char *drivername) { SC_HANDLE hSCManager; SC_HANDLE hService; int ret = 1; hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!hSCManager) Cleanup(ret, -1, c); hService = OpenService(hSCManager, drivername, SERVICE_ALL_ACCESS); if(!hService) Cleanup(ret, -2, c); if(!StartService(hService, 0, NULL)) Cleanup(ret, -3, c); c: if(hService) CloseServiceHandle(hService); if(hSCManager) CloseServiceHandle(hSCManager); return ret; } int myStopDriver(char *drivername) { SC_HANDLE hSCManager; SC_HANDLE hService; SERVICE_STATUS ss; int ret = 1; hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!hSCManager) Cleanup(ret, -1, c); hService = OpenService(hSCManager, drivername, SERVICE_ALL_ACCESS); if(!hService) Cleanup(ret, -2, c); if(!ControlService(hService, SERVICE_CONTROL_STOP, &ss)) Cleanup(ret, -3, c); c: if(hService) CloseServiceHandle(hService); if(hSCManager) CloseServiceHandle(hSCManager); return ret; } int myUnloadDriver(const char *drivername) { SC_HANDLE hSCManager; SC_HANDLE hService; SERVICE_STATUS ss; int ret = 1; hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!hSCManager) Cleanup(ret, -1, c); hService = OpenService(hSCManager, drivername, SERVICE_ALL_ACCESS); if(!hService) Cleanup(ret, -2, c); // try to stop service first ControlService(hService, SERVICE_CONTROL_STOP, &ss); if(!DeleteService(hService)) Cleanup(ret, -4, c); c: if(hService) CloseServiceHandle(hService); if(hSCManager) CloseServiceHandle(hSCManager); return ret; } void funcLoadDriver(DRIVER_LOADER *dl) { char drivername[256+1]; char driverpath[256+1]; char selection; int err; for( ; ; ) { printf(" Enter driver's name\n - "); FLUSH; scanf("%256[^\n]", drivername); printf(" Enter driver's full path\n - "); FLUSH; scanf("%256[^\n]", driverpath); printf(" Confirm (Y - yes | N - no | B - back): "); FLUSH; scanf("%c", &selection); switch(selection) { case 'y': case 'Y': printf(" Performing : myLoadDriver\n"); err = myLoadDriver(drivername, driverpath); dl->driverstatus = DRIVER_LOADED; if(err != 1) { dl->driverstatus = DRIVER_CANT_LOAD; printf(" Error : myLoadDriver (%d)\n", err); printf(" GetLastError: (%d)\n", GetLastError()); return; }printf(" Success : myLoadDriver\n"); return; case 'n': case 'N': break; case 'b': case 'B': return; default: printf(" Wrong option selected, default to N\n"); break; } printf("\n"); } } void funcStartDriver(DRIVER_LOADER *dl) { char drivername[256+1]; char selection; int err; for( ; ; ) { printf(" Enter driver's name\n - "); FLUSH; scanf("%256[^\n]", drivername); printf(" Confirm (Y - yes | N - no | B - back): "); FLUSH; scanf("%c", &selection); switch(selection) { case 'y': case 'Y': printf(" Performing : myStartDriver\n"); err = myStartDriver(drivername); dl->driverstatus = DRIVER_STARTED; if(err != 1) { dl->driverstatus = DRIVER_CANT_START; printf(" Error : myStartDriver (%d)\n", err); printf(" GetLastError: (%d)\n", GetLastError()); return; }printf(" Success : myStartDriver\n"); return; case 'n': case 'N': break; case 'b': case 'B': return; default: printf(" Wrong option selected, default to N\n"); break; } printf("\n"); } } void funcStopDriver(DRIVER_LOADER *dl) { char drivername[256+1]; char selection; int err; for( ; ; ) { printf(" Enter driver's name\n - "); FLUSH; scanf("%256[^\n]", drivername); printf(" Confirm (Y - yes | N - no | B - back): "); FLUSH; scanf("%c", &selection); switch(selection) { case 'y': case 'Y': printf(" Performing : myStopDriver\n"); err = myStopDriver(drivername); dl->driverstatus = DRIVER_STOPPED; if(err != 1) { dl->driverstatus = DRIVER_CANT_STOP; printf(" Error : myStopDriver (%d)\n", err); printf(" GetLastError: (%d)\n", GetLastError()); return; }printf(" Success : myStopDriver\n"); return; case 'n': case 'N': break; case 'b': case 'B': return; default: printf(" Wrong option selected, default to N\n"); break; } printf("\n"); } } void funcUnloadDriver(DRIVER_LOADER *dl) { char drivername[256+1]; char selection; int err; for( ; ; ) { printf(" Enter driver's name\n - "); FLUSH; scanf("%256[^\n]", drivername); printf(" Confirm (Y - yes | N - no | B - back): "); FLUSH; scanf("%c", &selection); switch(selection) { case 'y': case 'Y': printf(" Performing : myUnloadDriver\n"); err = myUnloadDriver(drivername); dl->driverstatus = DRIVER_UNLOADED; if(err != 1) { dl->driverstatus = DRIVER_CANT_UNLOAD; printf(" Error : myUnloadDriver (%d)\n", err); printf(" GetLastError: (%d)\n", GetLastError()); return; }printf(" Success : myUnloadDriver\n"); return; case 'n': case 'N': break; case 'b': case 'B': return; default: printf(" Wrong option selected, default to N\n"); break; } printf("\n"); } } int main(int argc, char **argv) { DRIVER_LOADER dl; int selection; for( ; ; ) { printf(" 1 - Load a driver\n" " 2 - Start service\n" " 3 - Stop service\n" " 4 - Unload a driver\n" " 0 - Exit\n" "\n" " Select an option: "); FLUSH; scanf("%d", &selection); switch(selection) { case 1: funcLoadDriver(&dl); break; case 2: funcStartDriver(&dl); break; case 3: funcStopDriver(&dl); break; case 4: funcUnloadDriver(&dl); break; case 0: printf(" Thanks for using...\n"); FLUSH; getchar(); return 0; default: break; } printf("\n"); } FLUSH; getchar(); return 0; } Sursa: Creating your own driver loader in C | Driver Loader | Source Code | Rootkit « Genesis Database
  14. [r00tkit] SSDT Hook for dummies Oct.10, 2010 Here I am again with my first tutorial which only focus on kerneland. We will do a simple approach to the System Service Dispatch Table (SSDT) Hook. The SSDT is in fact an array in which are stored all the syscalls addresses. A syscall is a function supplied straight by the kernel (kerneland) and usable by all userland processes. In order to hook a syscall in the SSDT, we will have thus to replace its address in the SSDT by the address of our function. The syscall we hook in the script will be ZwSetValueKey and will be our main thread all along this article. Code: /* Author: Shp * Website: http://www.shp-box.fr * Date: the 9th of October 2010 * Name: ssdt hook ZwSetValueKey This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ */ #include <wdm.h> /****************/ /* Declarations */ /****************/ #pragma pack(1) typedef struct ServiceDescriptorEntry { unsigned int *ServiceTableBase; unsigned int *ServiceCounterTableBase; unsigned int NumberOfServices; unsigned char *ParamTableBase; } SSDT_Entry; #pragma pack() __declspec(dllimport) SSDT_Entry KeServiceDescriptorTable; // We import KeServiceDescriptorTable (ntoskrnl.exe) // SYSTEMSERVICE returns the address of the Nt* function corresponding to the Zw* function we put in argument #define SYSTEMSERVICE(_func) \ KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_func+1)] typedef NTSTATUS (*ZWSETVALUEKEY)( // The type of the target function HANDLE KeyHandle, PUNICODE_STRING ValueName, ULONG TitleIndex OPTIONAL, ULONG Type, PVOID Data, ULONG DataSize ); ZWSETVALUEKEY ZwSetValueKeyOriginal; // We will call this function to call the original target function when its address will be replaced by our hook function address in the SSDT /*******************/ /* The Hook Function */ /*******************/ // Our hook function will avoid values writing for "Run" and "RunOnce" key: in this way it prevents malwares from writing their path in those keys in order to open up at each reboot. NTSTATUS ZwSetValueKeyHook( IN HANDLE KeyHandle, IN PUNICODE_STRING ValueName, IN ULONG TitleIndex OPTIONAL, IN ULONG Type, IN PVOID Data, IN ULONG DataSize ) { PKEY_BASIC_INFORMATION pKeyInformation = NULL; int i, flag = 1; NTSTATUS ret; WCHAR targetKey1[] = L"Run"; // first key target WCHAR targetKey2[] = L"RunOnce"; // second key target unsigned long size = 0, sizeNeeded = 0; DbgPrint("[+] In da hook function =)\n"); ret = ZwQueryKey(KeyHandle, KeyBasicInformation, pKeyInformation, size, &sizeNeeded); // We use this function in order to get the current key name. If it Run or RunOnce we prevent from writing. if((ret == STATUS_BUFFER_TOO_SMALL) || (ret == STATUS_BUFFER_OVERFLOW)) { // If size not enough => we allocate more space memory size = sizeNeeded; pKeyInformation = (PKEY_BASIC_INFORMATION) ExAllocatePoolWithTag(NonPagedPool, sizeNeeded, 'aaaa'); ret = ZwQueryKey(KeyHandle, KeyBasicInformation, pKeyInformation, size, &sizeNeeded); } if(ret != STATUS_SUCCESS) return ZwSetValueKeyOriginal(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize); if( (pKeyInformation->NameLength / sizeof(WCHAR)) == 3) { // 3 == strlen("Run") for(i = 0; i < strlen(targetKey1); i++) { if(pKeyInformation->Name[i] != targetKey1[i]) { // if one character is different from Run key name, flag = 0 flag = 0; break; } } } else if((pKeyInformation->NameLength / sizeof(WCHAR)) == 7) { // 7 == strlen("RunOnce") for(i = 0; i < strlen(targetKey2); i++) { if(pKeyInformation->Name[i] != targetKey2[i]) { // if one character is different from RunOnce key name, flag = 0 flag = 0; break; } } } else flag = 0; if(!flag) // If flag == 0 => normal work ... return ZwSetValueKeyOriginal(KeyHandle, ValueName, TitleIndex, Type, Data, DataSize); DbgPrint("[+] Bypassing Run key writing\n"); return STATUS_SUCCESS; // ... else the function will not be executed so no value writing ... } /*****************/ /* SSDT Functions */ /*****************/ void HookSSDT() { DbgPrint("[+] SSDTHOOK: in HookSSDT()\n"); ZwSetValueKeyOriginal = (ZWSETVALUEKEY) SYSTEMSERVICE(ZwSetValueKey); // We save target function address // unprotect CR0 __asm { push eax mov eax, CR0 and eax, 0FFFEFFFFh mov CR0, eax pop eax } // SYSTEMSERVICE(ZwSetValueKey) = (unsigned long *) ZwSetValueKeyHook; // We replace target function address by the address of our hook function // protect cr0 __asm { push eax mov eax, CR0 or eax, NOT 0FFFEFFFFh mov CR0, eax pop eax } // } void UnHookSSDT() { DbgPrint("[+] SSDTHOOK: in UnHookSSDT()\n"); // unprotect CR0 __asm { push eax mov eax, CR0 and eax, 0FFFEFFFFh mov CR0, eax pop eax } // SYSTEMSERVICE(ZwSetValueKey) = (ZWSETVALUEKEY) ZwSetValueKeyOriginal; // We delete hook by rewriting the good function address instead of our hook function address // protect cr0 __asm { push eax mov eax, CR0 or eax, NOT 0FFFEFFFFh mov CR0, eax pop eax } // } VOID unloadFunction(PDRIVER_OBJECT pDriverObject) { UnHookSSDT(); // unhook function } NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { HookSSDT(); // hook function pDriverObject->DriverUnload = unloadFunction; return STATUS_SUCCESS; } The principle ntoskrnl.exe process exports KeServiceDescriptorTable table hence we will import it. But what does it contain? typedef struct ServiceDescriptorEntry { unsigned int *ServiceTableBase; unsigned int *ServiceCounterTableBase; unsigned int NumberOfServices; unsigned char *ParamTableBase; } SSDT_Entry; ServiceTableBase is the SSDT address and ParamTableBase the System Service Parameter Table (SSPT) address. The SSPT contains for each function in the SSDT the number of bytes taken in arguments. Here is a very useful macro for our code: #define SYSTEMSERVICE(_func) \ KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_func+1)] It allows us to get the address of the place where the address of the _func function in the SSDT is stored since ServiceTableBase (so the SSDT) is an array of addresses.This macro is firstly hardly understandable but WinDbg makes our task easier. To begin with, we are sure that *(PULONG)((PUCHAR)_func+1) corresponds to the index of _func in the SSDT (between the []). Let’s see what is hidden at _func+1 therefore at ZwSetValueKey+1 in our case: kd> u ZwSetValueKey l 1 nt!ZwSetValueKey: 804dda08 b8f7000000 mov eax,0F7h Mov inserts 0xF7 value in eax: for each syscall, the first instruction is so: mov eax, SSDT_INDEX_VALUE Let’s now try to understand the casts: PULONG is the addresses type (PUCHAR cannot contain addresses such as FFFFFFFF because it is one byte size). The problem with PULONG during incrementation of our function (_func+1) is that _func will be incremented by 4 and not by 1! Indeed when we increments a pointer by 1, it is not incremented by 1 but by 1* sizeof(POINTER_TYPE) and we obviously know that sizeof(unsigned long) is 4. Therefore PUCHAR cast increments the pointer by 1 * sizeof(char) so by 1. Disable read-only SSDT protection At last we must know that SSDT writing is by default impossible: it is in most of cases in read-only mode. There are two technicals to modify this protection: one simple and another one less simple. We will choose the simplest one for our script but I will explain in few words how the second one works. CR0 Trick The first technical is called CR0 trick: CR0 is a register that when set to 0 disable all SSDT protections. Here is the assembler script which allows us to disable the protection: __asm { push eax // we save eax mov eax, CR0 // we put CR0 value into eax and eax, 0FFFEFFFFh // we apply the reverser filter mov CR0, eax // we update CR0 pop eax } And to enable it again: __asm { push eax mov eax, CR0 or eax, NOT 0FFFEFFFFh // the reverse operation to get CR0 state as before the hook mov CR0, eax pop eax // we get the eax save } Memory Descriptor List (MDL) The second technical is the use of MDL which allows our script to describe a part of the memory (here the SSDT) and therefore to modify its properties (here write access). The useful functions are MmCreateMdl, MmBuildMdlForNonPagedPool, MmMapLockedPages and MDL_MAPPED_TO_SYSTEM_VA flag. I have not really understood the interest of this method yet but if you are curious I suggest you to go to check out Ivanlef0u article (french). Now let us look at my script. Here is its working: he is going to hook ZwSetValueKey function that is used to modify or add values in a specified registry key. My hook function check if the current key is Run or RunOnce; if it is we prevent from writing. In the other case we let the function work normally. The purpose of this script is to protect the system from malwares that write their path in keys such as HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run. It would allow them, if they had the permission, to start up again at each computer reboot. Sursa: [r00tkit] SSDT Hook for dummies - Shp Labz
  15. E bine asa cu tutorialele? Eu sunt de parere ca sunt lucruri foarte utile si fiecare trebuie sa apara pe index ca o categorie de sine statatoare. Voi ce parere aveti?
  16. Da, îmi place cum sun?, dar vreau s? m? gândesc mai bine. Probabil vom da la vot, dar trebuie mai întâi s? discut?m. R?mâne de v?zut. Cel pu?in câteva zile categoriile r?mân a?a cum sunt.
  17. Am cautat pe la "User Banning" dar nu am gasit Mutat la locul potrivit.
  18. Da, dar tot nu prea se potriveste la programare. Daca era Web Development da. Dar asa cu: HTML, Flash, Template-uri, CSS, Photoshop... Nu prea merge. Vreti PHP sau altceva: exista Programare. Descrierile sunt vechi, la fel si categoriile, ca "Web Design" nu o voi muta la Programare, dar astept sugestii. Stiu ca sunt si alte posturi, am vrut sa le mut, dar e plictisitor sa muti o gramada de topicuri. Si nu prea am urmarit acea sectiune, nu prea stiu ce s-a postat si discutat acolo.
  19. Nu, Web-Design e pentru partea de front-end, presupune doar Design: layout-uri sau mai stiu eu ce, deci Photoshop si familia si nu presupune cunostinte de programare. Doar grafica.
  20. MSetup_x86.exe - 17.6MB
  21. Deja ma plictisesc. Ban sa te linisteti putin.
  22. denjacker: Nu e bine nici asa, pot sa apara probleme daca filtrezi cuvinte. De exemplu, ce se intampla cu Western Union? Nu trebuie sa fi chiar atat de paranoic, sunt cateva caractere mai importante: , - / \ ' " ( ) . < > : $ = [ ] Bine, sunt destule, dar daca sunt inlocuite cu entitatile HTML, pe langa 4 octeti in plus ocupati in baza de date, nu vor fi probleme. Bine, vor fi! Daca cineva introduce un link si ii transformi "/"-le in entitati va fi urat la afisare, dar totul tine de proiectare.
  23. Cel mai simplu, pentru toate tipurile de filtrari, atat XSS cat si SQL Injection sau mai stiu eu ce, transformi caracterele speciale, cat mai multe, in entitati HTML. http://www.exploit-db.com/download_pdf/12904
  24. Principala idee e ca va suporta procesoarele ARM, deci va rula pe tablete si anumite telefoane. Asta e ideea principala, marea schimbare. Iar grafica va trebui adoptada si pentru aceste tipuri de dispozitive.
  25. Super, inseamna ca Romania incepe sa acorde mai multa atentie securitatii informatiei.
×
×
  • Create New...