Rumah  >  Artikel  >  Tutorial sistem  >  Terokai teknik pengendalian berubah-ubah dalam penyahpepijat Linux!

Terokai teknik pengendalian berubah-ubah dalam penyahpepijat Linux!

PHPz
PHPzke hadapan
2024-01-15 23:09:05661semak imbas
Pengenalan Pembolehubah adalah licik. Kadang-kadang mereka akan dengan senang hati duduk dalam daftar, hanya untuk berakhir di timbunan sebaik sahaja mereka berpusing. Untuk tujuan pengoptimuman, pengkompil boleh membuangnya keluar dari tetingkap sepenuhnya. Tidak kira bagaimana pembolehubah bergerak dalam ingatan, kita memerlukan beberapa cara untuk menjejak dan memanipulasinya dalam penyahpepijat. Artikel ini akan mengajar anda cara mengendalikan pembolehubah dalam penyahpepijat dan menunjukkan pelaksanaan mudah menggunakan libelfin.
Indeks artikel bersiri
  1. Sediakan persekitaran
  2. Titik putus
  3. Daftar dan Memori
  4. DIRI dan KEDIRI
  5. Kod sumber dan isyarat
  6. Pelaksanaan langkah demi langkah pada tahap kod sumber
  7. Titik putus tahap sumber
  8. Peluasan tindanan
  9. Kendalikan pembolehubah
  10. Topik Lanjutan

Sebelum bermula, sila pastikan anda menggunakan versi libelfin fbreg di cawangan saya. Ini mengandungi beberapa penggodaman untuk menyokong mendapatkan alamat asas bingkai tindanan semasa dan menilai senarai lokasi, tidak satu pun daripadanya disediakan oleh libelfin asli. Anda mungkin perlu menghantar parameter -gdwarf-2 kepada GCC untuk menjana mesej DWARF yang serasi. Tetapi sebelum melaksanakannya, saya akan memperincikan cara pengekodan kedudukan berfungsi dalam spesifikasi DWARF 5 terkini. Jika anda ingin mengetahui lebih lanjut, anda boleh mendapatkan standard di sini.

Lokasi kerdil

Lokasi pembolehubah dalam ingatan pada masa tertentu dikodkan dalam mesej DWARF menggunakan atribut DW_AT_location. Perihalan lokasi boleh menjadi perihalan lokasi tunggal, perihalan lokasi komposit atau senarai lokasi.

  • Perihalan kedudukan mudah: Menerangkan kedudukan bahagian bersebelahan (biasanya semua bahagian) objek. Penerangan lokasi yang ringkas boleh menerangkan lokasi dalam memori boleh alamat atau daftar, atau kekurangannya (dengan atau tanpa nilai yang diketahui). Contohnya, DW_OP_fbreg -32: Keseluruhan pembolehubah tersimpan - 32 bait bermula dari pangkalan bingkai tindanan.
  • Perihalan lokasi komposit: Menggambarkan objek dari segi serpihan, setiap objek boleh terkandung dalam bahagian daftar atau disimpan di lokasi memori bebas daripada serpihan lain. Contohnya, DW_OP_reg3 DW_OP_piece 4 DW_OP_reg10 DW_OP_piece 2: Empat bait pertama berada dalam daftar 3 dan dua bait terakhir berada dalam pembolehubah dalam daftar 10.
  • Senarai Kedudukan: Menerangkan objek yang mempunyai hayat terhad atau menukar lokasi semasa hayatnya. contohnya:
      • [ 0]DW_OP_reg0
      • [ 1]DW_OP_reg3
      • [ 2]DW_OP_reg2
  • Pembolehubah yang lokasinya dialihkan antara daftar berdasarkan nilai semasa pembilang program.

DW_AT_location dikodkan dalam tiga cara berbeza bergantung pada jenis perihalan lokasi. exprloc mengekod huraian kedudukan mudah dan komposit. Ia terdiri daripada panjang bait diikuti dengan ungkapan DWARF atau perihalan lokasi. Senarai lokasi yang dikodkan untuk loclist dan loclistptr, yang menyediakan indeks atau offset dalam bahagian .debug_loclists, yang menerangkan senarai lokasi sebenar.

Ekspresi kerdil

Gunakan ungkapan DWARF untuk mengira kedudukan sebenar pembolehubah. Ini termasuk satu siri operasi yang memanipulasi nilai tindanan. Terdapat banyak operasi DWARF yang tersedia, jadi saya tidak akan menerangkannya secara terperinci. Sebaliknya, saya akan memberikan beberapa contoh daripada setiap ungkapan untuk memberi anda sesuatu untuk diusahakan. Juga, jangan takut tentang perkara ini; libelfin akan menangani semua kerumitan ini untuk kami.

  • Pengekodan literal
    • DW_OP_lit0, DW_OP_lit1...DW_OP_lit31
      • Tolak huruf ke dalam tindanan
    • DW_OP_addr
      • Tolak operan alamat pada tindanan
    • DW_OP_constu
      • Tolak nilai yang tidak ditandatangani pada tindanan
  • Nilai daftar
    • DW_OP_fbreg
      • Tolak nilai yang terdapat pada dasar bingkai tindanan, diimbangi oleh nilai yang diberikan
    • DW_OP_breg0, DW_OP_breg1... DW_OP_breg31
      • Tolak kandungan daftar yang diberikan ditambah dengan offset yang diberikan ke dalam tindanan
  • Operasi tindanan
    • DW_OP_dup
      • Salin nilai di bahagian atas tindanan
    • DW_OP_deref
      • Anggap bahagian atas timbunan sebagai alamat memori dan gantikannya dengan kandungan alamat itu
  • Operasi aritmetik dan logik
    • DW_OP_dan
      • Letakkan dua nilai di bahagian atas timbunan dan tolak logiknya DAN
    • DW_OP_plus
      • Sama seperti DW_OP_and, tetapi menambah nilai
  • Kawal operasi aliran
    • DW_OP_le, DW_OP_eq, DW_OP_gt, dsb.
      • Pop dua nilai pertama, bandingkannya dan tolak 1 jika keadaannya benar, 0 sebaliknya
    • DW_OP_bra
      • Cawangan bersyarat: Jika bahagian atas tindanan bukan 0, langkau ke hadapan atau ke belakang dalam ungkapan melalui offset
  • Masukkan penukaran
    • DW_OP_convert
      • Tukar nilai di bahagian atas tindanan kepada jenis yang berbeza, yang diterangkan oleh entri maklumat DWARF pada offset yang diberikan
  • Operasi khas
    • DW_OP_nop
      • Buat apa-apa!
Jenis kerdil

Perwakilan jenis DWARF perlu cukup berkuasa untuk menyediakan perwakilan pembolehubah yang berguna untuk menyahpepijat pengguna. Pengguna selalunya mahu dapat menyahpepijat pada peringkat aplikasi dan bukannya pada peringkat mesin, dan mereka perlu memahami perkara yang dilakukan oleh pembolehubah mereka.

Jenis DWARF dikodkan dalam DIE bersama-sama dengan kebanyakan maklumat penyahpepijatan yang lain. Mereka boleh mempunyai sifat yang menunjukkan nama mereka, pengekodan, saiz, bait, dsb. Pelbagai teg jenis tersedia untuk mewakili penunjuk, tatasusunan, struktur, typedef dan apa-apa lagi yang mungkin anda lihat dalam program C atau C++.

Ambil struktur mudah ini sebagai contoh:

struct test{
int i;
float j;
int k[42];
test* next;
};

Induk MATI struktur ini adalah seperti ini:

< 1><0x0000002a> DW_TAG_structure_type
DW_AT_name "test"
DW_AT_byte_size 0x000000b8
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000001

Apa yang dinyatakan di atas ialah kami mempunyai struktur yang dipanggil ujian, dengan saiz 0xb8, diisytiharkan pada baris 1 test.cpp. Seterusnya terdapat beberapa sub-DIE yang menggambarkan ahli.

< 2><0x00000032> DW_TAG_member
DW_AT_name "i"
DW_AT_type <0x00000063>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000002
DW_AT_data_member_location 0
< 2><0x0000003e> DW_TAG_member
DW_AT_name "j"
DW_AT_type <0x0000006a>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000003
DW_AT_data_member_location 4
< 2><0x0000004a> DW_TAG_member
DW_AT_name "k"
DW_AT_type <0x00000071>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000004
DW_AT_data_member_location 8
< 2><0x00000056> DW_TAG_member
DW_AT_name "next"
DW_AT_type <0x00000084>
DW_AT_decl_file 0x00000001 test.cpp
DW_AT_decl_line 0x00000005
DW_AT_data_member_location 176(as signed = -80)

Setiap ahli mempunyai nama, jenis (iaitu offset DIE), fail dan baris pengisytiharan, dan ofset bait kepada struktur di mana ahlinya berada. Titik jenisnya adalah seperti berikut.

< 1><0x00000063> DW_TAG_base_type
DW_AT_name "int"
DW_AT_encoding DW_ATE_signed
DW_AT_byte_size 0x00000004
< 1><0x0000006a> DW_TAG_base_type
DW_AT_name "float"
DW_AT_encoding DW_ATE_float
DW_AT_byte_size 0x00000004
< 1><0x00000071> DW_TAG_array_type
DW_AT_type <0x00000063>
< 2><0x00000076> DW_TAG_subrange_type
DW_AT_type <0x0000007d>
DW_AT_count 0x0000002a
< 1><0x0000007d> DW_TAG_base_type
DW_AT_name "sizetype"
DW_AT_byte_size 0x00000008
DW_AT_encoding DW_ATE_unsigned
< 1><0x00000084> DW_TAG_pointer_type
DW_AT_type <0x0000002a>

Seperti yang anda lihat, int pada komputer riba saya ialah jenis integer bertanda 4-bait dan apungan ialah nombor titik terapung 4-bait. Jenis tatasusunan integer mempunyai elemen 2a dengan menunjuk ke taip int sebagai jenis elemen dan jenis saiz (anggap ia sebagai size_t) sebagai jenis indeks. Jenis ujian * ialah DW_TAG_pointer_type, yang merujuk kepada ujian DIE.

Melaksanakan pembaca pembolehubah mudah

Seperti yang dinyatakan di atas, libelfin akan mengendalikan kebanyakan kerumitan untuk kita. Walau bagaimanapun, ia tidak melaksanakan semua kaedah untuk mewakili kedudukan berubah, dan pengendalian ini dalam kod kami akan menjadi sangat rumit. Oleh itu, saya kini memilih untuk menyokong exprloc sahaja. Sila tambah sokongan untuk lebih banyak jenis ungkapan seperti yang diperlukan. Jika anda benar-benar berani, sila serahkan tampung kepada libelfin untuk membantu melengkapkan sokongan yang diperlukan!

Mengendalikan pembolehubah terutamanya melibatkan pengesanan bahagian yang berbeza dalam ingatan atau daftar, dan membaca atau menulis adalah sama seperti sebelumnya. Untuk memastikan perkara mudah, saya hanya akan memberitahu anda cara melaksanakan pembacaan.

Mula-mula kami perlu memberitahu libelfin cara membaca daftar daripada proses kami. Kami mencipta kelas yang mewarisi daripada expr_context dan menggunakan ptrace untuk mengendalikan segala-galanya:

class ptrace_expr_context : public dwarf::expr_context {
public:
ptrace_expr_context (pid_t pid) : m_pid{pid} {}
dwarf::taddr reg (unsigned regnum) override {
return get_register_value_from_dwarf_register(m_pid, regnum);
}
dwarf::taddr pc() override {
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, m_pid, nullptr, &regs);
return regs.rip;
}
dwarf::taddr deref_size (dwarf::taddr address, unsigned size) override {
//TODO take into account size
return ptrace(PTRACE_PEEKDATA, m_pid, address, nullptr);
}
private:
pid_t m_pid;
};

Bacaan akan dikendalikan oleh fungsi read_variables dalam kelas debugger kami:

void debugger::read_variables() {
using namespace dwarf;
auto func = get_function_from_pc(get_pc());
//...
}

Perkara pertama yang kita lakukan di atas ialah mencari fungsi yang kita ada sekarang, maka kita perlu mengulangi entri dalam fungsi itu untuk mencari pembolehubah:

for (const auto& die : func) {
if (die.tag == DW_TAG::variable) {
//...
}
}

Kami mendapat maklumat lokasi dengan mencari entri DW_AT_location dalam DIE:

auto loc_val = die[DW_AT::location];

Kemudian kami pastikan ia exprloc dan minta libelfin menilai ungkapan kami:

if (loc_val.get_type() == value::type::exprloc) {
ptrace_expr_context context {m_pid};
auto result = loc_val.as_exprloc().evaluate(&context);

Sekarang kita telah menilai ungkapan, kita perlu membaca kandungan pembolehubah. Ia boleh berada dalam ingatan atau daftar, jadi kami akan mengendalikan kedua-dua kes:

switch (result.location_type) {
case expr_result::type::address:
{
auto value = read_memory(result.value);
std::cout << at_name(die) << " (0x" << std::hex << result.value << ") = "
<< value << std::endl;
break;
}
case expr_result::type::reg:
{
auto value = get_register_value_from_dwarf_register(m_pid, result.value);
std::cout << at_name(die) << " (reg " << result.value << ") = "
<< value << std::endl;
break;
}
default:
throw std::runtime_error{"Unhandled variable location"};
}

Anda boleh melihat bahawa berdasarkan jenis pembolehubah, saya mencetak nilai tanpa penjelasan. Mudah-mudahan dengan kod ini anda dapat melihat bagaimana terdapat sokongan untuk menulis pembolehubah, atau mencari pembolehubah dengan nama tertentu.

Akhirnya kami boleh menambah ini pada penghurai arahan kami:

else if(is_prefix(command, "variables")) {
read_variables();
}
Ujinya

Tulis beberapa fungsi kecil dengan beberapa pembolehubah, susunkannya tanpa pengoptimuman dan dengan maklumat nyahpepijat, kemudian lihat jika anda boleh membaca nilai pembolehubah itu. Cuba tulis ke alamat memori tempat pembolehubah disimpan dan lihat bagaimana program mengubah tingkah laku.

Sudah ada sembilan artikel, dan yang terakhir tinggal! Lain kali saya akan membincangkan beberapa konsep yang lebih maju yang mungkin menarik minat anda. Kini anda boleh mencari kod untuk siaran ini di sini.

Atas ialah kandungan terperinci Terokai teknik pengendalian berubah-ubah dalam penyahpepijat Linux!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:linuxprobe.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam