Membuat proses apa pun bekerja dengan NTFS transaksional: langkah pertama saya untuk membuat kotak pasir untuk Windows

TransactionMaster Ada modul di kernel Windows yang bertanggung jawab untuk mendukung pengelompokan operasi file ke dalam beberapa entitas yang disebut transaksi . Tindakan pada entitas ini terisolasi dan atom: tindakan itu dapat diterapkan dengan menjadikannya permanen, atau dibatalkan . Sangat nyaman saat menginstal program, setuju? Kami selalu berpindah dari satu negara yang disepakati ke negara lain, dan jika terjadi kesalahan, semua perubahan dibatalkan.


Karena saya belajar tentang dukungan fungsi seperti itu, saya selalu ingin melihat dunia dari dalam transaksi ini. Dan Anda tahu apa: Saya menemukan metode sederhana dan benar-benar luar biasa untuk membuat proses apa pun bekerja di dalam transaksi file, tetapi margin bukunya terlalu sempit untuknya . Dalam kebanyakan kasus, ini bahkan tidak memerlukan hak administratif.


Mari kita cari tahu cara kerjanya, bereksperimen dengan program saya, dan memahami apa itu kotak pasir.


Repositori


Bagi mereka yang ingin mencoba: TransactionMaster on GitHub .


Teori


Dukungan untuk transaksional NTFS, atau TxF , muncul di Windows Vista, dan memungkinkan untuk menyederhanakan secara signifikan kode yang bertanggung jawab untuk memulihkan dari kesalahan selama proses pembaruan perangkat lunak dan OS itu sendiri. Bahkan, tugas restorasi dipindahkan ke kernel sistem operasi, yang mulai menerapkan semantik ACID penuh untuk mengajukan operasi - tanyakan saja.


Untuk mendukung teknologi ini, fungsi API baru ditambahkan yang menggandakan fungsi yang ada, menambahkan satu parameter baru - transaksi. Transaksi itu sendiri telah menjadi salah satu dari banyak objek kernel di OS, bersama dengan file, proses, dan objek sinkronisasi. Dalam kasus paling sederhana, urutan tindakan ketika bekerja dengan transaksi terdiri dalam membuat objek transaksi dengan memanggil CreateTransaction , bekerja dengan file (menggunakan fungsi seperti CreateFileTransacted , MoveFileTransacted , DeleteFileTransacted dan sejenisnya), dan menerapkan / memutar kembali transaksi menggunakan CommitTransaction / RollbackTransaction .


Sekarang mari kita lihat arsitektur dari fitur-fitur ini. Kita tahu bahwa lapisan API yang terdokumentasi, dari perpustakaan seperti kernel32.dll , tidak secara langsung mentransfer kontrol ke kernel OS, tetapi mengacu pada lapisan abstraksi yang mendasari dalam mode pengguna - ntdll.dll , yang sudah melakukan panggilan sistem. Dan di sini kejutan menunggu kita: tidak ada duplikasi fungsi untuk bekerja dengan file dalam konteks transaksi di ntdll , seperti di kernel .


Lapisan API

Namun demikian, prototipe fungsi-fungsi ini dari API Asli belum berubah sejak dahulu kala, yang berarti mereka akan belajar dari tempat lain dalam konteks transaksi yang akan dilakukan operasi. Tapi dari mana? Jawabannya adalah bahwa setiap utas memiliki bidang khusus di mana pegangan transaksi saat ini disimpan. Area memori di mana ia berada disebut TEB , blok lingkungan aliran. Dari hal-hal yang diketahui, kode kesalahan terakhir dan pengidentifikasi aliran juga disimpan di sana.


Dengan demikian, fungsi dengan suffix *Transacted mengatur bidang transaksi saat ini, memanggil fungsi serupa tanpa suffix, dan kemudian mengembalikan nilai sebelumnya. Mereka melakukan ini menggunakan sepasang RtlSetCurrentTransaction / RtlSetCurrentTransaction dari ntdll . Kode fungsi itu sendiri sangat mudah, dengan pengecualian pada kasus dengan WoW64 , yang akan dibahas di bawah.


Apa artinya semua ini bagi kita? Dengan mengubah variabel dalam memori proses, kita dapat mengontrol dalam konteks transaksi mana ia bekerja dengan sistem file. Anda tidak perlu mengatur jebakan dan panggilan fungsi intersepsi, cukup kirimkan deskriptor transaksi ke proses target dan koreksi beberapa byte dalam memorinya untuk masing-masing utas. Kedengarannya dasar, mari kita lakukan!


Perangkap


Eksperimen pertama menunjukkan bahwa idenya bisa diterapkan: Far Manager , yang saya gunakan sebagai pengganti Windows Explorer, dengan sempurna selamat dari penggantian transaksi dengan cepat, dan memungkinkan Anda untuk melihat dunia dalam konteksnya. Tetapi ada juga program yang terus-menerus membuat utas baru untuk operasi file. Dan dalam skenario asli, ini adalah celah, karena tidak nyaman untuk melacak pembuatan utas di proses lain (belum lagi fakta bahwa "keterlambatan" sangat penting di sini). Contoh aplikasi dari kelas kedua adalah WinFile yang baru-baru ini porting.


Pelacakan DLL


Untungnya, pelacakan sinkron pembuatan utas dan konfigurasi transaksi berikutnya untuk mereka benar-benar dasar dari dalam proses target. Cukup dengan menanamkan DLL di dalamnya, dan pemuat modul akan memanggil titik masuknya dengan parameter DLL_THREAD_ATTACH setiap kali ketika membuat utas baru. Dengan menerapkan fungsi ini, saya memperbaiki kompatibilitas dengan selusin program lagi.


* Secara teknis, panggilan tidak selalu berfungsi, dan perilaku ini terkadang dapat diamati di antarmuka program saya. Sebagian besar, pengecualian adalah utas dari kumpulan kerja pemuat modul itu sendiri. Masalahnya adalah pustaka DLL diberitahu di bawah kunci bootloader, dan ini berarti: Anda tidak dapat memuat modul baru saat ini. Dan thread loader, seperti yang Anda tahu, melakukan hal itu, memparalelkan akses ke sistem file. Pengecualian diberikan untuk kasus-kasus seperti ini: jika Anda menentukan THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH sebagai bendera saat memanggil NtCreateThreadEx , Anda dapat menghindari menambahkan utas baru ke DLL yang ada, dan, masing-masing, kebuntuan. Tentang inilah yang terjadi di sini.


Luncurkan Explorer


Masih ada kategori ketiga, program terakhir yang masih macet ketika mencoba membuatnya bekerja di dalam suatu transaksi. Salah satu dari program ini adalah Windows Explorer. Saya tidak dapat mendiagnosis masalah secara akurat, tetapi aplikasi ini rumit, dan perpindahan panas di dalam transaksi tidak terlalu berpengaruh. Mungkin alasannya adalah karena memiliki banyak deskriptor file terbuka, beberapa di antaranya tidak berlaku lagi dalam konteks baru. Atau mungkin itu sesuatu yang lain. Dalam situasi seperti itu, memulai kembali proses akan membantu, sehingga berfungsi sejak awal dalam transaksi. Maka tidak ada inkonsistensi yang muncul.


Dan oleh karena itu, saya menambahkan kemampuan untuk memulai proses baru pada program, yang untuknya transaksi dan pelacakan arus baru dikonfigurasikan sebelum mencapai titik masuk, sementara prosesnya ditangguhkan. Dan Anda tahu, itu berhasil! Benar, karena Explorer secara aktif menggunakan objek COM di luar proses, pratinjau rusak saat memindahkan file. Tetapi sebaliknya, semuanya stabil.


Ada apa dengan WoW64?


Subsistem ini untuk meluncurkan program 32-bit pada sistem 64-bit adalah alat yang sangat nyaman, tetapi kebutuhan untuk mempertimbangkan fitur-fiturnya sering mempersulit pemrograman sistem. Saya sebutkan di atas bahwa perilaku Rtl[Get/Set]CurrentTransaction sangat berbeda dalam hal proses serupa. Alasan untuk ini terletak pada kenyataan bahwa utas dalam proses WoW64 memiliki sebanyak dua blok lingkungan. Mereka memiliki ukuran pointer yang berbeda, dan diinginkan untuk mempertahankannya dalam keadaan yang konsisten, meskipun, dalam kasus transaksi, TEB 64-bit lebih diutamakan. Ketika kami melakukan transaksi dari jarak jauh, kami harus mereproduksi perilaku fungsi-fungsi ini. Ini tidak sulit, tetapi Anda tidak boleh melupakannya, dan detailnya dapat ditemukan di sini . Akhirnya, proses WoW64 memerlukan salinan tambahan 32-bit dari DLL pelacakan kami.


Masalah yang belum terselesaikan


Terpaksa bersedih - skenario paling awal yang muncul di pikiran, yaitu peluncuran program installer dalam mode ini - belum operasional. Pertama, itu tidak dikonfigurasi untuk menangkap proses anak dalam transaksi yang sama. Ada beberapa solusi di sini, saya sedang mengerjakan ini. Tetapi jika aplikasi menciptakan beberapa proses, masih terlalu dini untuk menggunakannya dalam kombinasi dengan transaksi.


Kedua, kasus dengan file yang dapat dieksekusi yang tidak ada di luar transaksi patut mendapat perhatian khusus. Saya ingat ada beberapa jenis virus yang menipu antivirus yang naif dengan cara ini: itu dibongkar menjadi suatu transaksi, diluncurkan sendiri, dan kemudian memutar kembali transaksi. Ada proses, tetapi tidak ada file yang dapat dieksekusi. Antivirus dapat memutuskan bahwa tidak ada yang dipindai, dan mengabaikan ancaman. Kita juga perlu mengerjakan solusi kreatif, karena, untuk beberapa alasan, NtCreateUserProcess (dan, karenanya, CreateProcess ) mengabaikan transaksi saat ini. Tentu saja, NtCreateProcessEx selalu tetap, tetapi banyak kerepotan diharapkan untuk memperbaiki masalah kompatibilitas. Saya akan memikirkan sesuatu.


Dan di mana kotak pasirnya?


Lihatlah gambarnya. Di sini, tiga program berbeda memperlihatkan isi folder yang sama dari tiga transaksi berbeda. Keren kan?


Pandangan dari dalam transaksi

Namun, program saya sama sekali bukan kotak pasir, tidak memiliki satu detail penting - perbatasan keamanan . Tentu saja, ini tidak mencegah beberapa perusahaan menjual kerajinan serupa dengan kedok kotak pasir penuh, memalukan bagi mereka, apa yang bisa saya katakan. Dan, terlepas dari kenyataan bahwa ini tampaknya benar-benar mustahil, bagaimana kita dapat mencegah suatu program mengubah variabel di memori kita bahkan jika kita bahkan seorang debugger? - Saya punya satu trik yang menyenangkan di toko yang memungkinkan saya untuk menyelesaikan apa yang saya mulai dan membuat kotak pasir pertama yang saya tahu, yang tidak akan memerlukan driver, tetapi akan memvirtualisasi sistem file. Sampai saat itu, tunggu pembaruan, gunakan Sandboxie dan bereksperimen dengan teknologi AppContainer . Terima kasih atas perhatian anda


Repositori proyek di GitHub: TransactionMaster .
Artikel yang sama dalam bahasa Inggris .

Source: https://habr.com/ru/post/id485784/


All Articles