Browse Source

Fixing child memory write

Yann Weber 1 year ago
parent
commit
b639e6a5d6
5 changed files with 218 additions and 100 deletions
  1. 66
    62
      asm_env.c
  2. 12
    3
      asm_env.h
  3. 8
    32
      child.s
  4. 131
    3
      tests/tests_asm_env.c
  5. 1
    0
      tests/tests_compile.c

+ 66
- 62
asm_env.c View File

@@ -42,7 +42,7 @@ asmsh_env_t* asmsh_env(const char *childpath)
42 42
 	res->txt_map_addr = res->data_map_addr = res->stack_addr = NULL;
43 43
 	for(size_t i=0; i<res->mmap.size; i++)
44 44
 	{
45
-		if((res->mmap.maps[i].perm == (PROT_READ | PROT_EXEC)) &&
45
+		if((res->mmap.maps[i].perm == (PROT_READ | PROT_EXEC | PROT_WRITE)) &&
46 46
 			(res->mmap.maps[i].offset == 0) &&
47 47
 			strncmp("[vdso]", res->mmap.maps[i].pathname,
48 48
 					strlen("[vdso]")))
@@ -54,17 +54,6 @@ asmsh_env_t* asmsh_env(const char *childpath)
54 54
 			res->txt_map_addr = res->mmap.maps[i].start;
55 55
 			res->txt_map_sz = res->mmap.maps[i].stop - res->txt_map_addr;
56 56
 
57
-dprintf(2, "DEBUG : txtmap addr %p-%p (%dbytes) %d(%c%c%c)==%d '%s'\n",
58
-		res->txt_map_addr,
59
-		res->mmap.maps[i].stop,
60
-		res->txt_map_sz,
61
-		res->mmap.maps[i].perm,
62
-		(res->mmap.maps[i].perm & PROT_READ)?'r':'-',
63
-		(res->mmap.maps[i].perm & PROT_WRITE)?'w':'-',
64
-		(res->mmap.maps[i].perm & PROT_EXEC)?'x':'-',
65
-		PROT_READ|PROT_EXEC,
66
-		res->mmap.maps[i].pathname);
67
-
68 57
 		}
69 58
 		else if(res->mmap.maps[i].perm == PROT_READ | PROT_WRITE &&
70 59
 			res->mmap.maps[i].offset == 0)
@@ -90,7 +79,6 @@ dprintf(2, "DEBUG : txtmap addr %p-%p (%dbytes) %d(%c%c%c)==%d '%s'\n",
90 79
 			}
91 80
 		}
92 81
 	}
93
-dprintf(2, "Ptr %p\n", res->txt_map_addr);
94 82
 	res->txt_map_ptr = res->txt_map_addr;
95 83
 
96 84
 	res->code_write_ptr = res->txt_map_addr;
@@ -117,68 +105,84 @@ void asmsh_env_free(asmsh_env_t *asmenv)
117 105
 	free(asmenv);
118 106
 }
119 107
 
120
-int asmsh_env_write_code(asmsh_env_t *env, asmsh_bytecode_t *bcode)
121
-{
122
-	if(bcode->size < 8)
123
-	{
124
-		return asmsh_env_write_text(env, bcode->bytes, bcode->size);
125
-	}
126
-
127
-	if(asmsh_env_write_text(env, bcode->bytes, 8) < 0)
128
-	{
129
-		return -1;
130
-	}
131
-
132
-	if(bcode->size == 8)
133
-	{
134
-		return 0;
135
-	}
136
-	return asmsh_env_write_text(env, bcode->bytes + 8, bcode->size - 8);
137
-}
138
-
139
-
140
-int asmsh_env_write_text(asmsh_env_t *env, void *txt, size_t sz)
108
+int asmsh_env_write_mem(asmsh_env_t *env, void *addr, const unsigned char *buf, size_t buf_sz)
141 109
 {
142
-	uint64_t data;
143 110
 	int err;
144
-
145
-	if(sz > sizeof(data))
146
-	{
147
-		errno = EINVAL;
148
-		return -1;
111
+	u_int64_t data;
112
+	char written;
113
+	char bleft  = (u_int64_t)addr % 8;
114
+
115
+	written = 0;
116
+	if(bleft)
117
+	{
118
+		// First write to correct further write allignement
119
+		void *wr_addr = (void*)(((u_int64_t)addr / 8) * 8);
120
+		char towrite = 8 - bleft;
121
+		towrite = (towrite > buf_sz)?buf_sz:towrite;
122
+		
123
+		errno = 0;
124
+		data = ptrace(PTRACE_PEEKTEXT, env->pid, wr_addr, NULL);
125
+		if(errno)
126
+		{
127
+			err = errno;
128
+			perror("Unable to peektext in order to allign write");
129
+			errno = err;
130
+			return -1;
131
+		}
132
+		memcpy(((unsigned char*)&data)+bleft, &(buf[written]), towrite);
133
+		if(ptrace(PTRACE_POKETEXT, env->pid, wr_addr, data) < 0)
134
+		{
135
+			err = errno;
136
+			perror("Unable to poketext in order to allign write");
137
+			errno = err;
138
+			return -1;
139
+		}
140
+		written += towrite;
141
+		env->code_write_ptr += towrite;
149 142
 	}
150
-	if(sz == sizeof(data))
143
+	if(written >= buf_sz)
151 144
 	{
152
-		data = *(uint64_t*)txt;
145
+		return 0;
153 146
 	}
154
-	else
147
+	// env->code_write_ptr is now word alligned and "written" bytes are
148
+	// allready written
149
+	while(written < buf_sz)
155 150
 	{
156
-		data = 0;
157
-		memcpy(&data, txt, sz);
158
-	}
151
+		char towrite = buf_sz - written;
152
+		if(towrite >= 8)
153
+		{
154
+			data = *(u_int64_t*)(&buf[written]);
155
+		}
156
+		else
157
+		{
158
+			data = 0;
159
+			memcpy(&data, &buf[written], towrite);
160
+		}
159 161
 
160
-	if(ptrace(PTRACE_POKETEXT, env->pid, env->code_write_ptr, &data) < 0)
161
-	{
162
-		err = errno;
163
-		perror("Unable to ptrace poketext");
164
-		dprintf(2,"writing at %p map at %p rip %08X rax %08X\n",
165
-				env->code_write_ptr,
166
-				env->txt_map_addr,
167
-				env->regs.rip,
168
-				env->regs.rax);
169
-		errno = err;
170
-		return -1;
162
+		if(ptrace(PTRACE_POKETEXT, env->pid, env->code_write_ptr, data) < 0)
163
+		{
164
+			err = errno;
165
+			perror("Unable to poketext");
166
+			errno = err;
167
+			return -1;
168
+		}
169
+		written += towrite;
170
+		env->code_write_ptr += towrite;
171 171
 	}
172
+	return 0;
173
+}
172 174
 
173
-	env->code_write_ptr += sz;
174 175
 
175
-	return 0;
176
+int asmsh_env_write_code(asmsh_env_t *env, asmsh_bytecode_t *bcode)
177
+{
178
+	return asmsh_env_write_mem(env, env->code_write_ptr,
179
+			bcode->bytes, bcode->size);
176 180
 }
177 181
 
178 182
 
179 183
 int asmsh_env_step(asmsh_env_t *env, int *status)
180 184
 {
181
-	int err;
185
+	int err, ret;
182 186
 
183 187
 	if(status) { *status = 0; }
184 188
 
@@ -196,7 +200,7 @@ int asmsh_env_step(asmsh_env_t *env, int *status)
196 200
 	}
197 201
 	if(status) { *status = env->status; }
198 202
 
199
-	if(env->status != 1407)
203
+	if(!WIFSTOPPED(env->status))
200 204
 	{
201 205
 		goto err_wstatus;
202 206
 	}
@@ -356,7 +360,7 @@ static int _asmsh_env_spawn(asmsh_env_t *env, const char *childpath)
356 360
 	// WARNING totally depends on child.s source
357 361
 	// right now there is only 4 instructions (the fourth is the jmp to the txt_map)
358 362
 	// before reaching the start of the code mmap
359
-	for(int i=0; i<3; i++)
363
+	for(int i=0; i<4; i++)
360 364
 	{
361 365
 		/* // DEBUG to monitor placement in child exec
362 366
 		asmsh_env_update_regs(env);

+ 12
- 3
asm_env.h View File

@@ -67,6 +67,18 @@ asmsh_env_t* asmsh_env(const char *childpath);
67 67
 /** Free an env returned by @ref asmsh_env() */
68 68
 void asmsh_env_free(asmsh_env_t *asmenv);
69 69
 
70
+/** Write a buffer in tracee memory
71
+ *
72
+ * Handle un-alligned addr by reading tracee memory before writing if
73
+ * needed.
74
+ * @param asmsh_env_t* 
75
+ * @param void* The address in tracee memory
76
+ * @param const unsigned char* The buffer to write
77
+ * @param size_t number of bytes to write
78
+ * @return 0 if no errors else -1 with errno set
79
+ */
80
+int asmsh_env_write_mem(asmsh_env_t *env, void *addr, const unsigned char *buf, size_t buf_sz);
81
+
70 82
 /** Write bytecode at the next location available in the text map
71 83
  * @param asmsh_env_t* The run environment
72 84
  * @param asmsh_bytecode_t* The bytecode to copy
@@ -75,9 +87,6 @@ void asmsh_env_free(asmsh_env_t *asmenv);
75 87
  */
76 88
 int asmsh_env_write_code(asmsh_env_t *env, asmsh_bytecode_t *bcode);
77 89
 
78
-/** Write sz of txt data in text map */
79
-int asmsh_env_write_text(asmsh_env_t *env, void *txt, size_t sz);
80
-
81 90
 /** Run the next instruction
82 91
  * @param asmsh_env_t* The run environment
83 92
  * @param int* If not null will be set to the status of the child PID (error cases)

+ 8
- 32
child.s View File

@@ -1,18 +1,23 @@
1 1
 .file "child64.s"
2 2
 
3
+.comm map_len 8
4
+
3 5
 .section .text
4 6
 .global _start
5 7
 
6 8
 map_exec:
7 9
 
8 10
 _start:
9
-	pushq $0x1000 #  map size
11
+	movq $0x1000, (map_len)
12
+	lea redo(%rip), %rax
13
+	push %rax # redo addr
14
+	pushq $0x1000 # map size
10 15
 
11 16
 	mov $0x9, %rax # MMAP
12 17
 	xor %rdi, %rdi
13 18
 	mov (%rsp), %rsi # 1 page map
14 19
 	#mov $(0x1|0x2), %rdx # PROT_READ | PROT_WRITE
15
-	mov $(0x1|0x4), %rdx # PROT_READ | PROT_EXEC
20
+	mov $(0x1|0x4|0x2), %rdx # PROT_READ | PROT_EXEC | PROT_WRITE
16 21
 	mov $(0x20 | 0x1), %r10 # MAP_ANONYMOUS | MAP_SHARED
17 22
 	mov $-1, %r8 # fd
18 23
 	xor %r9, %r9
@@ -22,36 +27,7 @@ _start:
22 27
 	cmp $0, %rax
23 28
 	jle .errmap
24 29
 
25
-	mov $0xA, %rax # mprotect
26
-	mov (%rsp), %rdi # map addr
27
-	mov -8(%rsp), %rsi # map size
28
-	mov $(0x1|0x2), %rdx # PROT_READ | PROT_WRITE
29
-	syscall
30
-
31
-	####################
32
-	# TRACER STOP HERE #
33
-	#############################################################
34
-	# this is the point where you are able to write             #
35
-	# the map :                                                 #
36
-	# - start addr in (%rsp)                                    #
37
-	# - size in 8(%rsp)                                         #
38
-	# This informations can be fetched from /proc/[pid]/maps    #
39
-	# too                                                       #
40
-	#############################################################
41
-	# After this point you will be at 1 syscall (enter + exit)  #
42
-	# and 3 instructions (the third is the jump) from jumpexec  #
43
-	# in the map                                                #
44
-	#############################################################
45
-
46
-	mov $0xA, %rax # mprotect
47
-	mov (%rsp), %rdi # map start
48
-	mov -8(%rsp), %rsi # map size
49
-	mov $(0x1|0x4), %rdx # PROT_READ | PROT_EXEC
50
-	syscall
51
-	
52
-	cmp $0, %rax
53
-	jle .err
54
-
30
+	redo:
55 31
 	jmp *(%rsp)
56 32
 
57 33
 

+ 131
- 3
tests/tests_asm_env.c View File

@@ -57,7 +57,7 @@ START_TEST(test_env_update)
57 57
 }
58 58
 END_TEST
59 59
 
60
-START_TEST(test_env_steps)
60
+START_TEST(test_env_exit42)
61 61
 {
62 62
 	const char childpath[] = REAL_CHILD;
63 63
 
@@ -80,11 +80,13 @@ START_TEST(test_env_steps)
80 80
 
81 81
 	asmsh_asmc_ctx_free(cctx);
82 82
 
83
-	for(int i=0; i<3; i++)
83
+	for(int i=0; i<2; i++)
84 84
 	{
85 85
 		ret = asmsh_env_step(env, &status);
86 86
 		ck_assert_int_eq(ret, 0);
87
-		ck_assert_int_eq(status, 0);
87
+		ck_assert_msg(WCOREDUMP(status) ==  0, "SEGFAULT");
88
+		ck_assert_int_ne(WIFSTOPPED(status), 0);
89
+		ck_assert_int_eq(status>>8, 5);
88 90
 	}
89 91
 	ret = asmsh_env_step(env, &status);
90 92
 	ck_assert_int_eq(ret, 1);
@@ -96,10 +98,136 @@ START_TEST(test_env_steps)
96 98
 }
97 99
 END_TEST
98 100
 
101
+START_TEST(test_env_steps)
102
+{
103
+	const char childpath[] = REAL_CHILD;
104
+	const char *instr[] = {
105
+		"mov $60, %rax",
106
+		"mov $0x42,%rdi",
107
+		"syscall",
108
+	};
109
+
110
+	asmsh_asmc_ctx_t *cctx;
111
+	asmsh_bytecode_t bcode;
112
+	int ret, status, i;
113
+
114
+	asmsh_env_t *env = asmsh_env(childpath);
115
+	ck_assert_ptr_nonnull(env);
116
+
117
+	cctx = asmsh_asmc_ctx_default();
118
+	ck_assert_ptr_nonnull(cctx);
119
+
120
+
121
+	for(i=0; i<(sizeof(instr)/sizeof(*instr))-1; i++)
122
+	{
123
+		ck_assert_int_ge(asmsh_asmc_compile(cctx, instr[i], &bcode), 0);
124
+		ck_assert_int_ge(asmsh_env_write_code(env, &bcode), 0);
125
+		ret = asmsh_env_step(env, &status);
126
+		ck_assert_int_eq(ret, 0);
127
+		ck_assert_msg(WCOREDUMP(status) ==  0, "SEGFAULT");
128
+		ck_assert_int_ne(WIFSTOPPED(status), 0);
129
+		ck_assert_int_eq(status>>8, 5);
130
+	}
131
+	ck_assert_int_ge(asmsh_asmc_compile(cctx, instr[i], &bcode), 0);
132
+	ck_assert_int_ge(asmsh_env_write_code(env, &bcode), 0);
133
+	ret = asmsh_env_step(env, &status);
134
+	ck_assert_int_eq(ret, 1);
135
+	ck_assert(WIFEXITED(status));
136
+	ck_assert_int_eq(WEXITSTATUS(status), 0x42);
137
+
138
+	asmsh_asmc_ctx_free(cctx);
139
+	asmsh_env_free(env);
140
+
141
+}
142
+END_TEST
143
+
144
+START_TEST(test_env_regs)
145
+{
146
+	const char childpath[] = REAL_CHILD;
147
+	asmsh_env_t *env = asmsh_env(childpath);
148
+	ck_assert_ptr_nonnull(env);
149
+
150
+	asmsh_asmc_ctx_t *cctx;
151
+	asmsh_bytecode_t bcode;
152
+	int ret, status, i;
153
+	struct _instr_tests {
154
+		char *instr;
155
+		long long unsigned int *reg;
156
+		u_int64_t val;
157
+	} tinstr[] = {
158
+		{
159
+			.instr = "mov $42, %r15",
160
+			.reg = &env->regs.r15,
161
+			.val = 42,
162
+		},
163
+		{
164
+			.instr = "mov %r15, %rcx",
165
+			.reg = &env->regs.rcx,
166
+			.val = 42,
167
+		},
168
+		{
169
+			.instr = "mov $24, %rbx",
170
+			.reg = &env->regs.rbx,
171
+			.val = 24,
172
+		},
173
+		{
174
+			.instr = "add %rbx, %rcx",
175
+			.reg = &env->regs.rcx,
176
+			.val = 0x42,
177
+		},
178
+		{
179
+			.instr = "mov $60, %rax",
180
+			.reg = &env->regs.rax,
181
+			.val = 60,
182
+		},
183
+		{
184
+			.instr = "xor %rdi, %rdi",
185
+			.reg = &env->regs.rdi,
186
+			.val = 0,
187
+		},
188
+		{
189
+			.instr = "syscall",
190
+			.reg = NULL,
191
+			.val = 0,
192
+		},
193
+	};
194
+
195
+	cctx = asmsh_asmc_ctx_default();
196
+	ck_assert_ptr_nonnull(cctx);
197
+
198
+	for(i=0; i<(sizeof(tinstr)/sizeof(*tinstr))-1;i++)
199
+	{
200
+		ck_assert_int_ge(asmsh_asmc_compile(cctx, tinstr[i].instr, &bcode), 0);
201
+		ck_assert_int_ge(asmsh_env_write_code(env, &bcode), 0);
202
+		ret = asmsh_env_step(env, &status);
203
+		ck_assert_int_eq(ret, 0);
204
+		ck_assert_msg(WCOREDUMP(status) ==  0, "SEGFAULT");
205
+		ck_assert_int_ne(WIFSTOPPED(status), 0);
206
+		ck_assert_int_eq(status>>8, 5);
207
+		if(!tinstr[i].reg) { continue; }
208
+		asmsh_env_update_regs(env);
209
+		ck_assert_int_eq(*(tinstr[i].reg), tinstr[i].val);
210
+	}
211
+	ck_assert_int_ge(asmsh_asmc_compile(cctx, tinstr[i].instr, &bcode), 0);
212
+	ck_assert_int_ge(asmsh_env_write_code(env, &bcode), 0);
213
+	ret = asmsh_env_step(env, &status);
214
+	ck_assert_int_eq(ret, 1);
215
+	ck_assert(WIFEXITED(status));
216
+	ck_assert_int_eq(WEXITSTATUS(status), 0);
217
+
218
+	asmsh_asmc_ctx_free(cctx);
219
+	asmsh_env_free(env);
220
+
221
+}
222
+END_TEST
223
+
224
+
99 225
 ASMSH_CHECK_START("Testing asm env", "testing asm environment")
100 226
 	ASMSH_ADD_TEST(test_env);
101 227
 	ASMSH_ADD_TEST(test_asm_stamps);
102 228
 	ASMSH_ADD_TEST(test_env_update);
229
+	ASMSH_ADD_TEST(test_env_exit42);
103 230
 	ASMSH_ADD_TEST(test_env_steps);
231
+	ASMSH_ADD_TEST(test_env_regs);
104 232
 ASMSH_CHECK_END
105 233
 

+ 1
- 0
tests/tests_compile.c View File

@@ -153,6 +153,7 @@ typedef struct
153 153
 static const compile_sample_t comp_samples[] = {
154 154
 	{{"\x48\x83\xc0\x03\0", 4}, "add $3, %rax"},
155 155
 	{{"\x48\x89\xc3\0", 3}, "mov %rax, %rbx"},
156
+	{{"\xbf\x2a\x00\x00\x00", 5}, "mov $42, %rdi"},
156 157
 	{{"\x31\xc0\0", 2}, "xor %rax, %rax"},
157 158
 	{{"\x53\0", 1}, "push %rbx"},
158 159
 	{{"\x0f\x05\0", 2}, "syscall"},

Loading…
Cancel
Save