NtUtils adalah kerangka kerja untuk pemrograman sistem Windows di Delphi yang menyediakan serangkaian fungsi dengan penanganan kesalahan dan integrasi bahasa yang lebih baik daripada header Winapi/Ntapi biasa, dikombinasikan dengan cuplikan kode yang sering digunakan dan tipe data cerdas.
Anda dapat menemukan beberapa contoh kode di repositori khusus .
Perpustakaan memiliki struktur berlapis dengan total tiga lapisan:
Winapi.*.pas
dan perpustakaan Ntapi.*.pas
di program Anda; meskipun demikian, hal ini mungkin memerlukan penentuan awalan namespace secara eksplisit jika ada nama yang bertentangan.System.SysUtils
, System.Rtti
, dan System.Generics.Collections
.Oleh karena itu, semua yang Anda butuhkan sudah disertakan dengan Delphi versi gratis terbaru. Sebagai bonus, kompilasi aplikasi konsol tanpa RTTI (alias refleksi) menghasilkan executable yang sangat kecil. Lihat contoh untuk lebih jelasnya.
Karena memasukkan setiap file dari perpustakaan ke dalam proyek Anda biasanya berlebihan, Anda dapat mengkonfigurasi Delphi untuk penemuan otomatis file. Dengan cara ini, Anda dapat menentukan unit di bagian uses
, dan Delphi akan secara otomatis memasukkannya dan dependensinya ke dalam proyek. Untuk mengonfigurasi folder tempat Delphi melakukan pencarian, buka Project -> Options -> Building -> Delphi Compiler dan tambahkan baris berikut ke dalam Jalur Pencarian:
.NtUtilsLibrary
.NtUtilsLibraryHeaders
.NtUtilsLibraryNtUiLib
Jika nama folder atau lokasi berbeda untuk proyek Anda, Anda perlu menyesuaikan baris-baris ini.
Pustaka menunjukkan kegagalan pada pemanggil dengan mengembalikan nilai TNtxStatus yang gagal. TNtxStatus
(didefinisikan dalam NtUtils.pas) adalah struktur yang menyimpan kode kesalahan (kompatibel dengan NTSTATUS
, HRESULT
, dan Win32 Errors) ditambah metadata tentang sifat operasi yang dicoba, seperti lokasi kegagalan, stacktrace, dan detail lainnya seperti masker akses yang diharapkan/diminta untuk panggilan terbuka atau nilai kelas info untuk panggilan kueri/set. Untuk memeriksa apakah TNtxStatus
berhasil, gunakan metode IsSuccess
. Untuk mengakses atau mengatur kode kesalahan yang mendasarinya (tergantung pada jenisnya dan preferensi pemanggil) gunakan properti seperti Status
, HResult
, HResultAllowFalse
, Win32Error
, Win32ErrorOrSuccess
, IsHResult
, IsWin32
, dll.
Jika Anda lebih suka menggunakan pengecualian, Anda selalu dapat memanggil RaiseOnError()
pada TNtxStatus
tertentu. Perhatikan bahwa kecuali Anda benar-benar ingin menggunakan pengecualian tanpa mengimpor System.SysUtils
(yang mungkin), lebih baik menyertakan NtUiLib.Exceptions yang menghadirkan kelas pengecualian ENtError
khusus (berasal dari EOSError
bawaan).
NtUiLib.Errors melampirkan empat metode untuk merepresentasikan nilai TNtxStatus
sebagai string. Misalnya, jika kesalahan dengan nilai 0xC0000061
berasal dari upaya mengubah ID sesi token, metode ini akan mengembalikan informasi berikut:
Metode | String yang dikembalikan |
---|---|
Name | STATUS_PRIVILEGE_NOT_HELD |
Description | A required privilege is not held by the client |
Summary | Privilege Not Held |
ToString | NtSetInformationToken returned STATUS_PRIVILEGE_NOT_HELD |
Jika Anda ingin melangkah lebih jauh dan menampilkan kotak pesan cantik kepada pengguna, NtUiLib.Errors.Dialog menawarkan ShowNtxStatus()
. Selain itu, menyertakan NtUiLib.Exceptions.Dialog akan memberikan dukungan refleksi yang diperlukan dan memperkaya dialog lebih jauh. Berikut adalah contoh tampilannya:
TNtxStatus
mendukung pengambilan jejak tumpukan (dinonaktifkan secara default). Untuk mengaktifkannya, atur NtUtils.CaptureStackTraces
ke True. Ingatlah bahwa menampilkan pelacakan tumpukan dengan cara yang bermakna memerlukan konfigurasi pembuatan simbol debug untuk file yang dapat dieksekusi. Sayangnya, Delphi hanya dapat mengeluarkan file .map
(dikonfigurasi melalui Project -> Options -> Building -> Delphi Compiler -> Linking -> Map File) yang umumnya tidak cukup. Anda memerlukan alat map2dbg pihak ketiga untuk mengonversinya menjadi file .dbg
, sehingga API simbol dapat memahaminya. Meskipun file .dbg
mungkin cukup, lebih baik memprosesnya lebih jauh dengan mengonversinya menjadi .pdb
modern melalui cv2pdb .
Untuk menghasilkan simbol debug secara otomatis, tambahkan peristiwa pasca-pembuatan berikut ke dalam proyek Anda:
map2dbg.exe $(OUTPUTPATH)
cv2pdb64.exe -n -s. -p$(OUTPUTNAME).pdb $(OUTPUTPATH)
Delphi tidak menyertakan pengumpul sampah, jadi hanya beberapa tipe yang dikelola langsung: catatan, string, array dinamis, dan antarmuka. Kelas dan pointer, di sisi lain, memerlukan pembersihan eksplisit yang (dalam bentuk amannya) memerlukan penggunaan blok try-finally dan, oleh karena itu, mempersulit program secara signifikan. Untuk mengatasi masalah ini, perpustakaan menyertakan fasilitas manajemen seumur hidup otomatis untuk memori dan sumber daya lainnya, yang diterapkan di DelphiUtils.AutoObjects. Dengan menggunakan tipe dari modul ini, kami menginstruksikan kompiler untuk secara otomatis menghasilkan kode aman pengecualian untuk menghitung referensi dan secara otomatis melepaskan objek dalam epilog fungsi. Modul ini mendefinisikan beberapa antarmuka untuk berbagai jenis sumber daya yang mungkin memerlukan pembersihan. Ini memperkenalkan hierarki berikut:
grafik LR;
subgraf id1[Sumber daya apa pun]
IDapat Dilepas Secara Otomatis
akhir
subgraf id2[Nilai Pegangan]
Saya menangani
akhir
subgraf id3[Kelas Delphi]
IObjekOtomatis[IObjekOtomatis<T>]
akhir
subgraf id4[Penunjuk]
IAutoPointer[IAutoPointer<P>]
akhir
subgraf id5[Wilayah memori]
IMemori[IMemori<P>]
akhir
IAutoReleasable --> IHandle;
IAutoReleasable --> IAutoObject;
IAutoReleasable --> IAutoPointer;
IAutoPointer --> IMemori;
IAutoReleasable
adalah tipe dasar untuk semua sumber daya yang memerlukan tindakan pembersihan (otomatis). IHandle
berfungsi sebagai pembungkus sumber daya yang ditentukan oleh nilai THandle. IAutoObject<T>
adalah pembungkus generik untuk melepaskan kelas Delphi secara otomatis (yaitu, apa pun yang berasal dari TObject). IAutoPointer<P>
mendefinisikan antarmuka serupa untuk melepaskan pointer yang dialokasikan secara dinamis (di mana ukuran wilayah tidak relevan). IMemory<P>
menyediakan pembungkus untuk wilayah memori dengan ukuran yang diketahui yang dapat diakses melalui penunjuk yang diketik, seperti catatan kotak yang dikelola dan tidak dikelola.
Resep untuk menggunakan fasilitas ini adalah sebagai berikut:
Tentukan setiap variabel yang perlu mempertahankan kepemilikan (yang mungkin dimiliki bersama) atas suatu objek menggunakan salah satu antarmuka:
Gunakan pembantu Otomatis untuk mengalokasikan/menyalin/menangkap objek otomatis:
Bila perlu, gunakan casting sisi kiri yang membantu menghindari duplikasi informasi tipe dan dapat mempersingkat sintaksis.
Misalnya, berikut adalah kode aman untuk bekerja dengan TStringList menggunakan pendekatan klasik:
var
x: TStringList;
begin
x := TStringList.Create;
try
x.Add( ' Hi there ' );
x.SaveToFile( ' test.txt ' );
finally
x.Free;
end ;
end ;
Seperti yang dapat Anda bayangkan, menggunakan lebih banyak objek dalam fungsi ini akan meningkatkan kompleksitasnya secara signifikan dan non-linear. Alternatifnya, berikut adalah kode setara yang menggunakan IAutoObject dan ditingkatkan skalanya dengan lebih baik:
uses
DelphiUtils.AutoObjects;
var
x: IAutoObject<TStringList>;
begin
x := Auto.From(TStringList.Create);
x.Self.Add( ' Hi there ' );
x.Self.SaveToFile( ' test.txt ' );
end ;
Kompiler mengeluarkan kode pembersihan yang diperlukan ke dalam epilog fungsi dan memastikannya dijalankan bahkan jika terjadi pengecualian. Selain itu, pendekatan ini memungkinkan mempertahankan kepemilikan bersama atas objek yang mendasarinya, yang memungkinkan Anda menyimpan referensi yang dapat bertahan lebih lama dari fungsi saat ini (dengan menangkapnya dalam fungsi anonim dan mengembalikannya, misalnya). Jika Anda tidak memerlukan fungsi ini dan ingin mempertahankan satu pemilik yang membebaskan objek saat fungsi keluar, Anda dapat menyederhanakan sintaksisnya lebih jauh lagi:
uses
NtUtils;
var
x: TStringList;
begin
x := Auto.From(TStringList.Create).Self;
x.Add( ' Hi there ' );
x.SaveToFile( ' test.txt ' );
end ;
Kode ini masih setara dengan kode awal. Secara internal, ini menciptakan variabel lokal tersembunyi yang menyimpan antarmuka dan kemudian melepaskan objeknya.
Saat bekerja dengan alokasi memori dinamis, akan lebih mudah jika menggunakan transmisi sisi kiri sebagai berikut:
var
x: IMemory<PByteArray>;
begin
IMemory(x) := Auto.AllocateDynamic( 100 );
x.Data[ 15 ] := 20 ;
end ;
Anda juga dapat membuat rekaman terkelola dalam kotak (dialokasikan di heap) yang memungkinkan berbagi tipe nilai seolah-olah itu adalah tipe referensi. Perhatikan bahwa mereka juga dapat menyertakan bidang terkelola seperti string Delphi dan array dinamis - kompiler mengeluarkan kode untuk melepaskannya secara otomatis:
type
TMyRecord = record
MyInteger: Integer;
MyArray: TArray<Integer>;
end ;
PMyRecord = ^TMyRecord;
var
x: IMemory<PMyRecord>;
begin
IMemory(x) := Auto.Allocate<TMyRecord>;
x.Data.MyInteger := 42 ;
x.Data.MyArray := [ 1 , 2 , 3 ];
end ;
Karena Delphi menggunakan penghitungan referensi, kebocoran memori masih mungkin terjadi jika dua objek memiliki ketergantungan melingkar. Anda dapat mencegah hal ini terjadi dengan menggunakan referensi yang lemah . Referensi tersebut tidak dihitung untuk memperpanjang masa pakai, dan variabel yang menyimpannya secara otomatis menjadi nihil ketika objek target dihancurkan. Anda perlu mengupgrade referensi yang lemah ke referensi yang kuat sebelum Anda dapat menggunakannya. Lihat Lemah<I> dari DelphiUtils.AutoObjects untuk lebih jelasnya.
Ada beberapa alias yang tersedia untuk tipe penunjuk ukuran variabel yang umum digunakan, berikut beberapa contohnya:
Pegangan menggunakan tipe IHandle (lihat DelphiUtils.AutoObjects), yang mengikuti logika yang dibahas di atas, sehingga tidak memerlukan penutupan eksplisit. Anda juga dapat menemukan beberapa alias untuk IHandle (IScmHandle, ISamHandle, ILsaHandle, dll.), yang tersedia hanya demi keterbacaan kode.
Jika Anda perlu mengambil kepemilikan nilai pegangan ke dalam IHandle, Anda memerlukan kelas yang mengimplementasikan antarmuka ini dan mengetahui cara melepaskan sumber daya yang mendasarinya. Misalnya, NtUtils.Objects mendefinisikan kelas tersebut untuk objek kernel yang memerlukan pemanggilan NtClose
. Itu juga melampirkan metode pembantu ke Auto
, memungkinkan menangkap pegangan kernel berdasarkan nilai melalui Auto.CaptureHandle(...)
. Untuk membuat IHandle yang bukan milik, gunakan Auto.RefHandle(...)
.
Nama record, kelas, dan enumerasi dimulai dengan T
dan menggunakan CamelCase (contoh: TTokenStatistics
). Penunjuk ke rekaman atau tipe nilai lainnya dimulai dengan P
(contoh: PTokenStatistics
). Nama antarmuka dimulai dengan I
(contoh: ISid
). Konstanta menggunakan ALL_CAPITALS. Semua definisi dari lapisan header yang memiliki nama resmi yang diketahui (seperti tipe yang ditentukan dalam Windows SDK) ditandai dengan atribut SDKName
yang menentukan nama ini.
Sebagian besar fungsi menggunakan konvensi nama berikut: awalan subsistem dengan x di akhir (Ntx, Ldrx, Lsax, Samx, Scmx, Wsx, Usrx, ...) + Action + Target/Object type/etc. Nama fungsi juga menggunakan CamelCase.
Perpustakaan ini menargetkan Windows 7 atau lebih tinggi, baik edisi 32-bit maupun 64-bit. Meskipun demikian, beberapa fungsi mungkin hanya tersedia pada versi 64-bit terbaru Windows 11. Beberapa contohnya adalah AppContainers dan ntdll syscall unhooking. Jika fungsi perpustakaan bergantung pada API yang mungkin tidak ada di Windows 7, fungsi tersebut akan menggunakan impor tertunda dan memeriksa ketersediaan saat runtime.
Delphi hadir dengan sistem refleksi kaya yang digunakan perpustakaan dalam lapisan NtUiLib . Sebagian besar tipe yang ditentukan di lapisan Header dihiasi dengan atribut khusus (lihat DelphiApi.Reflection) untuk mencapainya. Dekorasi ini memancarkan metadata berguna yang membantu perpustakaan merepresentasikan tipe data kompleks secara tepat (seperti PEB, TEB, USER_SHARED_DATA) saat runtime dan menghasilkan laporan menakjubkan dengan satu baris kode.
Berikut adalah contoh representasi TSecurityLogonSessionData
dari Ntapi.NtSecApi menggunakan NtUiLib.Reflection.Types:
Berikut ikhtisar tujuan berbagai modul.
Satuan pendukung | Keterangan |
---|---|
DelphiUtils.AutoObjects | Manajemen seumur hidup sumber daya otomatis |
DelphiUtils.AutoEvents | Acara anonim multi-pelanggan |
DelphiUtils.Array | Pembantu TArray |
DelphiUtils.Daftar | Daftar tertaut ganda genetik primitif |
DelphiUtils.Async | Definisi dukungan I/O asinkron |
DelphiUtils.Impor Eksternal | Pembantu IAT kata kunci eksternal Delphi |
DelphiUtils.RangeChecks | Pembantu pengecekan jangkauan |
NtUtils | Jenis perpustakaan umum |
NtUtils.SysUtils | Manipulasi string |
NtUtils.Kesalahan | Konversi kode kesalahan |
NtUiLib.Kesalahan | Pencarian nama kode kesalahan |
NtUiLib.Pengecualian | Integrasi pengecualian SysUtils |
DelphiUiLib.String | Pretifikasi tali |
DelphiUiLib.Refleksi | Dukungan RTTI dasar |
DelphiUiLib.Refleksi.Numerik | Representasi RTTI dari tipe numerik |
DelphiUiLib.Refleksi.Catatan | Representasi RTTI dari tipe rekaman |
DelphiUiLib.Refleksi.String | Prettifikasi string RTTI |
NtUiLib.Refleksi.Jenis | Representasi RTTI untuk tipe umum |
NtUiLib.Konsol | Pembantu I/O konsol |
NtUiLib.TaskDialog | GUI berbasis TaskDialog |
NtUiLib.Kesalahan.Dialog | Dialog kesalahan GUI |
NtUiLib.Pengecualian.Dialog | Dialog pengecualian GUI |
Satuan sistem | Keterangan |
---|---|
NtUtils.ActCtx | Konteks aktivasi |
NtUtils.AntiHooking | Melepas kaitan dan mengarahkan syscall |
NtUtils.Com | COM, IDispatch, WinRT |
NtUtils.Csr | Pendaftaran CSRSS/SxS |
NtUtils.DbgHelp | DbgHelp dan simbol debug |
NtUtils.Debug | Men-debug objek |
NtUtils.Dism | API DISM |
NtUtils.Lingkungan | Variabel lingkungan |
NtUtils.Lingkungan.Pengguna | Variabel lingkungan pengguna |
NtUtils.Lingkungan.Jarak Jauh | Variabel lingkungan dari proses lainnya |
NtUtils.File | Nama file Win32/NT |
NtUtils.Files.Buka | File dan pipa dibuka/dibuat |
NtUtils.Files.Operasi | Operasi berkas |
NtUtils.Files.Direktori | Pencacahan direktori file |
NtUtils.Files.FltMgr | Filter Manajer API |
NtUtils.Files.Mup | Beberapa Penyedia UNC |
NtUtils.Files.Volume | Operasi volume |
NtUtils.Files.Kontrol | operasi FSCTL |
NtUtils.ImageHlp | penguraian PE |
NtUtils.ImageHlp.Syscalls | Pengambilan nomor Syscall |
NtUtils.ImageHlp.DbgHelp | Simbol publik tanpa DbgHelp |
NtUtils.Pekerjaan | Objek pekerjaan dan silo |
NtUtils.Jobs.Remote | Kueri objek pekerjaan lintas proses |
NtUtils.Ldr | Rutinitas dan parsing LDR |
NtUtils.Lsa | kebijakan LSA |
NtUtils.Lsa.Audit | Kebijakan audit |
NtUtils.Lsa.Sid | pencarian SID |
NtUtils.Lsa.Logon | Sesi masuk |
NtUtils.Manifest | Pembuat manifes Fusion/SxS |
NtUtils.Memori | Operasi memori |
NtUtils.MiniDumps | Penguraian format minidump |
NtUtils.Objek | Objek dan pegangan kernel |
NtUtils.Objects.Snapshot | Tangani pengambilan gambar |
NtUtils.Objects.Namespace | Ruang nama objek NT |
NtUtils.Objects.Remote | Operasi penanganan lintas proses |
NtUtils.Objects.Bandingkan | Tangani perbandingan |
NtUtils.Paket | Paket aplikasi & kelompok paket |
NtUtils.Paket.SRCache | Cache repositori negara |
NtUtils.Paket.WinRT | Info paket berbasis WinRT |
NtUtils.Kekuatan | Fungsi yang berhubungan dengan kekuasaan |
NtUtils.Proses | Memproses objek |
NtUtils.Proses.Info | Memproses kueri/mengatur info |
NtUtils.Proses.Info.Remote | Proses kueri/pengaturan melalui injeksi kode |
NtUtils.Proses.Modul | Pencacahan LDR lintas proses |
NtUtils.Proses.Snapshot | Proses enumerasi |
NtUtils.Proses.Buat | Definisi umum pembuatan proses |
NtUtils.Proses.Buat.Win32 | Metode pembuatan proses Win32 |
NtUtils.Proses.Buat.Shell | Metode pembuatan proses shell |
NtUtils.Proses.Buat.Asli | NtCreateUserProcess dan rekannya. |
NtUtils.Proses.Buat.Manual | NtCreateProcessEx |
NtUtils.Processes.Create.Com | Pembuatan proses berbasis COM |
NtUtils.Proses.Buat.Csr | Proses pembuatan melalui SbApiPort |
NtUtils.Proses.Buat.Paket | Aktivasi aplikasi |
NtUtils.Proses.Buat.Remote | Proses pembuatan melalui injeksi kode |
NtUtils.Proses.Buat.Klon | Proses kloning |
NtUtils.Profiles | Profil Pengguna & AppContainer |
NtUtils.Registry | Kunci registri |
NtUtils.Registry.Offline | Manipulasi sarang offline |
NtUtils.Registry.VReg | Virtualisasi registri berbasis silo |
NtUtils.Sam | basis data SAM |
NtUtils.Bagian | Objek proyeksi bagian/memori |
NtUtils.Keamanan | Deskriptor keamanan |
NtUtils.Keamanan.Acl | ACL dan ACE |
NtUtils.Keamanan.Sid | SID |
NtUtils.Security.AppContainer | AppContainer & SID kemampuan |
NtUtils.Shellcode | Injeksi kode |
NtUtils.Shellcode.Dll | injeksi DLL |
NtUtils.Shellcode.Exe | injeksi EXE |
NtUtils.Svc | layanan SCM |
NtUtils.Svc.SingleTaskSvc | Implementasi layanan |
NtUtils.Sinkronisasi | Sinkronisasi primitif |
NtUtils.Sistem | Informasi sistem |
NtUtils.Penjadwal Tugas | Penjadwal tugas |
NtUtils.Utas | Objek benang |
NtUtils.Token.Info | Kueri thread/info setel |
NtUtils.Threads.Pekerja | Pekerja thread (kumpulan thread) |
NtUtils.Token | Objek token |
NtUtils.Tokens.Meniru identitas | Peniruan identitas |
NtUtils.Token.Logon | Masuk pengguna & S4U |
NtUtils.Token.AppModel | Kebijakan Token AppModel |
NtUtils.Transaksi | Objek Transaksi (TmTx). |
NtUtils.Transaksi.Remote | Memaksa proses menjadi transaksi |
NtUtils.UserManager | API layanan Manajer Pengguna (Umgr). |
NtUtils.Wim | API Pencitraan Windows (*.wim). |
NtUtils.WinSafer | API yang lebih aman |
NtUtils.WinStation | API server terminal |
NtUtils.WinUser | API Pengguna32/GUI |
NtUtils.WinUser.WindowAffinity | Modifikasi afinitas jendela |
NtUtils.WinUser.WinstaLock | Mengunci & membuka kunci stasiun jendela |
NtUtils.XmlLite | Penguraian & pembuatan XML melalui XmlLite |
NtUiLib.Penyelesaian Otomatis | Pelengkapan otomatis untuk kontrol edit |
NtUiLib.AutoCompletion.Namespace | Penyelesaian otomatis namespace objek NT |
NtUiLib.PelengkapanOtomatis.Sid | Pelengkapan otomatis SID |
NtUiLib.PelengkapanOtomatis.Sid.Umum | Penyedia/pengenal nama SID sederhana |
NtUiLib.AutoCompletion.Sid.AppContainer | AppContainer & penyedia/pengenal SID paket |
Kemampuan NtUiLib.AutoCompletion.Sid | Kemampuan penyedia/pengenal SID |
NtUiLib.WinCred | Dialog kredensial |