Praktik terbaik kueri berburu tingkat lanjut

Dapatkan hasil lebih cepat dan hindari batas waktu saat menjalankan kueri kompleks dengan mengoptimalkan kueri Anda. Untuk panduan tentang meningkatkan performa kueri:

Memahami kuota sumber daya CPU

Bergantung pada ukurannya, setiap penyewa memiliki akses ke sejumlah sumber daya CPU yang dialokasikan untuk menjalankan kueri berburu tingkat lanjut. Untuk informasi terperinci tentang berbagai parameter penggunaan, baca tentang kuota perburuan tingkat lanjut dan parameter penggunaan.

Setelah menjalankan kueri, Anda dapat melihat waktu eksekusi dan penggunaan sumber dayanya (Rendah, Sedang, Tinggi). Tinggi menunjukkan bahwa kueri memerlukan lebih banyak sumber daya saat dijalankan dan dapat dioptimalkan untuk menghasilkan hasil dengan lebih efisien.

Cuplikan layar detail kueri di bawah tab Hasil di portal Microsoft Defender memperlihatkan waktu eksekusi dan penggunaan sumber daya.

Pelanggan yang menjalankan beberapa kueri secara teratur harus melacak konsumsi dan menerapkan panduan pengoptimalan dalam artikel ini untuk meminimalkan gangguan yang dihasilkan dari melebihi kuota atau parameter penggunaan.

Tips pengoptimalan umum

  • Perkirakan ukuran kueri baru—Jika Anda menduga bahwa kueri akan mengembalikan sekumpulan besar hasil, nilai terlebih dahulu dengan menggunakan operator count. Gunakan batas atau sinonimnya take untuk menghindari tataan hasil besar.

  • Terapkan filter lebih awal—Terapkan filter waktu dan filter lain untuk mengurangi himpunan data, terutama sebelum menggunakan fungsi transformasi dan penguraian, seperti substring(), replace(), trim(), toupper(), atau parse_json(). Dalam contoh di bawah ini, fungsi penguraian extractjson() digunakan setelah operator pemfilteran mengurangi jumlah rekaman.

    DeviceEvents
    | where Timestamp > ago(1d)
    | where ActionType == "UsbDriveMount"
    | where DeviceName == "user-desktop.domain.com"
    | extend DriveLetter = extractjson("$.DriveLetter", AdditionalFields)
    
  • Has beats contains—Untuk menghindari pencarian substring di dalam kata secara tidak perlu, gunakan operator has alih-alih contains. Pelajari tentang operator string

  • Batasi cakupan pencarian Anda—Hindari menjalankan kueri search atau union tanpa cakupan, karena kueri tersebut mencakup semua tabel dalam skema dan dapat melebihi batas ukuran kueri di lingkungan dengan banyak tabel. Gunakan search in untuk menentukan tabel yang ingin Anda cari. Misalnya, daripada search "email", gunakan search in (EmailEvents, EmailAttachmentInfo, IdentityInfo) "email".

  • Lihat di kolom tertentu—Lihat di kolom tertentu daripada menjalankan pencarian teks lengkap. Jangan gunakan * untuk memeriksa semua kolom.

  • Peka terhadap huruf besar/kecil agar lebih cepat—Pencarian yang peka terhadap huruf besar/kecil lebih spesifik dan umumnya lebih cepat. Nama operator string yang peka huruf besar/kecil, seperti has_cs dan contains_cs, umumnya diakhiri dengan _cs. Anda juga dapat menggunakan operator sama dengan yang peka terhadap huruf besar/kecil == sebagai pengganti =~.

  • Uraikan, jangan ekstrak—Jika memungkinkan, gunakan operator penguraian atau fungsi penguraian seperti parse_json(). matches regex Hindari operator string atau fungsi extract(), yang keduanya menggunakan ekspresi reguler. Gunakan ekspresi reguler hanya untuk skenario yang lebih kompleks. Baca selengkapnya tentang fungsi penguraian

  • Memfilter tabel, bukan ekspresi—Jangan memfilter kolom hasil perhitungan jika Anda dapat memfilter berdasarkan kolom tabel.

  • Tidak ada istilah tiga karakter—Hindari membandingkan atau memfilter menggunakan istilah dengan tiga karakter atau kurang. Istilah-istilah ini tidak diindeks dan mencocokkannya akan memerlukan lebih banyak sumber daya.

  • Proyeksikan secara selektif—Buat hasil lebih mudah dipahami dengan hanya memproyeksikan kolom yang Anda perlukan. Memproyeksikan kolom tertentu sebelum menjalankan gabungan atau operasi serupa juga membantu meningkatkan performa.

join Mengoptimalkan operator

Operator gabungan menggabungkan baris dari dua tabel dengan mencocokkan nilai dalam kolom tertentu. Terapkan tips ini untuk mengoptimalkan kueri yang menggunakan operator ini.

  • Tabel yang lebih kecil di sisi kiri Anda—Operator join mencocokkan record dalam tabel di sisi kiri pernyataan join Anda dengan record di sisi kanan. Dengan memiliki tabel yang lebih kecil di sebelah kiri, lebih sedikit rekaman yang perlu dicocokkan, sehingga mempercepat kueri.

    Dalam tabel di bawah ini, kami membatasi tabel sebelah kiri DeviceLogonEvents agar hanya mencakup tiga perangkat tertentu sebelum menggabungkannya dengan IdentityLogonEvents berdasarkan SID akun.

    DeviceLogonEvents
    | where DeviceName in ("device-1.domain.com", "device-2.domain.com", "device-3.domain.com")
    | where ActionType == "LogonFailed"
    | join
        (IdentityLogonEvents
        | where ActionType == "LogonFailed"
        | where Protocol == "Kerberos")
    on AccountSid
    
  • Gunakan varian inner join—Varian join default atau innerunique-join menghapus duplikasi baris dalam tabel kiri berdasarkan kunci join sebelum mengembalikan satu baris untuk setiap kecocokan dengan tabel kanan. Jika tabel kiri memiliki beberapa baris dengan nilai yang sama untuk kunci, baris tersebut join akan dideduplikasi untuk meninggalkan satu baris acak untuk setiap nilai unik.

    Perilaku default ini dapat meninggalkan informasi penting dari tabel kiri yang dapat memberikan wawasan yang berguna. Misalnya, kueri di bawah ini hanya akan menampilkan satu email yang berisi lampiran tertentu, bahkan jika lampiran yang sama dikirim menggunakan beberapa pesan email:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    

    Untuk mengatasi batasan ini, kami menerapkan ragam gabungan dalam dengan menentukan kind=inner untuk menampilkan semua baris di tabel kiri dengan nilai yang cocok di sebelah kanan:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Gabungkan rekaman dari jendela waktu—Saat menyelidiki peristiwa keamanan, analis mencari peristiwa terkait yang terjadi sekitar periode waktu yang sama. Menerapkan pendekatan yang sama saat menggunakan join juga meningkatkan performa dengan mengurangi jumlah record yang perlu diperiksa.

    Kueri di bawah ini memeriksa peristiwa log masuk dalam 30 menit setelah menerima file berbahaya:

    EmailEvents
    | where Timestamp > ago(7d)
    | where ThreatTypes has "Malware"
    | project EmailReceivedTime = Timestamp, Subject, SenderFromAddress, AccountName = tostring(split(RecipientEmailAddress, "@")[0])
    | join (
    DeviceLogonEvents
    | where Timestamp > ago(7d)
    | project LogonTime = Timestamp, AccountName, DeviceName
    ) on AccountName
    | where (LogonTime - EmailReceivedTime) between (0min .. 30min)
    
  • Menerapkan filter waktu di kedua sisi—Meskipun Anda tidak menyelidiki jendela waktu tertentu, menerapkan filter waktu di tabel kiri dan kanan dapat mengurangi jumlah rekaman untuk memeriksa dan meningkatkan join performa. Kueri di bawah ini berlaku Timestamp > ago(1h) untuk kedua tabel sehingga hanya menggabungkan rekaman dari jam terakhir:

    EmailAttachmentInfo
    | where Timestamp > ago(1h)
    | where Subject == "Document Attachment" and FileName == "Document.pdf"
    | join kind=inner (DeviceFileEvents | where Timestamp > ago(1h)) on SHA256
    
  • Gunakan petunjuk untuk performa—Gunakan petunjuk dengan join operator untuk menginstruksikan backend untuk mendistribusikan beban saat menjalankan operasi intensif sumber daya. Pelajari selengkapnya tentang petunjuk bergabung.

    Misalnya, petunjuk acak membantu meningkatkan performa kueri saat menggabungkan tabel menggunakan kunci dengan kardinalitas tinggi—kunci dengan banyak nilai unik—seperti AccountObjectId dalam kueri di bawah ini:

    IdentityInfo
    | where JobTitle == "CONSULTANT"
    | join hint.shufflekey = AccountObjectId
    (IdentityDirectoryEvents
        | where Application == "Active Directory"
        | where ActionType == "Private data retrieval")
    on AccountObjectId
    

    Petunjuk siaran membantu ketika tabel kiri kecil (hingga 100.000 rekaman) dan tabel kanan sangat besar. Misalnya, kueri di bawah ini mencoba menggabungkan beberapa email yang memiliki subjek tertentu dengan semua pesan yang berisi tautan dalam EmailUrlInfo tabel:

    EmailEvents
    | where Subject in ("Warning: Update your credentials now", "Action required: Update your credentials now")
    | join hint.strategy = broadcast EmailUrlInfo on NetworkMessageId
    

summarize Mengoptimalkan operator

Operator ringkasan mengagregasi konten tabel. Terapkan tips ini untuk mengoptimalkan kueri yang menggunakan operator ini.

  • Temukan nilai yang berbeda—Secara umum, gunakan summarize untuk menemukan nilai berbeda yang bisa berulang. Tidak perlu menggunakannya untuk mengagregasi kolom yang tidak memiliki nilai berulang.

    Meskipun satu email dapat menjadi bagian dari beberapa peristiwa, contoh di bawah ini bukan penggunaan summarize yang efisien karena ID pesan jaringan untuk email individual selalu dilengkapi dengan alamat pengirim yang unik.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by NetworkMessageId, SenderFromAddress
    

    Operator summarize dapat dengan mudah diganti dengan project, menghasilkan hasil yang berpotensi sama sambil mengonsumsi lebih sedikit sumber daya:

    EmailEvents
    | where Timestamp > ago(1h)
    | project NetworkMessageId, SenderFromAddress
    

    Contoh berikut adalah penggunaan summarize yang lebih efisien karena mungkin ada beberapa instans berbeda dari alamat pengirim yang mengirim email ke alamat penerima yang sama. Kombinasi seperti itu kurang berbeda dan kemungkinan memiliki duplikat.

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize by SenderFromAddress, RecipientEmailAddress
    
  • Mengacak kueri—Meskipun summarize paling baik digunakan dalam kolom dengan nilai berulang, kolom yang sama juga dapat memiliki kardinalitas tinggi atau sejumlah besar nilai unik. Seperti operator join, Anda juga dapat menerapkan hint shuffle dengan summarize untuk mendistribusikan beban pemrosesan dan berpotensi meningkatkan performa saat menangani kolom dengan kardinalitas tinggi.

    Kueri di bawah ini menggunakan summarize untuk menghitung alamat email penerima yang berbeda, yang dapat berjalan di ratusan ribu di organisasi besar. Untuk meningkatkan performa, ia menggabungkan hint.shufflekey:

    EmailEvents
    | where Timestamp > ago(1h)
    | summarize hint.shufflekey = RecipientEmailAddress count() by Subject, RecipientEmailAddress
    

Skenario kueri pencarian

Mengidentifikasi proses unik dengan ID proses

ID Proses (PID) didaur ulang dalam Windows dan digunakan kembali untuk proses baru. Dengan sendirinya, mereka tidak dapat berfungsi sebagai pengidentifikasi unik untuk proses tertentu.

Biasanya, satu-satunya cara untuk mengidentifikasi proses secara unik pada perangkat tertentu adalah dengan menggabungkan ID prosesnya dengan waktu pembuatan prosesnya, bersama dengan pengidentifikasi perangkat (baik DeviceId atau DeviceName). Misalnya, kueri contoh berikut mengidentifikasi proses yang mengakses lebih dari 10 alamat IP pada port 445 (SMB), yang kemungkinan sedang memindai share file.

DeviceNetworkEvents
| where RemotePort == 445 and Timestamp > ago(12h) and InitiatingProcessId !in (0, 4)
| summarize RemoteIPCount=dcount(RemoteIP) by DeviceName, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessFileName
| where RemoteIPCount > 10

Kueri di atas merangkum berdasarkan InitiatingProcessId dan InitiatingProcessCreationTime sehingga hanya melihat satu proses, tanpa mencampurkan beberapa proses yang memiliki ID proses yang sama.

Pendekatan ini masih berlaku, terutama untuk sistem non-Windows. Namun, di Windows, ada cara yang lebih langsung untuk menggunakan bidang ProcessUniqueId tersebut. Meskipun metode sebelumnya dan metode yang dibahas di bawah ini sama-sama menghasilkan instans proses yang unik, sebagai praktik terbaik, kami merekomendasikan penggunaan ProcessUniqueId jika tersedia, karena hal ini menyederhanakan kueri dan menghilangkan kebutuhan untuk menangani skenario penggunaan ulang PID.

Kueri ini menunjukkan cara menggunakan ProcessUniqueId bidang dan InitiatingProcessUniqueId untuk menautkan proses induk tertentu ke proses turunannya. Dengan mencocokkan InitiatingProcessUniqueId milik setiap proses anak dengan ProcessUniqueId milik proses induk, sistem ini hanya mengisolasi proses-proses anak yang diluncurkan oleh instans proses induk yang sama persis, bahkan jika ID proses digunakan ulang seiring waktu.

Kueri contoh:

// Step 1: Select a specific parent process instance (for instance, powershell.exe). 
let parentProcess = 
    DeviceProcessEvents
    | where FileName =~ "powershell.exe" // For your specific use case, consider modifying the FileName and adding more identifying properties to specify your query.
    | where isnotempty(ProcessUniqueId)
    | top 1 by Timestamp asc 
    | project DeviceId, DeviceName, ParentProcessUniqueId = ProcessUniqueId, ParentFileName = FileName;
// Step 2: Find all child processes started by this unique parent.
DeviceProcessEvents
| where isnotempty(InitiatingProcessUniqueId)
| join kind=inner (
    parentProcess
) on DeviceId
| where InitiatingProcessUniqueId == ParentProcessUniqueId
| project 
    DeviceName,
    ParentProcessUniqueId,
    ParentFileName,
    ChildProcessName = FileName,
    ChildProcessId = ProcessId,
    ChildProcessUniqueId = ProcessUniqueId,
    Timestamp

Demikian pula, kueri dikelompokkan berdasarkan InitiatingProcessId dan InitiatingProcessCreationTime sehingga kueri hanya melihat satu proses, tanpa mencampuradukkan beberapa proses yang memiliki ID proses yang sama.

Cuplikan layar sampel hasil kueri untuk mendapatkan proses unik di portal Microsoft Defender.

Baris perintah kueri

Ada banyak cara untuk membangun baris perintah untuk menyelesaikan tugas. Misalnya, penyerang dapat mereferensikan file gambar tanpa jalur, tanpa ekstensi file, menggunakan variabel lingkungan, atau dengan tanda kutip. Penyerang juga dapat mengubah urutan parameter atau menambahkan beberapa tanda kutip dan spasi.

Untuk membuat kueri yang lebih tahan lama di sekitar baris perintah, terapkan praktik berikut:

  • Identifikasi proses yang diketahui (seperti net.exe atau psexec.exe) dengan mencocokkan pada bidang nama file, alih-alih memfilter pada baris perintah itu sendiri.
  • Mengurai bagian baris perintah menggunakan fungsi parse_command_line()
  • Saat mengkueri argumen baris perintah, jangan cari kecocokan persis pada beberapa argumen yang tidak terkait dalam urutan tertentu. Sebagai gantinya, gunakan ekspresi reguler atau beberapa operator `contains` yang terpisah.
  • Gunakan kecocokan tanpa membedakan huruf besar/kecil. Misalnya, gunakan =~, in~, dan contains bukan ==, in, dan contains_cs.
  • Untuk mengurangi teknik pengaburan baris perintah, pertimbangkan untuk menghapus tanda kutip, mengganti koma dengan spasi, dan mengganti beberapa spasi berurutan dengan satu spasi. Ada teknik obfuskasi yang lebih kompleks yang memerlukan pendekatan lain, tetapi penyesuaian ini dapat membantu mengatasi teknik yang umum.

Contoh berikut menunjukkan berbagai cara untuk membuat kueri yang mencari file net.exe untuk menghentikan layanan firewall "MpsSvc":

// Non-durable query - do not use
DeviceProcessEvents
| where ProcessCommandLine == "net stop MpsSvc"
| limit 10

// Better query - filters on file name, does case-insensitive matches
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe") and ProcessCommandLine contains "stop" and ProcessCommandLine contains "MpsSvc"

// Best query also ignores quotes
DeviceProcessEvents
| where Timestamp > ago(7d) and FileName in~ ("net.exe", "net1.exe")
| extend CanonicalCommandLine=replace("\"", "", ProcessCommandLine)
| where CanonicalCommandLine contains "stop" and CanonicalCommandLine contains "MpsSvc"

Menyerap data dari sumber eksternal

Untuk menggabungkan daftar panjang atau tabel besar ke dalam kueri Anda, gunakan operator data eksternal untuk menyerap data dari URI tertentu. Anda bisa mendapatkan data dari file di TXT, CSV, JSON, atau format lainnya. Contoh di bawah ini menunjukkan bagaimana Anda dapat menggunakan daftar luas hash SHA-256 malware yang disediakan oleh MalwareBazaar (abuse.ch) untuk memeriksa lampiran pada email:

let abuse_sha256 = (externaldata(sha256_hash: string)
[@"https://bazaar.abuse.ch/export/txt/sha256/recent/"]
with (format="txt"))
| where sha256_hash !startswith "#"
| project sha256_hash;
abuse_sha256
| join (EmailAttachmentInfo
| where Timestamp > ago(1d)
) on $left.sha256_hash == $right.SHA256
| project Timestamp,SenderFromAddress,RecipientEmailAddress,FileName,FileType,
SHA256,ThreatTypes,DetectionMethods

Mengurai teks

Ada berbagai fungsi yang dapat Anda gunakan untuk menangani string secara efisien yang membutuhkan penguraian atau konversi.

string Function Contoh penggunaan
Baris perintah parse_command_line() Ekstrak perintah dan semua argumen.
Paths parse_path() Ekstrak bagian dari jalur file atau folder.
Nomor versi parse_version() Uraikan nomor versi yang terdiri atas paling banyak empat bagian dan paling banyak delapan karakter per bagian. Gunakan data yang diurai untuk membandingkan usia versi.
Alamat IPv4 parse_ipv4() Mengonversi alamat IPv4 ke bilangan bulat panjang. Untuk membandingkan alamat IPv4 tanpa mengonversinya, gunakan ipv4_compare().
Alamat IPv6 parse_ipv6() Konversi alamat IPv4 atau IPv6 ke notasi IPv6 kanonis. Untuk membandingkan alamat IPv6, gunakan ipv6_compare().

Untuk mempelajari tentang semua fungsi penguraian yang didukung, baca tentang fungsi string Kusto.

Note

Beberapa tabel dalam artikel ini mungkin tidak tersedia di Microsoft Defender untuk Titik Akhir. Aktifkan Microsoft Defender untuk berburu ancaman menggunakan lebih banyak sumber data. Anda dapat memindahkan alur kerja berburu tingkat lanjut dari Microsoft Defender untuk Titik Akhir ke Microsoft Defender dengan mengikuti langkah-langkah dalam Memigrasikan kueri berburu tingkat lanjut dari Microsoft Defender untuk Titik Akhir.

Tip

Apakah Anda ingin mempelajari lebih lanjut? Terlibatlah dengan komunitas Microsoft Security di Komunitas Teknologi kami: Microsoft Defender XDR Tech Community.