Added ReallocObserver(), and associated code as well.
[aesalon.git] / src / monitor / ptrace / Portal.cpp
bloba86cab3cf722095e5eef90f940397a4218e6b80d
1 #include <iostream>
2 #include <sys/user.h>
3 #include <sys/ptrace.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <unistd.h>
7 #include <errno.h>
8 #include <signal.h>
9 #include <cstring>
10 #include <iostream>
11 #include <fstream>
12 #include <sstream>
13 #include <climits>
15 #include "Portal.h"
16 #include "Initializer.h"
18 #include "ExitObserver.h"
19 #include "TrapObserver.h"
20 #include "SegfaultObserver.h"
21 #include "MallocObserver.h"
22 #include "FreeObserver.h"
23 #include "ReallocObserver.h"
24 #include "MainObserver.h"
25 #include "BreakpointReference.h"
27 #include "misc/String.h"
29 #include "Message.h"
31 namespace Aesalon {
32 namespace Monitor {
33 namespace PTrace {
35 Portal::Portal(Misc::SmartPointer<Platform::ArgumentList> argument_list) : pid(0), libc_offset(0) {
36 std::cout << "Portal::Portal(): called, initialization begun . . . \n";
37 pid = fork();
38 if(pid == -1)
39 throw PTraceException(Misc::StreamAsString() << "Forking to create child process failed: " << strerror(errno));
40 else if(pid == 0) {
41 ptrace(PTRACE_TRACEME, 0, NULL, NULL);
42 if(execv(argument_list->get_argument(0).c_str(), argument_list->get_as_argv()) == -1) {
43 throw PTraceException(Misc::StreamAsString() << "Failed to execute process: " << strerror(errno));
47 /*ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);*/
49 /* Trap signals are the most common, so add the TrapObserver on first. */
50 add_signal_observer(new TrapObserver());
51 add_signal_observer(new ExitObserver());
52 add_signal_observer(new SegfaultObserver());
54 /* This is a single-shot breakpoint observer, but since there's currently no way to remove observers, stick it on last. */
55 initial_observer = new MainObserver();
56 malloc_observer = new MallocObserver();
57 free_observer = new FreeObserver();
58 realloc_observer = new ReallocObserver();
60 std::cout << "Portal::Portal(): waiting for SIGTRAP call from child . . ." << std::endl;
61 /* Wait for the SIGTRAP that indicates a exec() call . . . */
62 wait_for_signal();
63 std::cout << "Portal::Portal(): got SIGTRAP call, placing breakpoint on main . . ." << std::endl;
64 /* place a breakpoint at main, for intialization purposes. */
65 Word main_address = Initializer::get_instance()->get_program_manager()->get_elf_parser()->get_symbol("main")->get_address();
66 place_breakpoint(main_address, initial_observer);
68 std::cout << "Portal::Portal(): continuing execution until breakpoint on main . . ." << std::endl;
69 /* Now continue until main(). */
70 continue_execution();
73 Word Portal::get_register(ASM::Register which) const {
74 struct user_regs_struct registers;
75 if(ptrace(PTRACE_GETREGS, pid, NULL, &registers) == -1)
76 throw PTraceException(Misc::StreamAsString() << "Couldn't get register values: " << strerror(errno));
78 switch(which) {
79 #if AESALON_PLATFORM == AESALON_PLATFORM_x86_64
80 case ASM::Register::RAX:
81 /*std::cout << "Value of RAX requested; RAX is " << registers.rax << ", ORIG_RAX is " << registers.orig_rax << std::endl;*/
82 return registers.rax;
83 case ASM::Register::RBX:
84 return registers.rbx;
85 case ASM::Register::RDI:
86 return registers.rdi;
87 case ASM::Register::RSI:
88 return registers.rsi;
89 case ASM::Register::RIP:
90 return registers.rip;
91 case ASM::Register::RBP:
92 return registers.rbp;
93 case ASM::Register::RSP:
94 return registers.rsp;
95 #endif
96 default:
97 throw PTraceException("Value of invalid register requested");
101 void Portal::set_register(ASM::Register which, Word new_value) {
102 struct user_regs_struct registers;
103 std::cout << "Portal::set_register() called . . .\n";
104 if(ptrace(PTRACE_GETREGS, pid, NULL, &registers) == -1)
105 throw PTraceException(Misc::StreamAsString() << "Couldn't get register values: " << strerror(errno));
107 switch(which) {
108 #if AESALON_PLATFORM == AESALON_PLATFORM_x86_64
109 case ASM::Register::RIP:
110 std::cout << "\tSetting RIP to " << std::hex << new_value << std::dec << std::endl;
111 registers.rip = new_value;
112 break;
113 #endif
114 default:
115 throw PTraceException("Asked to set value of invalid register");
117 if(ptrace(PTRACE_SETREGS, pid, NULL, &registers) == -1)
118 throw PTraceException(Misc::StreamAsString() << "Couldn't set register values: " << strerror(errno));
121 Word Portal::read_memory(Platform::MemoryAddress address) const {
122 std::cout << "Portal::read_memory() called . . ." << std::endl;
123 std::cout << "\tReading address " << std::hex << address << std::endl;
124 Word return_value = ptrace(PTRACE_PEEKDATA, pid, address, NULL);
125 if(return_value == Word(-1) && errno != 0)
126 throw PTraceException(Misc::StreamAsString() << "Couldn't read memory: " << strerror(errno));
127 return return_value;
130 void Portal::write_memory(Platform::MemoryAddress address, Word value) {
131 std::cout << "Portal::write_memory(address, Word) called . . ." << std::endl;
132 std::cout << "\tWriting " << std::hex <<value << " to " << address << std::endl;
133 if(ptrace(PTRACE_POKEDATA, pid, address, value) == -1)
134 throw PTraceException(Misc::StreamAsString() << "Couldn't write memory: " << strerror(errno));
137 void Portal::write_memory(Platform::MemoryAddress address, Byte value) {
138 std::cout << "Portal::write_memory(address, Byte) called . . ." << std::endl;
139 std::cout << "\tWriting 0x" << std::hex << (int)value << " to " << address << std::endl;
140 Word word_offset = 0;
141 word_offset = address & 0x07;
142 Word current_value = read_memory(address - word_offset);
144 std::cout << "\tUtilizing write_memory(address, Word); word_offset is: " << word_offset << std::endl;
145 std::cout << "\tBefore byte clearing, current_value is: " << current_value << std::endl;
146 current_value &= ~(Word(0xff) << (word_offset * CHAR_BIT));
147 std::cout << "\tAfter byte clearing, current_value is: " << current_value << std::endl;
148 current_value |= (Word(value) << (word_offset * CHAR_BIT));
149 std::cout << "\tAfter byte insertion, current_value is: " << current_value << std::endl;
150 write_memory(address - word_offset, current_value);
153 void Portal::attach() {
154 ptrace(PTRACE_ATTACH, pid, NULL, NULL);
157 void Portal::detach() {
158 ptrace(PTRACE_DETACH, pid, NULL, NULL);
161 std::size_t Portal::place_breakpoint(Platform::MemoryAddress address, Misc::SmartPointer<BreakpointObserver> observer) {
162 std::cout << "Portal::place_breakpoint() called . . ." << std::endl;
163 std::cout << "\tPlacing breakpoint at " << std::hex << address << std::dec << std::endl;
164 Misc::SmartPointer<Breakpoint> bp = get_breakpoint_by_address(address);
165 if(!bp.is_valid()) {
166 Byte original = read_memory(address) & 0xff;
167 bp = new Breakpoint(address, original);
168 add_breakpoint(bp);
169 write_memory(address, bp->get_breakpoint_character());
171 bp->add_observer(observer);
172 std::cout << "\tplaced breakpoint #" << bp->get_id() << std::endl;
173 return bp->get_id();
176 void Portal::remove_breakpoint(Platform::MemoryAddress address) {
177 std::cout << "Portal::remove_breakpoint() called . . ." << std::endl;
178 std::cout << "\tRemoving breakpoint at " << std::hex << address << std::dec << std::endl;
180 Misc::SmartPointer<Breakpoint> bp = get_breakpoint_by_address(address);
181 if(!bp.is_valid()) {
182 std::cout << "\tAsked to remove non-existent breakpoint!" << std::endl;
183 return;
185 Byte original = bp->get_original();
186 write_memory(bp->get_address(), original);
187 for(breakpoint_list_t::iterator i = breakpoint_list.begin(); i != breakpoint_list.end(); i ++) {
188 if((*i)->get_id() == bp->get_id()) {
189 breakpoint_list.erase(i);
190 break;
195 Misc::SmartPointer<Breakpoint> Portal::get_breakpoint_by_id(std::size_t which) const {
196 for(breakpoint_list_t::const_iterator i = breakpoint_list.begin(); i != breakpoint_list.end(); i ++) {
197 if((*i)->get_id() == which) return *i;
199 return NULL;
202 Misc::SmartPointer<Breakpoint> Portal::get_breakpoint_by_address(Platform::MemoryAddress address) const {
203 for(breakpoint_list_t::const_iterator i = breakpoint_list.begin(); i != breakpoint_list.end(); i ++) {
204 if((*i)->get_address() == address) return *i;
206 return NULL;
210 void Portal::handle_signal() {
211 int signal;
212 int status = wait_for_signal();
213 if(!WIFEXITED(status) && WIFSTOPPED(status)) {
214 siginfo_t signal_info;
215 if(ptrace(PTRACE_GETSIGINFO, pid, NULL, &signal_info) == -1) {
216 throw PTraceException(Misc::StreamAsString() << "Failed to get signal information: " << strerror(errno));
218 signal = signal_info.si_signo;
220 else signal = -1;
222 std::cout << std::dec;
224 std::cout << "Portal::handle_signal(): status: (" << status << "): ";
226 for(int mask = 1 << 20; mask > 0; mask >>= 1) {
227 std::cout << ((status & mask)?"1":"0");
230 std::cout << ", signal: " << signal << std::endl;
232 for(signal_observer_list_t::iterator i = signal_observer_list.begin(); i != signal_observer_list.end(); i ++) {
233 if((*i)->handle_signal(signal, status)) return;
235 std::cout << "\twarning: unhandled signal!" << std::endl;
238 void Portal::continue_execution(int signal) {
239 std::cout << "Portal::continue_execution() called . . ." << std::endl;
240 if(ptrace(PTRACE_CONT, pid, NULL, NULL) == -1)
241 throw PTraceException(Misc::StreamAsString() << "Couldn't continue program execution: " << strerror(errno));
242 std::cout << "\tExecution continued." << std::endl;
245 void Portal::single_step() {
246 std::cout << "Portal::single_step() called . . ." << std::endl;
247 if(ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) == -1)
248 throw PTraceException(Misc::StreamAsString() << "Couldn't single-step program:" << strerror(errno));
249 wait_for_signal(); /* ptrace(PTRACE_SINGLESTEP throws a SIGTRAP when it's done, so wait for it. */
250 std::cout << "\tSingle-step successful. " << std::endl;
253 int Portal::wait_for_signal() {
254 std::cout << "Portal::wait_for_signal() called . . ." << std::endl;
255 int status;
256 if(waitpid(pid, &status, 0) == -1) throw PTraceException(Misc::StreamAsString() << "Couldn't waitpid() on child: " << strerror(errno));
257 std::cout << "\tGot signal." << std::endl;
258 return status;
261 void Portal::handle_breakpoint() {
262 std::cout << "Portal::handle_breakpoint() called . . ." << std::endl;
263 Word ip = get_register(
264 #if AESALON_PLATFORM == AESALON_PLATFORM_x86_64
265 ASM::Register::RIP
266 #elif AESALON_PLATFORM == AESALON_PLATFORM_x86
267 ASM::Register::EIP
268 #endif
270 /* Subtract one from the IP, since the 0xcc SIGTRAP instruction was executed . . . */
271 ip --;
272 Misc::SmartPointer<Breakpoint> breakpoint = get_breakpoint_by_address(ip);
273 std::cout << "\tIP: " << std::hex << ip << std::dec << std::endl;
274 if(!breakpoint.is_valid()) {
275 /*Message(Message::DEBUG_MESSAGE, "handle_breakpoint() called on non-breakpoint");*/
276 return;
278 Message(Message::DEBUG_MESSAGE, "handle_breakpoint() found a breakpoint . . .");
280 /* ip is currently ($rip - 1), to use gdb notation. In other words, back up one byte. */
281 set_register(ASM::Register::RIP, ip);
283 breakpoint->notify();
285 /* Write out the original instruction . . . */
286 write_memory(breakpoint->get_address(), breakpoint->get_original());
287 /* Single-step over that instruction . . . */
288 single_step();
289 /* Only re-write out the trap instruction if the breakpoint is still valid. */
290 if(breakpoint->is_valid()) {
291 /* Re-write out the trap instruction . . . */
292 write_memory(breakpoint->get_address(), breakpoint->get_breakpoint_character());
294 else {
295 remove_breakpoint(breakpoint->get_address());
297 /* And then TrapObserver will call continue_execution(). */
300 Word Portal::get_libc_offset() {
301 if(libc_offset != 0) return libc_offset;
302 libc_offset = 0;
304 std::string map_file;
305 map_file = Misc::StreamAsString() << "/proc/" << pid << "/maps";
306 std::ifstream map_stream(map_file.c_str());
307 if(!map_stream.is_open()) throw PTraceException(Misc::StreamAsString() << "Couldn't open " << map_file << ", perhaps permissions are screwy?");
309 /*std::cout << "Memory map: " << std::endl;*/
311 /* Example memory map excerpt (from bash):
312 00400000-004d5000 r-xp 00000000 08:06 71555 /bin/bash
313 006d4000-006de000 rw-p 000d4000 08:06 71555 /bin/bash
314 006de000-006e3000 rw-p 00000000 00:00 0
315 015ba000-01a44000 rw-p 00000000 00:00 0 [heap]
316 7f7698328000-7f769832a000 r-xp 00000000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
317 7f769832a000-7f7698529000 ---p 00002000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
318 7f7698529000-7f769852a000 r--p 00001000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
319 7f769852a000-7f769852b000 rw-p 00002000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
322 /* Get one line at a time, for EOF detection purposes. */
324 char buffer[1024];
325 while(!map_stream.eof() && map_stream.getline(buffer, 1024, '\n') && std::strlen(buffer)) {
326 Word from, to;
327 char spacer;
328 Word offset;
329 std::string mode, device, inode, path;
331 std::stringstream line_stream;
332 line_stream << buffer;
333 line_stream >> std::hex >> from;
334 line_stream >> spacer;
335 line_stream >> std::hex >> to;
336 line_stream >> mode;
337 line_stream >> std::hex >> offset;
338 line_stream >> device;
339 line_stream >> inode;
340 line_stream >> path;
341 /*std::cout << "\tLibrary path is \"" << path << "\"\n";
342 std::cout << "\tAddress range is: 0x" << std::hex << from << " to 0x" << to << " (size 0x" << to-from << ")" << std::endl;
343 std::cout << "\tMap mode is: \"" << mode << "\"\n";
344 std::cout << std::dec << std::endl;*/
345 if(Misc::String::begins_with(path, "/lib/libc")) {
346 if(mode == "r-xp") libc_offset = from;
350 map_stream.close();
352 return libc_offset;
355 } // namespace PTrace
356 } // namespace Monitor
357 } // namespace Aesalon