{"id":395866,"date":"2024-06-29T12:29:05","date_gmt":"2024-06-29T12:29:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=395866"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T12:29:05","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=395866","title":{"rendered":"<span>GDB Tutorial for Reverse Engineers: Breakpoints, Modifying Memory and Printing its Contents<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/223\/500\/8b1\/2235008b12534a7fbc5591a8bf6a3e64.jpeg\" width=\"1828\" height=\"1028\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/223\/500\/8b1\/2235008b12534a7fbc5591a8bf6a3e64.jpeg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>GDB is THE debugger for Linux programs. It\u2019s super powerful. But its user-friendliness or lack thereof can actually make you throw your PC out of the window. But what\u2019s important to understand about GDB is that GDB is not simply a tool, it\u2019s a debugging framework for you to build upon. In this video, I\u2019m gonna walk you through GDB setup for reverse engineering and show you all of the necessary commands and shortcuts for your debugging workflow.<\/p>\n<h2>GDB Setup With Plugins<\/h2>\n<p>The\u00a0most common mistake I see is that people perceive GDB as a standalone debugger tool. I suggest you think of GDB as a debugger framework that allows you to build your own tools. Or you can use premade tools. My GDB-based tool of choice is Peda. It\u2019s pretty easy to install, just follow installation instructions from Peda repository:\u00a0<a href=\"https:\/\/github.com\/longld\/peda\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/github.com\/longld\/peda<\/u><\/a><\/p>\n<pre><code>git clone https:\/\/github.com\/longld\/peda.git ~\/peda echo \"source ~\/peda\/peda.py\" >> ~\/.gdbinit<\/code><\/pre>\n<h2>Program example<\/h2>\n<p>I\u2019ll be using this simple program as our debugging target:<\/p>\n<pre><code>#include &lt;stdio.h>int main() {int x = 5;printf(\"x = %d\\n\", x);x = x + 5;printf(\"x = %d\", x);return 0;}<\/code><\/pre>\n<p>This code creates local variable X on the stack, prints its value to the console, then adds 5 to X, and prints its value again. Pretty simple, just what we need for our learning example.<\/p>\n<h2>First Steps with Debugging a Program<\/h2>\n<p>To open a file in GDB, type\u00a0<code>gdb [filename]<\/code>\u00a0in the console. GDB will open and load your file. Notice, that code execution hasn\u2019t started. That\u2019s because there\u2019s a separate command to start the execution, it\u2019s called\u00a0<code>run<\/code>, or\u00a0<code>r\u00a0<\/code>for short.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/e12\/053\/d65\/e12053d65fac36fc1791ef6a4176ac13.png\" width=\"894\" height=\"192\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e12\/053\/d65\/e12053d65fac36fc1791ef6a4176ac13.png\"\/><figcaption><\/figcaption><\/figure>\n<p>And if we start it, our program successfully completes execution. That\u2019s because we haven\u2019t set any breakpoints yet. We can set a breakpoint using symbol name, for example,\u00a0<code>break main<\/code>, or using address: `break *0x555555555149`. You can print a list of all your breakpoints with \u2018info breakpoints\u2019 and delete them with\u00a0<code>delete &lt;breakpoint number><\/code>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9c1\/2b5\/47a\/9c12b547a99bccdac2bd6e3b25409ec2.png\" width=\"1022\" height=\"472\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9c1\/2b5\/47a\/9c12b547a99bccdac2bd6e3b25409ec2.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Now if I issue\u00a0<code>run\u00a0<\/code>command, the execution conveniently stops at the beginning of the \u201cmain\u201d function. And just to save us some time we can use command\u00a0<code>start\u00a0<\/code>which comes from Peda instead of doing all of this. This command will do all this work for us.<\/p>\n<p>Of course, as with any other debugger, we can use single-stepping with command\u00a0<code>step\u00a0<\/code>or\u00a0<code>stepi<\/code>.<\/p>\n<p>And if you like to do single-stepping a lot, note, that pressing \u2018Return\u2019 on an empty line will make GDB execute the previous command you entered once more. Also, you can use command\u00a0<code>next\u00a0<\/code>to single step without entering function calls.<\/p>\n<p>To step through several instructions at once you can use command\u00a0<code>next &lt;number of instructions><\/code>.<\/p>\n<p>If you want to continue execution to a certain point in a program (for example to exit a loop), you can use command\u00a0<code>xuntil &lt;address or function><\/code>.<\/p>\n<h2>Modifying Registers<\/h2>\n<p>To modify registers, use following construction\u00a0<code>set$&lt;register name> = 1234<\/code>. For example, if I want to skip incrementing X, I can change the value of RIP register to 0x555555555176 with command\u00a0<code>set $RIP = 0x555555555176<\/code>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/421\/28a\/dad\/42128adad20d3fc30fd35e43e17a2fa6.png\" width=\"626\" height=\"46\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/421\/28a\/dad\/42128adad20d3fc30fd35e43e17a2fa6.png\"\/><figcaption><\/figcaption><\/figure>\n<p>As you might have noticed, you can treat registers like variables in GDB. So I can assign RIP value of EAX with\u00a0<code>set $rip = $eax<\/code>\u00a0command. Now I can issue\u00a0<code>context<\/code>\u00a0command to make Peda reprint its beautiful context \u201cwindow\u201d to make sure that RIP points to some nonsense.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c6b\/3fa\/b8b\/c6b3fab8b2281a423d8fa73475b7619c.png\" width=\"834\" height=\"304\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c6b\/3fa\/b8b\/c6b3fab8b2281a423d8fa73475b7619c.png\"\/><figcaption><\/figcaption><\/figure>\n<p>And I want to start execution from the start of \u2018main\u2019 function I can just write\u00a0<code>set $rip = main<\/code>.<\/p>\n<p>By the way, with Peda you have a faster way to skip instructions without executing them with\u00a0<code>skipi<\/code>\u00a0command.<\/p>\n<h2>Modifying Memory<\/h2>\n<p>Modifying memory is similar to modifying registers in the sense that any memory location can be a variable in GDB. So in my example, I want to change the contents of the format string \u201cx = %d\u201d. This string is located at 0x555555556004 in my binary. I can use this address as a variable and type in the same command as with registers:\u00a0<code>set 0x555555556004 = \u201cAAAA%d\u201d<\/code>. But in this case, we\u2019ll see an error message. That\u2019s because you should always provide a variable type when modifying memory in GDB. So let me correct my command to\u00a0<code>set {char[7]}0x555555556004 = \u201cAAAA%d\u201d<\/code>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b65\/584\/a89\/b65584a89c8fb6405c84f16fe12b6835.png\" width=\"846\" height=\"118\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b65\/584\/a89\/b65584a89c8fb6405c84f16fe12b6835.png\"\/><figcaption><\/figcaption><\/figure>\n<h2>Print xprint hexdump hexprint<\/h2>\n<p>You can use the command\u00a0<code>x<\/code>\u00a0to examine memory. For example, if I want to print 20 8-byte words from the stack pointer, I\u2019ll use command\u00a0<code>x\/20xg $rsp<\/code>: 20 \u2014 the number of words, x \u2014 hexadecimal format, g \u2014 giant (8-byte words).<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/01f\/95e\/81c\/01f95e81c2c2c1c86f37bd93b3a6be3d.png\" width=\"1566\" height=\"806\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/01f\/95e\/81c\/01f95e81c2c2c1c86f37bd93b3a6be3d.png\"\/><figcaption><\/figcaption><\/figure>\n<p>By changing the second\u00a0<code>x<\/code>\u00a0to\u00a0<code>i<\/code>\u00a0you can print memory as instructions.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1b0\/83e\/902\/1b083e902313686454c4019337417f82.png\" width=\"940\" height=\"758\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1b0\/83e\/902\/1b083e902313686454c4019337417f82.png\"\/><figcaption><\/figcaption><\/figure>\n<p>The full list of supported modifiers you can find in the documentation:<\/p>\n<blockquote>\n<p><em>From\u00a0<\/em><a href=\"https:\/\/sourceware.org\/gdb\/current\/onlinedocs\/gdb\/Memory.html\" rel=\"noopener noreferrer nofollow\"><em><u>https:\/\/sourceware.org\/gdb\/current\/onlinedocs\/gdb\/Memory.html<\/u><\/em><\/a><\/p>\n<p><em>You can use the command\u00a0<\/em><code>x<\/code><em>\u00a0(for \u201cexamine\u201d) to examine memory in any of several formats, independently of your program\u2019s data types.<\/em><\/p>\n<p><code>x\/nfu addrx addrx<\/code><em>\u00a0Use the\u00a0<\/em><code>x<\/code><em>\u00a0command to examine memory.<\/em><\/p>\n<p><em>n, f, and u are all optional parameters that specify how much memory to display and how to format it; addr is an expression giving the address where you want to start displaying memory. If you use defaults for nfu, you need not type the slash \u2018\/\u2019. Several commands set convenient defaults for addr.<\/em><\/p>\n<p><em>n, the repeat countThe repeat count is a decimal integer; the default is 1. It specifies how much memory (counting by units u) to display. If a negative number is specified, memory is examined backward from addr.f, the display formatThe display format is one of the formats used by\u00a0<\/em><code>print<\/code><em>\u00a0(\u2018x\u2019, \u2018d\u2019, \u2018u\u2019, \u2018o\u2019, \u2018t\u2019, \u2018a\u2019, \u2018c\u2019, \u2018f\u2019, \u2018s\u2019), and in addition \u2018i\u2019 (for machine instructions). The default is \u2018x\u2019 (hexadecimal) initially. The default changes each time you use either\u00a0<\/em><code>x<\/code><em>\u00a0or\u00a0<\/em><code>print<\/code><em>.u, the unit sizeThe unit size is any of<\/em><code>b<\/code><em>Bytes.<\/em><code>h<\/code><em>Halfwords (two bytes).<\/em><code>w<\/code><em>Words (four bytes). This is the initial default.<\/em><code>g<\/code><em>Giant words (eight bytes).<\/em><\/p>\n<\/blockquote>\n<p>Also, Peda provides you with a convenient\u00a0<code>hexdump address \/count<\/code>\u00a0(dump \u201ccount\u201d lines, 16-bytes each) command which you can use\u2026 well, to display hex dump.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/308\/5b3\/da8\/3085b3da86fa0d60a0913433e52ffaa7.png\" width=\"1046\" height=\"414\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/308\/5b3\/da8\/3085b3da86fa0d60a0913433e52ffaa7.png\"\/><figcaption><\/figcaption><\/figure>\n<p>And if you want to print again all the stuff Peda shows to you (instructions, registers, stack, etc.), you command\u00a0<code>context<\/code>.<\/p>\n<h2>Final Thoughts<\/h2>\n<p>So today we\u2019ve seen a glimpse of GDB functionality. To sum up, I want you to take home 3 things:<\/p>\n<ul>\n<li>\n<p>use Peda or a different GDB plugin that suits you<\/p>\n<\/li>\n<li>\n<p>use \u2018break\u2019 and \u2018delete\u2019 commands to control breakpoints<\/p>\n<\/li>\n<li>\n<p>use \u2018x\u2019 command to print memory contents<\/p>\n<\/li>\n<\/ul>\n<p>If you have any further questions on GDB, please leave a comment below. Like the article, if you want more content like this. And happy hacking, you guys.<\/p>\n<p>As always, instead of reading this article, you can watch the full video (bonus outtakes and cats included):<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/606f18984bb7a5868986240e\" data-style=\"\" id=\"606f18984bb7a5868986240e\" width=\"\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/551500\/\"> https:\/\/habr.com\/ru\/articles\/551500\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>GDB is THE debugger for Linux programs. It\u2019s super powerful. But its user-friendliness or lack thereof can actually make you throw your PC out of the window. But what\u2019s important to understand about GDB is that GDB is not simply a tool, it\u2019s a debugging framework for you to build upon. In this video, I\u2019m gonna walk you through GDB setup for reverse engineering and show you all of the necessary commands and shortcuts for your debugging workflow.<\/p>\n<h2>GDB Setup With Plugins<\/h2>\n<p>The\u00a0most common mistake I see is that people perceive GDB as a standalone debugger tool. I suggest you think of GDB as a debugger framework that allows you to build your own tools. Or you can use premade tools. My GDB-based tool of choice is Peda. It\u2019s pretty easy to install, just follow installation instructions from Peda repository:\u00a0<a href=\"https:\/\/github.com\/longld\/peda\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/github.com\/longld\/peda<\/u><\/a><\/p>\n<pre><code>git clone https:\/\/github.com\/longld\/peda.git ~\/peda echo \"source ~\/peda\/peda.py\" >> ~\/.gdbinit<\/code><\/pre>\n<h2>Program example<\/h2>\n<p>I\u2019ll be using this simple program as our debugging target:<\/p>\n<pre><code>#include &lt;stdio.h>int main() {int x = 5;printf(\"x = %d\\n\", x);x = x + 5;printf(\"x = %d\", x);return 0;}<\/code><\/pre>\n<p>This code creates local variable X on the stack, prints its value to the console, then adds 5 to X, and prints its value again. Pretty simple, just what we need for our learning example.<\/p>\n<h2>First Steps with Debugging a Program<\/h2>\n<p>To open a file in GDB, type\u00a0<code>gdb [filename]<\/code>\u00a0in the console. GDB will open and load your file. Notice, that code execution hasn\u2019t started. That\u2019s because there\u2019s a separate command to start the execution, it\u2019s called\u00a0<code>run<\/code>, or\u00a0<code>r\u00a0<\/code>for short.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>And if we start it, our program successfully completes execution. That\u2019s because we haven\u2019t set any breakpoints yet. We can set a breakpoint using symbol name, for example,\u00a0<code>break main<\/code>, or using address: `break *0x555555555149`. You can print a list of all your breakpoints with \u2018info breakpoints\u2019 and delete them with\u00a0<code>delete &lt;breakpoint number><\/code>.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>Now if I issue\u00a0<code>run\u00a0<\/code>command, the execution conveniently stops at the beginning of the \u201cmain\u201d function. And just to save us some time we can use command\u00a0<code>start\u00a0<\/code>which comes from Peda instead of doing all of this. This command will do all this work for us.<\/p>\n<p>Of course, as with any other debugger, we can use single-stepping with command\u00a0<code>step\u00a0<\/code>or\u00a0<code>stepi<\/code>.<\/p>\n<p>And if you like to do single-stepping a lot, note, that pressing \u2018Return\u2019 on an empty line will make GDB execute the previous command you entered once more. Also, you can use command\u00a0<code>next\u00a0<\/code>to single step without entering function calls.<\/p>\n<p>To step through several instructions at once you can use command\u00a0<code>next &lt;number of instructions><\/code>.<\/p>\n<p>If you want to continue execution to a certain point in a program (for example to exit a loop), you can use command\u00a0<code>xuntil &lt;address or function><\/code>.<\/p>\n<h2>Modifying Registers<\/h2>\n<p>To modify registers, use following construction\u00a0<code>set$&lt;register name> = 1234<\/code>. For example, if I want to skip incrementing X, I can change the value of RIP register to 0x555555555176 with command\u00a0<code>set $RIP = 0x555555555176<\/code>.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>As you might have noticed, you can treat registers like variables in GDB. So I can assign RIP value of EAX with\u00a0<code>set $rip = $eax<\/code>\u00a0command. Now I can issue\u00a0<code>context<\/code>\u00a0command to make Peda reprint its beautiful context \u201cwindow\u201d to make sure that RIP points to some nonsense.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>And I want to start execution from the start of \u2018main\u2019 function I can just write\u00a0<code>set $rip = main<\/code>.<\/p>\n<p>By the way, with Peda you have a faster way to skip instructions without executing them with\u00a0<code>skipi<\/code>\u00a0command.<\/p>\n<h2>Modifying Memory<\/h2>\n<p>Modifying memory is similar to modifying registers in the sense that any memory location can be a variable in GDB. So in my example, I want to change the contents of the format string \u201cx = %d\u201d. This string is located at 0x555555556004 in my binary. I can use this address as a variable and type in the same command as with registers:\u00a0<code>set 0x555555556004 = \u201cAAAA%d\u201d<\/code>. But in this case, we\u2019ll see an error message. That\u2019s because you should always provide a variable type when modifying memory in GDB. So let me correct my command to\u00a0<code>set {char[7]}0x555555556004 = \u201cAAAA%d\u201d<\/code>.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h2>Print xprint hexdump hexprint<\/h2>\n<p>You can use the command\u00a0<code>x<\/code>\u00a0to examine memory. For example, if I want to print 20 8-byte words from the stack pointer, I\u2019ll use command\u00a0<code>x\/20xg $rsp<\/code>: 20 \u2014 the number of words, x \u2014 hexadecimal format, g \u2014 giant (8-byte words).<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>By changing the second\u00a0<code>x<\/code>\u00a0to\u00a0<code>i<\/code>\u00a0you can print memory as instructions.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>The full list of supported modifiers you can find in the documentation:<\/p>\n<blockquote>\n<p><em>From\u00a0<\/em><a href=\"https:\/\/sourceware.org\/gdb\/current\/onlinedocs\/gdb\/Memory.html\" rel=\"noopener noreferrer nofollow\"><em><u>https:\/\/sourceware.org\/gdb\/current\/onlinedocs\/gdb\/Memory.html<\/u><\/em><\/a><\/p>\n<p><em>You can use the command\u00a0<\/em><code>x<\/code><em>\u00a0(for \u201cexamine\u201d) to examine memory in any of several formats, independently of your program\u2019s data types.<\/em><\/p>\n<p><code>x\/nfu addrx addrx<\/code><em>\u00a0Use the\u00a0<\/em><code>x<\/code><em>\u00a0command to examine memory.<\/em><\/p>\n<p><em>n, f, and u are all optional parameters that specify how much memory to display and how to format it; addr is an expression giving the address where you want to start displaying memory. If you use defaults for nfu, you need not type the slash \u2018\/\u2019. Several commands set convenient defaults for addr.<\/em><\/p>\n<p><em>n, the repeat countThe repeat count is a decimal integer; the default is 1. It specifies how much memory (counting by units u) to display. If a negative number is specified, memory is examined backward from addr.f, the display formatThe display format is one of the formats used by\u00a0<\/em><code>print<\/code><em>\u00a0(\u2018x\u2019, \u2018d\u2019, \u2018u\u2019, \u2018o\u2019, \u2018t\u2019, \u2018a\u2019, \u2018c\u2019, \u2018f\u2019, \u2018s\u2019), and in addition \u2018i\u2019 (for machine instructions). The default is \u2018x\u2019 (hexadecimal) initially. The default changes each time you use either\u00a0<\/em><code>x<\/code><em>\u00a0or\u00a0<\/em><code>print<\/code><em>.u, the unit sizeThe unit size is any of<\/em><code>b<\/code><em>Bytes.<\/em><code>h<\/code><em>Halfwords (two bytes).<\/em><code>w<\/code><em>Words (four bytes). This is the initial default.<\/em><code>g<\/code><em>Giant words (eight bytes).<\/em><\/p>\n<\/blockquote>\n<p>Also, Peda provides you with a convenient\u00a0<code>hexdump address \/count<\/code>\u00a0(dump \u201ccount\u201d lines, 16-bytes each) command which you can use\u2026 well, to display hex dump.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>And if you want to print again all the stuff Peda shows to you (instructions, registers, stack, etc.), you command\u00a0<code>context<\/code>.<\/p>\n<h2>Final Thoughts<\/h2>\n<p>So today we\u2019ve seen a glimpse of GDB functionality. To sum up, I want you to take home 3 things:<\/p>\n<ul>\n<li>\n<p>use Peda or a different GDB plugin that suits you<\/p>\n<\/li>\n<li>\n<p>use \u2018break\u2019 and \u2018delete\u2019 commands to control breakpoints<\/p>\n<\/li>\n<li>\n<p>use \u2018x\u2019 command to print memory contents<\/p>\n<\/li>\n<\/ul>\n<p>If you have any further questions on GDB, please leave a comment below. Like the article, if you want more content like this. And happy hacking, you guys.<\/p>\n<p>As always, instead of reading this article, you can watch the full video (bonus outtakes and cats included):<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/606f18984bb7a5868986240e\" data-style=\"\" id=\"606f18984bb7a5868986240e\" width=\"\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/551500\/\"> https:\/\/habr.com\/ru\/articles\/551500\/<\/a><br \/><\/br><\/br><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-395866","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/395866","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=395866"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/395866\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=395866"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=395866"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=395866"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}