phpdbg
for fun and profitaharvey@aharvey-mbp:/tmp$ phpdbg code/fizzbuzz.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /tmp/code/fizzbuzz.php]
prompt> break fizzbuzz
[Breakpoint #0 added at fizzbuzz]
prompt> run 15
[Breakpoint #0 at /tmp/code/fizzbuzz.php:3, hits: 1]
>00003: if (0 == ($n % 3)) {
00004: return 'Fizz';
00005: } elseif (0 == ($n % 5)) {
prompt>
apt-get install php5-phpdbg
apt-get install php7.0-phpdbg
apt-get install php7.1-phpdbg
yum install php56-php-dbg
yum install php70-php-dbg
yum install php71-php-dbg
dnf install php-dbg
brew tap homebrew/php
brew install php70 --with-phpdbg
./configure --enable-phpdbg
aharvey@aharvey-mbp:/tmp$ phpdbg code/fizzbuzz.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /tmp/code/fizzbuzz.php]
prompt>
prompt> help
phpdbg is a lightweight, powerful and easy to use debugging
platform for PHP5.4+
It supports the following commands:
Information
list list PHP source
info displays information on the debug session
print show opcodes
frame select a stack frame and print a stack frame summary
back shows the current backtrace
help provide help on a topic
Starting and Stopping Execution
exec set execution context
run attempt execution
step continue execution until other line is reached
continue continue execution
until continue execution up to the given location
next continue execution up to the given location and halt on the first line after it
finish continue up to end of the current execution frame
leave continue up to end of the current execution frame and halt after the calling instruction
break set a breakpoint at the specified target
watch set a watchpoint on $variable
clear clear one or all breakpoints
clean clean the execution environment
Miscellaneous
set set the phpdbg configuration
source execute a phpdbginit script
register register a phpdbginit function as a command alias
sh shell a command
ev evaluate some code
quit exit phpdbg
Type help <command> or (help alias) to get detailed help on any of the above commands, for example help list or h l Note that help will also match partial commands if unique (and list out options if not unique), so help clea will give help on the clean command, but help cl will list the summary for clean and clear.
Type help aliases to show a full alias list, including any registered phpdginit functions
Type help syntax for a general introduction to the command syntax.
Type help options for a list of phpdbg command line options.
Type help phpdbginit to show how to customise the debugger environment.
prompt> help run
Command: run Alias: r attempt execution
Enter the vm, starting execution. Execution will then continue until the next
breakpoint or completion of the script. Add parameters you want to use as $argv
Examples
prompt> run
prompt> r
Will cause execution of the context, if it is set
prompt> r test
Will execute with $argv[1] == "test"
Note that the execution context must be set. If not previously compiled, then
the script will be compiled before execution.
Note that attempting to run a script that is already executing will result in
an "execution in progress" error.
prompt>
prompt> help run
Command: run Alias: r attempt execution
Enter the vm, starting execution. Execution will then
continue until the next breakpoint or completion of the
script. Add parameters you want to use as $argv
Examples
prompt> run
prompt> r
Will cause execution of the context, if it is set
prompt> r test
Will execute with $argv[1] == "test"
Note that the execution context must be set. If not
previously compiled, then the script will be compiled
before execution.
Note that attempting to run a script that is already
executing will result in an "execution in progress" error.
prompt> run 15
prompt> run 15
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 Fizz
[Script ended normally]
prompt>
function fizzbuzz(int $n): string {
if (0 == ($n % 3)) {
return 'Fizz';
} elseif (0 == ($n % 5)) {
return 'Buzz';
}
return (string) $n;
}
foreach (range(1, (int) $_SERVER['argv'][1]) as $n) {
echo fizzbuzz($n).' ';
}
echo "\n";
prompt> help break
Command: break Alias: b set breakpoint
Breakpoints can be set at a range of targets within the
execution environment. Execution will be paused if the
program flow hits a breakpoint.
prompt> break 3
[Breakpoint #0 added at /tmp/code/fizzbuzz.php:3]
prompt> break fizzbuzz.php:3
[Breakpoint #0 added at /tmp/code/fizzbuzz.php:3]
prompt> break fizzbuzz
[Breakpoint #1 added at fizzbuzz]
prompt> run 15
[Breakpoint #0 at /tmp/code/fizzbuzz.php:3, hits: 1]
>00003: if (0 == ($n % 3)) {
00004: return 'Fizz';
00005: } elseif (0 == ($n % 5)) {
prompt>
prompt> help ev
Command: ev Alias:
The ev command takes a string expression which it evaluates
and then displays. It evaluates in the context of the
lowest (that is the executing) frame, unless this has
first been explicitly changed by issuing a frame command.
prompt> ev $n
1
prompt>
prompt> continue
1
[Breakpoint #0 at /tmp/code/fizzbuzz.php:3, hits: 2]
>00003: if (0 == ($n % 3)) {
00004: return 'Fizz';
00005: } elseif (0 == ($n % 5)) {
prompt> ev $n
2
prompt>
Examples
prompt> break at phpdbg::isGreat if $opt == 'S'
prompt> break @ phpdbg::isGreat if $opt == 'S'
Break at any opcode in phpdbg::isGreat when the
condition ($opt == 'S') is true
prompt> break del 0
[Deleted breakpoint #0]
prompt> break at fizzbuzz if $n == 15
[Conditional breakpoint #1 added $n == 15/0x10a668300]
prompt>
prompt> continue
2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14
[Conditional breakpoint #1: at fizzbuzz if $n == 15 at
/tmp/code/fizzbuzz.php:3, hits: 1]
>00003: if (0 == ($n % 3)) {
00004: return 'Fizz';
00005: } elseif (0 == ($n % 5)) {
prompt> ev $n
15
prompt>
prompt> help step
Command: step Alias: s step through execution
Execute opcodes until next line
Examples
prompt> s
Will continue and break again in the next encountered
line
prompt> step
[L3 0x10a67a040 IS_EQUAL 0 ~0 ~1 /tmp/code/fizzbuzz.php]
[Conditional breakpoint #1: at fizzbuzz if $n == 15 at /tmp/code/fizzbuzz.php:3, hits: 2]
>00003: if (0 == ($n % 3)) {
00004: return 'Fizz';
00005: } elseif (0 == ($n % 5)) {
prompt>
prompt> step
[L4 0x10a67a080 RETURN "Fizz" /tmp/code/fizzbuzz.php]
>00004: return 'Fizz';
00005: } elseif (0 == ($n % 5)) {
00006: return 'Buzz';
prompt>
prompt> step
[L12 0x10a6801c0 CONCAT @6 " " ~7 /tmp/code/fizzbuzz.php]
>00012: echo fizzbuzz($n).' ';
00013: }
00014: echo "\n";
prompt>
class TableFlipException extends Exception {}
function (╯°□°)╯︵ ┻━┻() {
throw new TableFlipException('ruh roh');
}
(╯°□°)╯︵ ┻━┻();
aharvey@aharvey-mbp:/tmp$ phpdbg code/exception.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /tmp/code/exception.php]
prompt> r
[Uncaught TableFlipException in /tmp/code/exception.php on line 5: ruh roh]
>00005: throw new TableFlipException('ruh roh');
00006: }
00007:
prompt>
class TableFlipException extends Exception {}
function (╯°□°)╯︵ ┻━┻() {
throw new TableFlipException('ruh roh');
}
set_exception_handler(function ($e) { echo $e; });
(╯°□°)╯︵ ┻━┻();
aharvey@aharvey-mbp:/tmp$ phpdbg code/exception.php
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /tmp/code/exception.php]
prompt> r
[Uncaught TableFlipException in /tmp/code/exception.php on line 5: ruh roh]
>00005: throw new TableFlipException('ruh roh');
00006: }
00007:
prompt>
function (╯°□°)╯︵ ┻━┻() {
throw new TableFlipException('ruh roh');
}
prompt> print
[Stack in (╯°□°)╯︵ ┻━┻() (5 ops)]
L4-6 (╯°□°)╯︵ ┻━┻() /Users/aharvey/Trees/phpdbg-for-fun-and-profit/code/exception.php - 0x11206e180 + 5 ops
L5 #0 NEW "TableFlipException" @0
L5 #1 SEND_VAL_EX "ruh roh" 1
L5 #2 DO_FCALL
L5 #3 THROW @0
L6 #4 RETURN null
prompt>
LawnGnome/confoo-tweeps
root@57d38b20b3cb:/src# php artisan tweeps:update
Setting the name of LGnome to 'Adam Harvey'...
Setting the name of adamculp to Adam Culp...
Setting the name of afilina to Anna Filina...
Setting the name of serialseb to SerialSeb 🇪🇺🏳️🌈...
Setting the name of hannelita to Hanneli Tavante...
Setting the name of confooca to ConFoo Conference...
Setting the name of EliW to EliW...
Setting the name of beausimensen to Beau D. Simensen...
Setting the name of pjf to Paul Fenwick...
Muahahahahaha!
root@57d38b20b3cb:/src# php artisan tweeps:update
Setting the name of LGnome to 'Adam Harvey'...
[Illuminate\Database\QueryException]
SQLSTATE[42000]: Syntax error or access violation: 1064
You have an error in your SQL syntax; check the manual
that corresponds to your MariaDB server version for th
e right syntax to use near '; --')' at line 1 (SQL: REP
LACE INTO tweeps (account, name) VALUES ('LGnome', ''Ad
am Harvey''))
root@57d38b20b3cb:/src# phpdbg artisan
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /src/artisan]
prompt> run tweeps:update
Setting the name of LGnome to 'Adam Harvey'...
[Illuminate\Database\QueryException]
...
[Script ended normally]
prompt>
prompt> break ZEND_ADD
prompt> b ZEND_ADD
Break on any occurrence of the opcode ZEND_ADD
#define ZEND_THROW 108
prompt> break ZEND_THROW
[Breakpoint #0 added at ZEND_THROW]
prompt> run tweeps:update
Setting the name of LGnome to 'Adam Harvey'...
[Breakpoint #0 in ZEND_THROW at /src/vendor/laravel/framework/src/Illuminate/Database/Connection.php:714, hits: 1]
00713: throw new QueryException(
>00714: $query, $this->prepareBindings($bindings), $e
00715: );
00716: }
prompt>
prompt> help back
Command: back Alias: t show trace
Provide a formatted backtrace using the standard
debug_backtrace() functionality. An optional unsigned
integer argument specifying the maximum number of frames
to be traced; if omitted then a complete backtrace is
given.
prompt> back
...
frame #5: App\Console\Commands\UpdateTweeps->updateTweep(account="LGnome", name="'Adam Harvey'") at /src/app/Console/Commands/UpdateTweeps.php:62
...
prompt>
prompt> help frame
Command: frame Alias: f switch to a frame
The frame takes an optional integer argument. If omitted,
then the current frame is displayed If specified then the
current scope is set to the corresponding frame listed in
a back trace. This can be used to allowing access to the
variables in a higher stack frame than that currently
being executed.
prompt> frame 5
[Switched to frame #5]
>00062: DB::statement("REPLACE INTO tweeps (account, name) VALUES ('$account', '$name')");
00063: }
00064: }
prompt>
prompt> ev $name
'Adam Harvey'
prompt> ev $_ = "REPLACE INTO tweeps (account, name) VALUES ('$account', '$name')"
REPLACE INTO tweeps (account, name) VALUES ('LGnome', ''Adam Harvey'')
prompt>
$_SERVER = [
'HTTP_HOST' => 'localhost',
'HTTP_ACCEPT' => '...',
...
];
$_GET = [];
$_REQUEST = [];
$_POST = [];
$_COOKIE = [];
$_FILES = [];
chdir('public'); include 'index.php';
Source: http://phpdbg.com/docs/mocking-webserver
LawnGnome/phpdbg-fake-request
lawngnome/phpdbg-fake-request
<tr>
<td>nefarioushax0r</td>
<td><script>alert("I PWNED U NOOB LOLX0RZ")</script></td>
</tr>
http://127.0.0.1:9000/?page=2
root@57d38b20b3cb:/src# phpdbg ./vendor/bin/fake-request GET / public/index.php -g page=2
[Welcome to phpdbg, the interactive PHP debugger, v0.5.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://bugs.php.net/report.php>]
[Successful compilation of /src/vendor/lawngnome/phpdbg-fake-request/bin/fake-request]
prompt>
prompt> break routes.php:15
[Pending breakpoint #0 added at routes.php:15]
prompt>
prompt> run
[Breakpoint #0 at /src/app/Http/routes.php:15, hits: 1]
>00015: $tweeps = DB::table('tweeps')->paginate(15);
00016: ob_start();
00017:
prompt>
Route::get('/', function () {
$tweeps = DB::table('tweeps')->paginate(15);
ob_start();
?>
...
<?php foreach ($tweeps as $tweep): ?><tr>
<td><?= $tweep->account ?></td>
<td><?= $tweep->name ?></td>
</tr><?php endforeach ?>
...
<?php
return ob_get_clean();
});
prompt> break if strpos($tweep->name, 'alert')
[Conditional breakpoint #1 added strpos($tweep->name, 'alert')/0x7f46430a8460]
prompt> continue
[Conditional breakpoint #1: on strpos($tweep->name, 'alert') == true at /src/app/Http/routes.php:46, hits: 1]
>00046: <td><?= $tweep->account ?></td>
00047: <td><?= $tweep->name ?></td>
00048: </tr>
prompt>
prompt> ev $tweep
stdClass Object
(
[account] => nefarioushax0r
[name] => <script>alert("I PWNED U NOOB LOLX0RZ")</script>
)
prompt>
root@de345107bc84:/src# phpdbg -qrr vendor/bin/phpunit --color --coverage-html coverage
PHPUnit 5.3.4 by Sebastian Bergmann and contributors.
....................... 23 / 23 (100%)
Time: 1.45 seconds, Memory: 6.00MB
OK (23 tests, 29 assertions)
Generating code coverage report in HTML format ... done
Slides: https://lawngnome.github.io/phpdbg-for-fun-and-profit/