Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nice work,but poor doc #6

Open
tangxiaodao opened this issue Feb 21, 2024 · 5 comments
Open

Nice work,but poor doc #6

tangxiaodao opened this issue Feb 21, 2024 · 5 comments

Comments

@tangxiaodao
Copy link

Nice work,I'd like contribute for it,but poor document

@diversenok
Copy link
Owner

Hi! Thanks for the interest in the project. What kind of documentation are you looking for?

Most of the library's functionality assumes certain understanding of system mechanisms and familiarity with the underlying low-level API. The goal of this project is to simplify calling functions from ntdll.dll and other system libraries by providing wrappers that integrate them into Delphi. Documenting what these (underlying) system functions do is out of the scope of this project. I know, some of the things might be undocumented and meaningful only to security researchers that reverse engineer Windows. If your goal is understanding the system mechanisms, I can recommend the Windows Internals book; if you want to learn the details about the underlying APIs, NtDoc (which I'm slowly expanding) might have some useful information. Otherwise, just googling the functions that my wrappers call might help.

When it comes to documenting the added functionality of this library, you can find comments inside the interface section of each NtUtils.*.pas file. These files group wrappers for a subset of APIs based on a common topic and each exposed function has a short description. For instance, if you know what registry syscalls exist/do on Windows, you should find functions from NtUtils.Registry.pas pretty self-explanetory.

I can work on better discoverability of the available functionality, though. I was planning to add a table to the readme that summarizes which system mechanisms each library module covers. However, you would still need to check the comments in the interface section of the corresponding module for the complete overview of the exposed functionality.

@tangxiaodao
Copy link
Author

tangxiaodao commented Mar 24, 2024 via email

@tangxiaodao
Copy link
Author

tangxiaodao commented Mar 24, 2024 via email

@diversenok
Copy link
Owner

diversenok commented Mar 26, 2024

For example, whether the header files for Ntdll and Undoc Ntdll have been defined, or is it just a wrapper for the Ntdll function.

You can find definitions in the Headers directory. Most filenames resemble the names of related Windows SDK/WDK and PHNT headers. It's possible to include and use them directly, without relying on any wrappers.

Has PEB, SYSTEM PROCESS, etc. been defined? Does it provide access to SSDT arrays, and does it provide access to APINAME through SSDT.

  • PEB (as well as TEB, and KUSER_SHARED_DATA) are available in the Ntapi.ntpebteb.pas header. There also RtlGetCurrentPeb and NtCurrentTeb for accessing them.
  • "SYSTEM PROCESS"... not sure; do you mean SYSTEM_PROCESS_INFORMATION and related structures for enumerating processes? Then it's in Ntapi.ntexapi.pas, but I would recommend using more Delphi-friendly wrappers from NtUtils.Processes.Snapshots.pas as they do all parsing for you.
  • SSDT is not directly readable from user-mode, but there is a function in NtUtils.ImageHlp.Syscalls.pas that allows retrieving syscall numbers from ntdll.dll or win32u.dll. If the goal is to bypass user-mode hooks by issuing syscalls, NtUtils.AntiHooking.pas offers this functionality transparently to the rest of the code. You specify which ntdll imports to unhook and it automatically redirects related library calls into clean syscall stubs. Internally, it uses IAT hooks and a second instance of ntdll mapped from KnownDlls. Here is an example of how someone might use it:
uses
  Ntapi.WinNt, Ntapi.ntpsapi, NtUtils, NtUtils.AntiHooking, NtUtils.Threads;

function UnhookAndInjectAPC(
  [in] TID: TThreadId;
  [in] Payload: Pointer;
  [in, opt] PayloadParameter: Pointer = nil
): TNtxStatus;
var
  hxThread: IHandle;
begin
  // Dynamically replace our imports of ntdll.NtOpenThread and
  // ntdll.NtQueueApcThreadEx with clean syscall stubs
  Result := RtlxEnforceExternalImportAntiHooking([
    @NtOpenThread,
    @NtQueueApcThreadEx
  ]);

  if not Result.IsSuccess then
    Exit;

  // Open the target thread (now issues a syscall instad of
  // calling ntdll.NtOpenThread)
  Result := NtxOpenThread(hxThread, TID, THREAD_SET_CONTEXT);

  if not Result.IsSuccess then
    Exit;

  // Inject an APC that executes the payload on the thread
  // (now issues a syscall instead of calling ntdll.NtQueueApcThreadEx)
  Result := NtxQueueApcThreadEx(hxThread.Handle, Payload, PayloadParameter);

  // Here the compiler automatically inserts code for closing the thread handle
end;

@tangxiaodao
Copy link
Author

Thank for your reply.

I'll find something from your code and test it.

About SYSTEM_PROCESS ,defined as follow:

    _SYSTEM_PROCESS = record

      NextEntryDelta:ULONG;// Netx Struct Offset,0 = Struct End
      ThreadCount:ULONG;
      Reserved : array [0..5] of ULONG;
      CreateTime:LARGE_INTEGER;
      UserTime:LARGE_INTEGER;
      KernelTime:LARGE_INTEGER;
      ImageName: UNICODE_STRING;
      BasePriority:KPRIORITY;
      ProcessId :ULONG;
      InheritedFromProcessId:ULONG;
      HandleCount:ULONG;
      Reserved2:array[0..1]of ULONG;
      VmCounters : VM_COUNTERS;
      IoCounters : IO_COUNTERS;
      Threads : array [0..0] of SYSTEM_THREADS;
   end;
   SYSTEM_PROCESS = _SYSTEM_PROCESS;
   PSYSTEM_PROCESS = ^SYSTEM_PROCESS;

My EnumKernelSSDT code as follow(Uncompleted && Untest):

procedure EnumKernelSSDT_ID;stdcall;
var
   modIndex:Integer;// 1=ntdll.dll 2=user32.dll 3=gdi32.dll
   tempFile:string;
   modFile:string;
   modData:THandle;
   dataPointer:Pointer;
   r3handle:PDWORD;
   modHandle:Integer;
   optHeader:IMAGE_OPTIONAL_HEADER;
   sectionHeader:array of IMAGE_SECTION_HEADER_E;
   funcExportArr:array of FuncExportTable;
   temp : NTFuncID;
   opcodeArr : ByteArray; //API Entry First X number Bytes OpCodes
   mpointer : Integer;
   count : Integer;
   tempAddr : DWORD;
   tempAddrJmp : DWORD;
   tempAddrJmpX:DWORD;
   tempAddrJmpXX:DWORD;

   tempPath : array [0..MAX_PATH-1] of AnsiChar;
   hFile : THandle;
   fileSize : Integer;
  i: Integer;

begin
   if g_NTSSDT_Id  = nil then
   begin
      g_NTSSDT_Id := TList.Create;
   end;

   for modIndex := 1 to 3 do
   begin
      case modIndex of
         1: modFile := 'ntdll.dll';
         2: modFile := 'use32.dll';
         3: modFile := 'gdi32.dll';
      end;
      temp.dllIndex := modIndex;
      modHandle := Module_GetModuleHandle(modFile);

      GetTempPath(MAX_PATH,tempPath);
      GetTempFileNameA(tempPath,'*',0,PChar(tempFile));
      GetSystemDirectory(tempPath,MAX_PATH);
      modFile := StrPas(tempPath) + '\' + modFile;
      CopyFile(PChar(modFile),PChar(tempFile),False);

      hFile := FileOpen(tempFile,fmOpenRead);
      fileSize := GetFileSize(hFile,nil);
      FileRead(hFile,modData,fileSize);
      DeleteFile(tempFile);

      if (GetModuleImage(modData,optHeader,False) and (optHeader.Magic = $10B)) then
      begin
        r3handle := VirtualAllocEx(
        THandle(-1),
        nil,
        optHeader.SizeOfImage,
        4096 or 8192,
        64);
        Mem_WriteMem_(
        r3handle^,
        modData,
        optHeader.SizeOfHeaders);
        for i := 0 to GetModuleSection(modData,sectionHeader,nil) - 1 do//Copy all sectionData from Dll
        begin
           Mem_WriteMem_(
           ADD__(r3handle^,sectionHeader[i].VirtualAddress),
           ADD__(modData,sectionHeader[i].PointerToRawData),
           sectionHeader[i].SizeOfRawData);
        end;
        RetifyReloc(r3handle^,modHandle);

        
        for i := 0 to GetModuleExport(r3handle^,funcExportArr) - 1 do
        begin
           if modIndex = 1 then//一级
           begin
             if LeftStr(funcExportArr[i].ApiName,2) <> 'Zw' then//Jmp if not Zw prefix
                Continue;
           end;
           Mem_ReadBytes_(funcExportArr[i].ApiEntry,opcodeArr,32);
           if (opcodeArr[0] = 184) or (opcodeArr[0] = 255) then //
           begin
              temp.APIName := funcExportArr[i].ApiName;
              temp.RETN_XX := 0;
              temp.SSDT_ID := ReadInteger(opcodeArr,1);
              if (g_WindowsVersoin.dwMajorVersion = 5) and (g_WindowsVersoin.dwMinorVersion = 0) then//' Windows 2000
              begin
                 if (opcodeArr[9] = 205) and (opcodeArr[10] = 46) then // INT 2E
                 begin
                    if opcodeArr[11] = 194 then// C2=194 C3=195
                    begin
                       temp.RETN_XX := ReadWord(opcodeArr,12);
                       g_NTSSDT_Id.Add(@temp);
                       Continue;
                    end;
                    if opcodeArr[11] = 195 then
                    begin
                       g_NTSSDT_Id.Add(@temp);
                       Continue;
                    end;
                    if opcodeArr[11] = 233 then
                    begin
                       mpointer := ADD__(mpointer,16);
                       mpointer := ADD__(mpointer,ReadWord(opcodeArr,12));
                       Mem_ReadBytes_(mpointer,opcodeArr,32);//C2 0C00         RETN 0C             JMP to Here
                       if opcodeArr[0] = 194 then//C2=194 C3=195
                       begin
                         temp.RETN_XX := ReadWord(opcodeArr,1);
                         g_NTSSDT_Id.Add(@temp);
                         Continue;
                       end;
                       if opcodeArr[0] = 195 then
                       begin
                         g_NTSSDT_Id.Add(@temp);
                         Continue;
                       end;
                    end;
                 end;
              end;
              if g_WindowsVersoin.isWin64 then
              begin

              end;

              if opcodeArr[5] = 232 then//' WIN8.32
              begin
                 mpointer := ReadInteger(opcodeArr,6);
                 if (mpointer > 0) and (mpointer < 10) then //11+API Pointer + 2 = 15(sysenter)
                 begin
                     mpointer := ADD__(mpointer,13);
                     if 15 = opcodeArr[mpointer] then
                     begin
                        if opcodeArr[10] = 194 then
                        begin
                          temp.RETN_XX := ReadWord(opcodeArr,11);
                          g_NTSSDT_Id.Add(@temp);
                          Continue;
                        end;
                        if opcodeArr[10] = 195 then
                        begin
                          g_NTSSDT_Id.Add(@temp);
                          Continue;
                        end;
                     end;
                 end;

              end;

              if (opcodeArr[5] = 186) and (opcodeArr[10] = 255) then//XP.SP2 2003 Vista Win7
              begin
                 if (opcodeArr[11] = 18) or (opcodeArr[11] = 210) then
                 begin
                    if opcodeArr[12] = 194 then// C2=194 C3=195
                     begin
                        temp.RETN_XX := ReadWord(opcodeArr,13);
                        g_NTSSDT_Id.Add(@temp);
                        Continue;
                     end;
                 end;
                 if opcodeArr[12] = 195 then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;
           end;
        end;

        if g_WindowsVersoin.isWin64 then
        begin
           temp.ECX := 0;
           if (g_WindowsVersoin.dwMajorVersion = 10) and (g_WindowsVersoin.dwMinorVersion = 0) then
           begin
              if (opcodeArr[5] = 186) and (opcodeArr[10] = 255) and (opcodeArr[11] = 210) then//Win10.x64
              begin
                 if opcodeArr[12] = 194 then//temp.ECX = 0
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,13);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[12] = 195 then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;

              if (opcodeArr[5] = 186) and (opcodeArr[10] <> 255) and (opcodeArr[11] <> 210) then//Win10.x64
              begin
                 if funcExportArr[i].ApiName = 'ZwQueryInformationProcess' then
                 begin
                    if (opcodeArr[24] = 194) or (opcodeArr[28] = 194) then// temp.ECX = 0
                    begin
                       if (opcodeArr[24] = 194) then
                          temp.RETN_XX := ReadWord(opcodeArr,25);
                       if opcodeArr[28] = 194 then
                         temp.RETN_XX := ReadWord(opcodeArr,29);
                       g_NTSSDT_Id.Add(@temp);
                       Continue;
                    end;
                    if (opcodeArr[24] = 195) or (opcodeArr[28] = 195) then
                     begin
                        g_NTSSDT_Id.Add(@temp);
                        Continue;
                     end;
                 end;
              end;

              if opcodeArr[0] = 255 then
              begin
                Mem_ReadInteger_(funcExportArr[i].ApiEntry + 2,tempAddrJmp);
                Mem_ReadInteger_(tempAddrJmpX,tempAddrJmpXX);
                Mem_ReadBytes_(tempAddrJmpXX,opcodeArr,32);
                temp.SSDT_ID := ReadInteger(opcodeArr,1);
                if opcodeArr[12] = 194 then//temp.ECX = 0
                begin
                  temp.RETN_XX := ReadWord(opcodeArr,13);
                  g_NTSSDT_Id.Add(@temp);
                  Continue;
                end;
                if opcodeArr[12] = 195 then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;
           end;
           if (opcodeArr[5] = 100) and (opcodeArr[6] = 255) and (opcodeArr[8] = 192) then//Win8.x64
              begin
                 if opcodeArr[12] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,13);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[12] = 195 then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;

              if (opcodeArr[5] = 51) and (opcodeArr[7] = 141) and (opcodeArr[11] = 100) then// xor       ecx, ecx
              begin
                 if (opcodeArr[18] = 195) or (opcodeArr[21] = 195) then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[18] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,19);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[21] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,22);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;

              if (opcodeArr[5] = 141) and (opcodeArr[9] = 51) and (opcodeArr[11] = 100) then//xor     ecx, ecx Unknown
              begin
                 if (opcodeArr[18] = 195) or (opcodeArr[21] = 195) then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[18] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,19);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[21] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,22);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;

              if (opcodeArr[5] = 185) and (opcodeArr[10] = 141) and (opcodeArr[14] = 100) then//mov       ecx,xxxxx
              begin
                 temp.ECX := ReadInteger(opcodeArr,6);
                 if (opcodeArr[21] = 195) or (opcodeArr[24] = 195) then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[21] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,22);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[24] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,25);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 temp.ECX := 0;
              end;

              if (opcodeArr[5] = 141) and (opcodeArr[9] = 185) and (opcodeArr[14] = 100) then//' mov       ecx, xxxxxxxx
              begin
                 temp.ECX := ReadInteger(opcodeArr,10);
                 if (opcodeArr[21] = 195) or (opcodeArr[24] = 195) then
                 begin
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[22] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,22);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
                 if opcodeArr[24] = 194 then
                 begin
                    temp.RETN_XX := ReadWord(opcodeArr,25);
                    g_NTSSDT_Id.Add(@temp);
                    Continue;
                 end;
              end;
        end;
      end;
   end;
end;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants