Browse Source

Implements a .breakpoint command

Yann Weber 1 year ago
parent
commit
53d2b40af9
6 changed files with 303 additions and 5 deletions
  1. 3
    2
      Makefile.am
  2. 3
    0
      asm_env.c
  3. 5
    0
      asm_env.h
  4. 7
    3
      asmsh.h
  5. 279
    0
      shell_cmd_breakpoint.c
  6. 6
    0
      shell_cmds.h

+ 3
- 2
Makefile.am View File

@@ -1,8 +1,9 @@
1 1
 bin_PROGRAMS = asmsh child
2 2
 
3 3
 libcheck_asmsh_a_SOURCES = mmap_parse.c asm_env.c breakpoints.c compile.c logger.c \
4
-			   completion.c shell.c shell_cmds.c shell_sym.c \
5
-			   history.c
4
+			   completion.c history.c \
5
+			   shell.c shell_cmds.c shell_sym.c \
6
+			   shell_cmd_breakpoint.c
6 7
 
7 8
 asmsh_SOURCES = asmsh.c $(libcheck_asmsh_a_SOURCES)
8 9
 child_SOURCES = child.s

+ 3
- 0
asm_env.c View File

@@ -39,6 +39,8 @@ asmsh_env_t* asmsh_env(const char *childpath)
39 39
 	}
40 40
 	child_mmap_init(&(res->mmap));
41 41
 
42
+	asmsh_brk_init(&res->brks);
43
+
42 44
 	res->childpath = NULL;
43 45
 	if(childpath && (res->childpath = strdup(childpath)) == NULL)
44 46
 	{
@@ -86,6 +88,7 @@ void asmsh_env_free(asmsh_env_t *asmenv)
86 88
 		free(asmenv->mmap.maps);
87 89
 	}
88 90
 	kill(asmenv->pid, SIGKILL);
91
+	asmsh_brk_free(&asmenv->brks);
89 92
 	free(asmenv->childpath);
90 93
 	free(asmenv);
91 94
 }

+ 5
- 0
asm_env.h View File

@@ -27,6 +27,7 @@
27 27
 
28 28
 #include "mmap_parse.h"
29 29
 #include "compile.h"
30
+#include "breakpoints.h"
30 31
 
31 32
 ///! Initial size of the child's memory map with PROT_EXEC permission
32 33
 #define ASMSH_CHILD_TEXT_MAP_SZ 0x1000 // defined in child.s
@@ -56,6 +57,10 @@ struct asmsh_env_s
56 57
 	/** Pointer on current write addr in child's map */
57 58
 	unsigned char *txt_map_ptr;
58 59
 
60
+	/** List of breakpoints addresses in child's memory */
61
+	asmsh_brk_t brks;
62
+
63
+	/** @todo check & delete useless properties */
59 64
 	/** Pointer on the next addr where we should write some
60 65
 	 *  compiled bytecode */
61 66
 	unsigned char *code_write_ptr;

+ 7
- 3
asmsh.h View File

@@ -8,7 +8,7 @@ A shell designed to run assembly (for the moment only x86_64 is supported).
8 8
 A simple programm is spawned by the shell, and each instructions are runned in the
9 9
 subprocess environment.
10 10
 
11
-@section UI
11
+@section man_ui USER INTERFACE
12 12
 
13 13
 For the moment, the UI is implemented using GNU readline with basic support for
14 14
 completion (using tab).
@@ -16,6 +16,9 @@ completion (using tab).
16 16
 The prompt is composed like "asmsh@RIPVAL > " where RIPVAL is the RIP register (
17 17
 Instruction Pointer ) value in hexadecimal.
18 18
 
19
+Other readline(3) features are available like up arrow to display previous commands,
20
+C^R for history search, etc.
21
+
19 22
 @section INSTRUCTIONS
20 23
 
21 24
 The shell uses the GNU as compiler from binutils, the instructions syntax
@@ -109,12 +112,13 @@ asmsh@0x7f6e312e5022 >
109 112
 @section TODO TODOLIST
110 113
 
111 114
 @todo Implement breakpoints
115
+@todo Implement command for memory read/dump
112 116
 @todo Implement symbols for jumps
117
+@todo Add support for label declarations & references
113 118
 @todo Implement write without exec
114 119
 @todo Implement function declaration
115
-@todo Implement command for memory read/dump
116 120
 @todo Add switch between intel's & AT&T's syntaxes.
117
-@todo Add support for label declarations & references
121
+@todo Rationalise commands argument parsing
118 122
 
119 123
 @section AUTHOR
120 124
 Written by Yann Weber <yann.weber@members.fsf.org>

+ 279
- 0
shell_cmd_breakpoint.c View File

@@ -0,0 +1,279 @@
1
+/* Copyright Yann Weber <asmsh@yannweb.net>
2
+   This file is part of asmsh.
3
+
4
+   asmsh is free software: you can redistribute it and/or modify it under the
5
+   terms of the GNU General Public License as published by the Free Software
6
+   Foundation, either version 3 of the License, or any later version.
7
+   
8
+   asmsh is distributed in the hope that it will be useful, but WITHOUT ANY
9
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10
+   FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
+   details.
12
+   
13
+   You should have received a copy of the GNU General Public License along
14
+   with asmsh. If not, see <https://www.gnu.org/licenses/>.
15
+*/
16
+#include "shell_cmds.h"
17
+
18
+/** Add a breakpoint
19
+ * @param asmsh_t* The shell
20
+ * @param asmsh_cmd_args_t* The command arguments
21
+ * @param int The first argument of the expression
22
+ * @return -1 on error else 0
23
+ */
24
+static int brk_add(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
25
+
26
+/** Remove a breakpoint
27
+ * @param asmsh_t* The shell
28
+ * @param asmsh_cmd_args_t* The command arguments
29
+ * @param int The first argument of the expression
30
+ * @return -1 on error else 0
31
+ */
32
+static int brk_del(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
33
+
34
+/** List all breakpoints
35
+ * @param asmsh_t* The shell
36
+ * @param asmsh_cmd_args_t* The command arguments
37
+ * @param int The first argument of the expression
38
+ * @return -1 on error else 0
39
+ * @todo add filters arguments
40
+ */
41
+static int brk_ls(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first);
42
+
43
+/** Parse a breakpoint address expression
44
+ *
45
+ * An expression must be of the form A [OP B] with A and B
46
+ * an integer or '.' for current RIP value and OP one of + or -
47
+ *
48
+ * @param asmsh_cmd_args_t* The breakpoint command argument list
49
+ * @param int The first argument of the address expression
50
+ * @param const unsigned long RIP value
51
+ * @param unsigned long* A pointer on the result address
52
+ *
53
+ * @returns -1 on error and 0 if ok
54
+ */
55
+static int parse_addr_expr(asmsh_cmd_args_t *args, int first,
56
+		const unsigned long rip,  unsigned long *addr);
57
+
58
+/** Utility function for @ref parse_addr_expr() that parse an expression value.
59
+ *
60
+ * An expression can be . for rip value or an unsigned long, possibly in hex, etc
61
+ * @param const char* The value
62
+ * @param unsigned long The value of RIP
63
+ * @param unsigned long* A pointer on the result
64
+ * @return -1 on error else 0
65
+ */
66
+static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res);
67
+
68
+
69
+void log_brk_usage()
70
+{
71
+	asmsh_log_info("Available commands are : add, a, del, d, rm, list, ls, l");
72
+}
73
+
74
+void log_expr_usage()
75
+{
76
+	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)");
77
+}
78
+
79
+
80
+int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args)
81
+{
82
+	int ret;
83
+
84
+	if(args->argc == 0 || args->args[0][0] == '.' || \
85
+			(args->args[0][0] >= '0' && args->args[0][0] <= '9'))
86
+	{
87
+		if((ret = brk_add(sh, args, 0)) < 0)
88
+		{
89
+			log_expr_usage();
90
+		}
91
+		return ret;
92
+	}
93
+	// At least 1 arg, 1st arg chr is not in [0-9] nor is '.'
94
+	// Looking for specific commands argument
95
+	if(strcmp(args->args[0], "a") == 0 || \
96
+			strcmp(args->args[0], "add") == 0)
97
+	{
98
+		if((ret = brk_add(sh, args, 1)) < 0)
99
+		{
100
+			log_expr_usage();
101
+		}
102
+		return ret;
103
+	}
104
+	else if(strcmp(args->args[0], "d") == 0 || \
105
+			strcmp(args->args[0], "del") == 0 || \
106
+			strcmp(args->args[0], "rm") == 0)
107
+	{
108
+		ret = brk_del(sh, args, 1);
109
+		return ret;
110
+	}
111
+	else if(strcmp(args->args[0], "list") == 0 || \
112
+			strcmp(args->args[0], "ls") == 0 || \
113
+			strcmp(args->args[0], "l") == 0)
114
+	{
115
+		ret = brk_ls(sh, args, 1);
116
+		return ret;
117
+	}
118
+	else
119
+	{
120
+		asmsh_log_error("Unrecognized action '%s'", args->args[0]);
121
+		log_brk_usage();
122
+	}
123
+	return -1;
124
+}
125
+
126
+static int brk_add(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
127
+{
128
+	const unsigned long rip = sh->env->regs.rip;
129
+
130
+	unsigned long addr;
131
+
132
+	if(parse_addr_expr(args, expr_first, rip, &addr) < 0)
133
+	{
134
+		return -1;
135
+	}
136
+
137
+	int ret = asmsh_brk_add(&sh->env->brks, addr);
138
+	if(ret < 0)
139
+	{
140
+		int err = errno;
141
+		asmsh_log_error("Unable to set breakpoint @ %016lX : %s",
142
+				addr, strerror(errno));
143
+		errno = err;
144
+		return -1;
145
+	}
146
+	else if(ret > 0)
147
+	{
148
+		asmsh_log_warning("Breakpoint @ %016lX allready set", addr);
149
+		return -1;
150
+	}
151
+	asmsh_log_info("Set breakpoint @ %016lX", addr);
152
+	return 0;
153
+}
154
+
155
+static int brk_del(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
156
+{
157
+	const unsigned long rip = sh->env->regs.rip;
158
+
159
+	unsigned long addr;
160
+
161
+	if(parse_addr_expr(args, expr_first, rip, &addr) < 0)
162
+	{
163
+		return -1;
164
+	}
165
+
166
+	int ret = asmsh_brk_del(&sh->env->brks, addr);
167
+	if(ret < 0)
168
+	{
169
+		int err = errno;
170
+		asmsh_log_error("Unable to remove breakpoint @ %016lX : %s",
171
+				addr, strerror(errno));
172
+		errno = err;
173
+		return -1;
174
+	}
175
+	else if(ret > 0)
176
+	{
177
+		asmsh_log_warning("Breakpoint @ %016lX do not exists", addr);
178
+		return -1;
179
+	}
180
+	asmsh_log_info("Removed breakpoint @ %016lX", addr);
181
+	return 0;
182
+}
183
+
184
+
185
+static int brk_ls(asmsh_t *sh, asmsh_cmd_args_t *args, int expr_first)
186
+{
187
+	for(size_t i=0; i<sh->env->brks.sz; i++)
188
+	{
189
+		printf("0x%016lx\n", sh->env->brks.addrs[i]);
190
+	}
191
+	printf("%ld breakpoints\n", sh->env->brks.sz);
192
+	return 0;
193
+}
194
+
195
+static int addr_expr_val(const char *val, unsigned long rip, unsigned long *res)
196
+{
197
+	if(strcmp(".", val) == 0)
198
+	{
199
+		*res = rip;
200
+		return 0;
201
+	}
202
+
203
+	char *endptr;
204
+	errno = 0;
205
+	*res = strtoul(val, &endptr, 0);
206
+	if(errno != 0)
207
+	{
208
+		return -1;
209
+	}
210
+	else if(*endptr != '\0')
211
+	{
212
+		errno = EINVAL;
213
+		return -1;
214
+	}
215
+	return 0;
216
+}
217
+
218
+static int parse_addr_expr(asmsh_cmd_args_t *args, int first,
219
+		const unsigned long rip, unsigned long *addr)
220
+{
221
+	char * const * argv = args->args + first;
222
+	const int argc = args->argc - first;
223
+
224
+	unsigned long val[2];
225
+
226
+	switch(argc)
227
+	{
228
+		case 0:
229
+			*addr = rip;
230
+			break;
231
+		case 1:
232
+			if(addr_expr_val(argv[0], rip, addr) < 0)
233
+			{
234
+				int err = errno;
235
+				asmsh_log_error("Invalid expression value '%s': %s",
236
+						argv[0], strerror(errno));
237
+				errno = err;
238
+				return -1;
239
+			}
240
+			break;
241
+		case 3:
242
+
243
+			for(int i=0; i<2; i++)
244
+			{
245
+				const char *arg = i ? argv[2]:argv[0];
246
+				if(addr_expr_val(arg, rip, &val[i]) < 0)
247
+				{
248
+
249
+					asmsh_log_error("Invalid value '%s' in expression '%s %s %s' : %s",
250
+							arg,
251
+							argv[0], argv[1], argv[2],
252
+							strerror(errno));
253
+					return -1;
254
+				}
255
+			}
256
+
257
+			const char operator = strlen(argv[1]) == 1 ? argv[1][0]: '\0';
258
+			switch(operator)
259
+			{
260
+				case '+':
261
+					*addr = val[0] + val[1];
262
+					break;
263
+				case '-':
264
+					*addr = val[0] - val[1];
265
+					break;
266
+				default:
267
+					asmsh_log_error("Invalid operator '%s' in expression '%s %s %s'",
268
+							argv[1],
269
+							argv[0], argv[1], argv[2]);
270
+					return -1;
271
+			}
272
+			break;
273
+		default:
274
+			//USAGE !
275
+			asmsh_log_error("Unexexpected argument count for an expression. Expecting one of 0, 1 or 3 but got %d", argc);
276
+			return -1;
277
+	}
278
+	return 0;
279
+}

+ 6
- 0
shell_cmds.h View File

@@ -91,6 +91,9 @@ const char *asmsh_cmd_help(asmsh_t *sh);
91 91
 // Quit the shell
92 92
 int asmsh_cmd_quit(asmsh_t *sh, asmsh_cmd_args_t *args);
93 93
 
94
+// Defined in @file shell_cmd_breakpoint.c
95
+int asmsh_cmd_breakpoint(asmsh_t *sh, asmsh_cmd_args_t *args);
96
+
94 97
 // Print an instruction bytecode 
95 98
 int asmsh_cmd_bcode_(asmsh_t *sh, char *buf, int bufsz, int argc, char **args);
96 99
 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);
116 119
  * The list of shell commands
117 120
  */
118 121
 static const asmsh_cmd_t asmsh_CMDS[] = {
122
+	{".breakpoint", asmsh_cmd_breakpoint, 3,
123
+	 ".br(eakpoint)", "[addr]",
124
+	 "Set a breakpoint"},
119 125
 	{".bytecode", asmsh_cmd_bcode, 2,
120 126
 	 ".b(ytecode)", "",
121 127
 	 "display last instruction bytecode"},

Loading…
Cancel
Save