Implements a .breakpoint command
This commit is contained in:
parent
0ed105117d
commit
53d2b40af9
6 changed files with 303 additions and 5 deletions
|
|
@ -1,8 +1,9 @@
|
|||
bin_PROGRAMS = asmsh child
|
||||
|
||||
libcheck_asmsh_a_SOURCES = mmap_parse.c asm_env.c breakpoints.c compile.c logger.c \
|
||||
completion.c shell.c shell_cmds.c shell_sym.c \
|
||||
history.c
|
||||
completion.c history.c \
|
||||
shell.c shell_cmds.c shell_sym.c \
|
||||
shell_cmd_breakpoint.c
|
||||
|
||||
asmsh_SOURCES = asmsh.c $(libcheck_asmsh_a_SOURCES)
|
||||
child_SOURCES = child.s
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ asmsh_env_t* asmsh_env(const char *childpath)
|
|||
}
|
||||
child_mmap_init(&(res->mmap));
|
||||
|
||||
asmsh_brk_init(&res->brks);
|
||||
|
||||
res->childpath = NULL;
|
||||
if(childpath && (res->childpath = strdup(childpath)) == NULL)
|
||||
{
|
||||
|
|
@ -86,6 +88,7 @@ void asmsh_env_free(asmsh_env_t *asmenv)
|
|||
free(asmenv->mmap.maps);
|
||||
}
|
||||
kill(asmenv->pid, SIGKILL);
|
||||
asmsh_brk_free(&asmenv->brks);
|
||||
free(asmenv->childpath);
|
||||
free(asmenv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "mmap_parse.h"
|
||||
#include "compile.h"
|
||||
#include "breakpoints.h"
|
||||
|
||||
///! Initial size of the child's memory map with PROT_EXEC permission
|
||||
#define ASMSH_CHILD_TEXT_MAP_SZ 0x1000 // defined in child.s
|
||||
|
|
@ -56,6 +57,10 @@ struct asmsh_env_s
|
|||
/** Pointer on current write addr in child's map */
|
||||
unsigned char *txt_map_ptr;
|
||||
|
||||
/** List of breakpoints addresses in child's memory */
|
||||
asmsh_brk_t brks;
|
||||
|
||||
/** @todo check & delete useless properties */
|
||||
/** Pointer on the next addr where we should write some
|
||||
* compiled bytecode */
|
||||
unsigned char *code_write_ptr;
|
||||
|
|
|
|||
10
asmsh.h
10
asmsh.h
|
|
@ -8,7 +8,7 @@ A shell designed to run assembly (for the moment only x86_64 is supported).
|
|||
A simple programm is spawned by the shell, and each instructions are runned in the
|
||||
subprocess environment.
|
||||
|
||||
@section UI
|
||||
@section man_ui USER INTERFACE
|
||||
|
||||
For the moment, the UI is implemented using GNU readline with basic support for
|
||||
completion (using tab).
|
||||
|
|
@ -16,6 +16,9 @@ completion (using tab).
|
|||
The prompt is composed like "asmsh@RIPVAL > " where RIPVAL is the RIP register (
|
||||
Instruction Pointer ) value in hexadecimal.
|
||||
|
||||
Other readline(3) features are available like up arrow to display previous commands,
|
||||
C^R for history search, etc.
|
||||
|
||||
@section INSTRUCTIONS
|
||||
|
||||
The shell uses the GNU as compiler from binutils, the instructions syntax
|
||||
|
|
@ -109,12 +112,13 @@ asmsh@0x7f6e312e5022 >
|
|||
@section TODO TODOLIST
|
||||
|
||||
@todo Implement breakpoints
|
||||
@todo Implement command for memory read/dump
|
||||
@todo Implement symbols for jumps
|
||||
@todo Add support for label declarations & references
|
||||
@todo Implement write without exec
|
||||
@todo Implement function declaration
|
||||
@todo Implement command for memory read/dump
|
||||
@todo Add switch between intel's & AT&T's syntaxes.
|
||||
@todo Add support for label declarations & references
|
||||
@todo Rationalise commands argument parsing
|
||||
|
||||
@section AUTHOR
|
||||
Written by Yann Weber <yann.weber@members.fsf.org>
|
||||
|
|
|
|||
279
shell_cmd_breakpoint.c
Normal file
279
shell_cmd_breakpoint.c
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/* Copyright Yann Weber <asmsh@yannweb.net>
|
||||
This file is part of asmsh.
|
||||
|
||||
asmsh is free software: you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or any later version.
|
||||
|
||||
asmsh is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with asmsh. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "shell_cmds.h"
|
||||
|
||||
/** Add a breakpoint
|
||||
* @param asmsh_t* The shell
|
||||
* @param asmsh_cmd_args_t* The command arguments
|
||||
* @param int The first argument of the expression
|
||||
* @return -1 on error else 0
|
||||
*/
|
||||
static int brk_add(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
|
||||
|
||||
/** Remove a breakpoint
|
||||
* @param asmsh_t* The shell
|
||||
* @param asmsh_cmd_args_t* The command arguments
|
||||
* @param int The first argument of the expression
|
||||
* @return -1 on error else 0
|
||||
*/
|
||||
static int brk_del(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
|
||||
|
||||
/** List all breakpoints
|
||||
* @param asmsh_t* The shell
|
||||
* @param asmsh_cmd_args_t* The command arguments
|
||||
* @param int The first argument of the expression
|
||||
* @return -1 on error else 0
|
||||
* @todo add filters arguments
|
||||
*/
|
||||
static int brk_ls(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
|
||||
|
||||
/** Parse a breakpoint address expression
|
||||
*
|
||||
* An expression must be of the form A [OP B] with A and B
|
||||
* an integer or '.' for current RIP value and OP one of + or -
|
||||
*
|
||||
* @param asmsh_cmd_args_t* The breakpoint command argument list
|
||||
* @param int The first argument of the address expression
|
||||
* @param const unsigned long RIP value
|
||||
* @param unsigned long* A pointer on the result address
|
||||
*
|
||||
* @returns -1 on error and 0 if ok
|
||||
*/
|
||||
static int parse_addr_expr(asmsh_cmd_args_t *args, int first,
|
||||
const unsigned long rip, unsigned long *addr);
|
||||
|
||||
/** Utility function for @ref parse_addr_expr() that parse an expression value.
|
||||
*
|
||||
* An expression can be . for rip value or an unsigned long, possibly in hex, etc
|
||||
* @param const char* The value
|
||||
* @param unsigned long The value of RIP
|
||||
* @param unsigned long* A pointer on the result
|
||||
* @return -1 on error else 0
|
||||
*/
|
||||
static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res);
|
||||
|
||||
|
||||
void log_brk_usage()
|
||||
{
|
||||
asmsh_log_info("Available commands are : add, a, del, d, rm, list, ls, l");
|
||||
}
|
||||
|
||||
void log_expr_usage()
|
||||
{
|
||||
asmsh_log_info("Bad argument, expected an expression of the form A + B or A - B with A and B an integer or '.' (for current RIP value)");
|
||||
}
|
||||
|
||||
|
||||
int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if(args->argc == 0 || args->args[0][0] == '.' || \
|
||||
(args->args[0][0] >= '0' && args->args[0][0] <= '9'))
|
||||
{
|
||||
if((ret = brk_add(sh, args, 0)) < 0)
|
||||
{
|
||||
log_expr_usage();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// At least 1 arg, 1st arg chr is not in [0-9] nor is '.'
|
||||
// Looking for specific commands argument
|
||||
if(strcmp(args->args[0], "a") == 0 || \
|
||||
strcmp(args->args[0], "add") == 0)
|
||||
{
|
||||
if((ret = brk_add(sh, args, 1)) < 0)
|
||||
{
|
||||
log_expr_usage();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else if(strcmp(args->args[0], "d") == 0 || \
|
||||
strcmp(args->args[0], "del") == 0 || \
|
||||
strcmp(args->args[0], "rm") == 0)
|
||||
{
|
||||
ret = brk_del(sh, args, 1);
|
||||
return ret;
|
||||
}
|
||||
else if(strcmp(args->args[0], "list") == 0 || \
|
||||
strcmp(args->args[0], "ls") == 0 || \
|
||||
strcmp(args->args[0], "l") == 0)
|
||||
{
|
||||
ret = brk_ls(sh, args, 1);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
asmsh_log_error("Unrecognized action '%s'", args->args[0]);
|
||||
log_brk_usage();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int brk_add(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
|
||||
{
|
||||
const unsigned long rip = sh->env->regs.rip;
|
||||
|
||||
unsigned long addr;
|
||||
|
||||
if(parse_addr_expr(args, expr_first, rip, &addr) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = asmsh_brk_add(&sh->env->brks, addr);
|
||||
if(ret < 0)
|
||||
{
|
||||
int err = errno;
|
||||
asmsh_log_error("Unable to set breakpoint @ %016lX : %s",
|
||||
addr, strerror(errno));
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
else if(ret > 0)
|
||||
{
|
||||
asmsh_log_warning("Breakpoint @ %016lX allready set", addr);
|
||||
return -1;
|
||||
}
|
||||
asmsh_log_info("Set breakpoint @ %016lX", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brk_del(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
|
||||
{
|
||||
const unsigned long rip = sh->env->regs.rip;
|
||||
|
||||
unsigned long addr;
|
||||
|
||||
if(parse_addr_expr(args, expr_first, rip, &addr) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = asmsh_brk_del(&sh->env->brks, addr);
|
||||
if(ret < 0)
|
||||
{
|
||||
int err = errno;
|
||||
asmsh_log_error("Unable to remove breakpoint @ %016lX : %s",
|
||||
addr, strerror(errno));
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
else if(ret > 0)
|
||||
{
|
||||
asmsh_log_warning("Breakpoint @ %016lX do not exists", addr);
|
||||
return -1;
|
||||
}
|
||||
asmsh_log_info("Removed breakpoint @ %016lX", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int brk_ls(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
|
||||
{
|
||||
for(size_t i=0; i<sh->env->brks.sz; i++)
|
||||
{
|
||||
printf("0x%016lx\n", sh->env->brks.addrs[i]);
|
||||
}
|
||||
printf("%ld breakpoints\n", sh->env->brks.sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res)
|
||||
{
|
||||
if(strcmp(".", val) == 0)
|
||||
{
|
||||
*res = rip;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
errno = 0;
|
||||
*res = strtoul(val, &endptr, 0);
|
||||
if(errno != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if(*endptr != '\0')
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_addr_expr(asmsh_cmd_args_t *args, int first,
|
||||
const unsigned long rip, unsigned long *addr)
|
||||
{
|
||||
char * const * argv = args->args + first;
|
||||
const int argc = args->argc - first;
|
||||
|
||||
unsigned long val[2];
|
||||
|
||||
switch(argc)
|
||||
{
|
||||
case 0:
|
||||
*addr = rip;
|
||||
break;
|
||||
case 1:
|
||||
if(addr_expr_val(argv[0], rip, addr) < 0)
|
||||
{
|
||||
int err = errno;
|
||||
asmsh_log_error("Invalid expression value '%s': %s",
|
||||
argv[0], strerror(errno));
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
|
||||
for(int i=0; i<2; i++)
|
||||
{
|
||||
const char *arg = i ? argv[2]:argv[0];
|
||||
if(addr_expr_val(arg, rip, &val[i]) < 0)
|
||||
{
|
||||
|
||||
asmsh_log_error("Invalid value '%s' in expression '%s %s %s' : %s",
|
||||
arg,
|
||||
argv[0], argv[1], argv[2],
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char operator = strlen(argv[1]) == 1 ? argv[1][0]: '\0';
|
||||
switch(operator)
|
||||
{
|
||||
case '+':
|
||||
*addr = val[0] + val[1];
|
||||
break;
|
||||
case '-':
|
||||
*addr = val[0] - val[1];
|
||||
break;
|
||||
default:
|
||||
asmsh_log_error("Invalid operator '%s' in expression '%s %s %s'",
|
||||
argv[1],
|
||||
argv[0], argv[1], argv[2]);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//USAGE !
|
||||
asmsh_log_error("Unexexpected argument count for an expression. Expecting one of 0, 1 or 3 but got %d", argc);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -91,6 +91,9 @@ const char *asmsh_cmd_help(asmsh_t *sh);
|
|||
// Quit the shell
|
||||
int asmsh_cmd_quit(asmsh_t *sh, asmsh_cmd_args_t *args);
|
||||
|
||||
// Defined in @file shell_cmd_breakpoint.c
|
||||
int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args);
|
||||
|
||||
// Print an instruction bytecode
|
||||
int asmsh_cmd_bcode_(asmsh_t *sh, char *buf, int bufsz, int argc, char **args);
|
||||
int asmsh_cmd_bcode(asmsh_t *sh, asmsh_cmd_args_t *args);
|
||||
|
|
@ -116,6 +119,9 @@ int asmsh_cmd_help_(asmsh_t *sh, asmsh_cmd_args_t *args);
|
|||
* The list of shell commands
|
||||
*/
|
||||
static const asmsh_cmd_t asmsh_CMDS[] = {
|
||||
{".breakpoint", asmsh_cmd_breakpoint, 3,
|
||||
".br(eakpoint)", "[addr]",
|
||||
"Set a breakpoint"},
|
||||
{".bytecode", asmsh_cmd_bcode, 2,
|
||||
".b(ytecode)", "",
|
||||
"display last instruction bytecode"},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue