Rumah > Tutorial sistem > LINUX > teks badan

Pengembangan tindanan debugger Linux!

王林
Lepaskan: 2024-01-06 22:25:43
ke hadapan
979 orang telah melayarinya
Pengenalan Kadangkala maklumat paling penting yang perlu anda ketahui ialah bagaimana keadaan program semasa anda sampai ke sana. Terdapat arahan jejak belakang, yang memberikan anda rangkaian panggilan fungsi semasa program anda. Siaran ini akan menunjukkan kepada anda cara melaksanakan penyalahan tindanan pada x86_64 untuk menjana jejak balik sedemikian.
Indeks Siri

Pautan ini akan disiarkan secara langsung apabila siaran lain diterbitkan.

  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. Baca pembolehubah
  10. Langkah seterusnya

Gunakan program berikut sebagai contoh:

void a() {
//stopped here
}
void b() {
a();
}
void c() {
a();
}
int main() {
b();
c();
}
Salin selepas log masuk

Jika penyahpepijat berhenti di baris //berhenti di sini', terdapat dua cara untuk mencapainya: main->b->a atau main->c->a`. Jika kami menetapkan titik putus dengan LLDB, teruskan pelaksanaan dan minta jejak balik, maka kami mendapat perkara berikut:

* frame #0: 0x00000000004004da a.out`a() + 4 at bt.cpp:3
frame #1: 0x00000000004004e6 a.out`b() + 9 at bt.cpp:6
frame #2: 0x00000000004004fe a.out`main + 9 at bt.cpp:14
frame #3: 0x00007ffff7a2e830 libc.so.6`__libc_start_main + 240 at libc-start.c:291
frame #4: 0x0000000000400409 a.out`_start + 41
Salin selepas log masuk

Ini bermakna kita sedang dalam fungsi a, a melompat dari fungsi b, b melompat dari utama, dsb. Dua bingkai terakhir ialah cara pengkompil membongkar fungsi utama.

Persoalannya sekarang ialah bagaimana kami melaksanakannya pada x86_64. Pendekatan yang paling mantap ialah menghuraikan bahagian .eh_frame bagi fail ELF dan memikirkan cara untuk melepaskan timbunan dari sana, tetapi itu akan menyusahkan. Anda boleh melakukannya menggunakan libunwind atau serupa, tetapi itu membosankan. Sebaliknya, kami menganggap bahawa pengkompil telah menyediakan timbunan dalam beberapa cara dan kami akan melintasinya secara manual. Untuk melakukan ini, kita perlu memahami susun atur timbunan terlebih dahulu.

High
| ... |
+---------+
+24| Arg 1 |
+---------+
+16| Arg 2 |
+---------+
+ 8| Return |
+---------+
EBP+--> |Saved EBP|
+---------+
- 8| Var 1 |
+---------+
ESP+--> | Var 2 |
+---------+
| ... |
Low
Salin selepas log masuk

Seperti yang anda lihat, penuding bingkai bingkai tindanan terakhir disimpan pada permulaan bingkai tindanan semasa, mencipta senarai penuding yang dipautkan. Tindanan dibongkar berdasarkan senarai terpaut ini. Kita boleh mencari fungsi untuk bingkai seterusnya dalam senarai dengan mencari alamat pemulangan dalam mesej DWARF. Sesetengah penyusun akan mengabaikan penjejakan alamat asas bingkai EBP kerana ini boleh dinyatakan sebagai offset daripada ESP dan membebaskan daftar tambahan. Walaupun dengan pengoptimuman didayakan, menghantar -fno-omit-frame-pointer kepada GCC atau Clang akan memaksanya untuk mengikut konvensyen yang kami bergantung kepada.

Kami akan melakukan semua kerja dalam fungsi print_backtrace:

void debugger::print_backtrace() {
Salin selepas log masuk

Perkara pertama yang perlu diputuskan ialah format yang hendak digunakan untuk mencetak maklumat bingkai. Saya menggunakan lambda untuk melancarkan kaedah ini:

auto output_frame = [frame_number = 0] (auto&& func) mutable {
std::cout << "frame #" << frame_number++ << ": 0x" << dwarf::at_low_pc(func)
<< ' ' << dwarf::at_name(func) << std::endl;
};
Salin selepas log masuk

Bingkai pertama yang dicetak ialah bingkai yang sedang dilaksanakan. Kita boleh mendapatkan maklumat tentang bingkai ini dengan mencari kaunter program semasa dalam DWARF:

auto current_func = get_function_from_pc(get_pc());
output_frame(current_func);
Salin selepas log masuk

Seterusnya kita perlu mendapatkan penunjuk bingkai dan alamat pemulangan fungsi semasa. Penunjuk bingkai disimpan dalam daftar rbp dan alamat pemulangan ialah 8 bait yang disusun daripada penuding bingkai.

auto frame_pointer = get_register_value(m_pid, reg::rbp);
auto return_address = read_memory(frame_pointer+8);
Salin selepas log masuk

Kini kami mempunyai semua maklumat yang kami perlukan untuk mengembangkan timbunan. Saya terus berehat sehingga penyahpepijat mencecah utama, tetapi anda juga boleh memilih untuk berhenti apabila penuding bingkai ialah 0x0, iaitu fungsi yang anda panggil sebelum memanggil fungsi utama. Kami akan mengambil penunjuk bingkai dan alamat pemulangan dari setiap bingkai dan mencetak maklumat tersebut.

while (dwarf::at_name(current_func) != "main") {
current_func = get_function_from_pc(return_address);
output_frame(current_func);
frame_pointer = read_memory(frame_pointer);
return_address = read_memory(frame_pointer+8);
}
}
Salin selepas log masuk

Itu sahaja! Inilah keseluruhan fungsi:

void debugger::print_backtrace() {
auto output_frame = [frame_number = 0] (auto&& func) mutable {
std::cout << "frame #" << frame_number++ << ": 0x" << dwarf::at_low_pc(func)
<< ' ' << dwarf::at_name(func) << std::endl;
};
auto current_func = get_function_from_pc(get_pc());
output_frame(current_func);
auto frame_pointer = get_register_value(m_pid, reg::rbp);
auto return_address = read_memory(frame_pointer+8);
while (dwarf::at_name(current_func) != "main") {
current_func = get_function_from_pc(return_address);
output_frame(current_func);
frame_pointer = read_memory(frame_pointer);
return_address = read_memory(frame_pointer+8);
}
}
Salin selepas log masuk
Tambah arahan

Sudah tentu, kita mesti mendedahkan arahan ini kepada pengguna.

else if(is_prefix(command, "backtrace")) {
print_backtrace();
}
Salin selepas log masuk
Ujian

Salah satu cara untuk menguji fungsi ini ialah dengan menulis program ujian dengan sekumpulan fungsi kecil yang memanggil satu sama lain. Tetapkan beberapa titik putus, lompat sekeliling kod dan pastikan jejak balik anda adalah tepat.

Kami telah melangkah jauh dari program yang hanya boleh melahirkan dan melekat pada program lain. Artikel terakhir dalam siri ini akan melengkapkan pelaksanaan penyahpepijat dengan menyokong pembolehubah membaca dan menulis. Sehingga itu, anda boleh mencari kod untuk siaran ini di sini.

Atas ialah kandungan terperinci Pengembangan tindanan debugger Linux!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:linuxprobe.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan