3 #include <sys/ptrace.h>
16 #include "Initializer.h"
18 #include "ExitObserver.h"
19 #include "TrapObserver.h"
20 #include "SegfaultObserver.h"
21 #include "MallocObserver.h"
22 #include "MainObserver.h"
23 #include "BreakpointReference.h"
25 #include "misc/String.h"
33 Portal::Portal(Misc::SmartPointer
<Platform::ArgumentList
> argument_list
) : pid(0), libc_offset(0) {
34 std::cout
<< "Portal::Portal(): called, initialization begun . . . \n";
37 throw PTraceException(Misc::StreamAsString() << "Forking to create child process failed: " << strerror(errno
));
39 ptrace(PTRACE_TRACEME
, 0, NULL
, NULL
);
40 if(execv(argument_list
->get_argument(0).c_str(), argument_list
->get_as_argv()) == -1) {
41 throw PTraceException(Misc::StreamAsString() << "Failed to execute process: " << strerror(errno
));
45 /*ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);*/
47 /* Trap signals are the most common, so add the TrapObserver on first. */
48 add_signal_observer(new TrapObserver());
49 add_signal_observer(new ExitObserver());
50 add_signal_observer(new SegfaultObserver());
52 /* This is a single-shot breakpoint observer, but since there's currently no way to remove observers, stick it on last. */
53 initial_observer
= new MainObserver();
54 malloc_observer
= new MallocObserver();
56 std::cout
<< "Portal::Portal(): waiting for SIGTRAP call from child . . ." << std::endl
;
57 /* Wait for the SIGTRAP that indicates a exec() call . . . */
59 std::cout
<< "Portal::Portal(): got SIGTRAP call, placing breakpoint on main . . ." << std::endl
;
60 /* place a breakpoint at main, for intialization purposes. */
61 Word main_address
= Initializer::get_instance()->get_program_manager()->get_elf_parser()->get_symbol("main")->get_address();
62 place_breakpoint(main_address
, initial_observer
);
64 std::cout
<< "Portal::Portal(): continuing execution until breakpoint on main . . ." << std::endl
;
65 /* Now continue until main(). */
69 Word
Portal::get_register(ASM::Register which
) const {
70 struct user_regs_struct registers
;
71 if(ptrace(PTRACE_GETREGS
, pid
, NULL
, ®isters
) == -1)
72 throw PTraceException(Misc::StreamAsString() << "Couldn't get register values: " << strerror(errno
));
75 #if AESALON_PLATFORM == AESALON_PLATFORM_x86_64
76 case ASM::Register::RAX
:
77 /*std::cout << "Value of RAX requested; RAX is " << registers.rax << ", ORIG_RAX is " << registers.orig_rax << std::endl;*/
79 case ASM::Register::RBX
:
81 case ASM::Register::RDI
:
83 case ASM::Register::RIP
:
85 case ASM::Register::RBP
:
87 case ASM::Register::RSP
:
91 throw PTraceException("Value of invalid register requested");
95 void Portal::set_register(ASM::Register which
, Word new_value
) {
96 struct user_regs_struct registers
;
97 std::cout
<< "Portal::set_register() called . . .\n";
98 if(ptrace(PTRACE_GETREGS
, pid
, NULL
, ®isters
) == -1)
99 throw PTraceException(Misc::StreamAsString() << "Couldn't get register values: " << strerror(errno
));
102 #if AESALON_PLATFORM == AESALON_PLATFORM_x86_64
103 case ASM::Register::RIP
:
104 std::cout
<< "\tSetting RIP to " << std::hex
<< new_value
<< std::dec
<< std::endl
;
105 registers
.rip
= new_value
;
109 throw PTraceException("Asked to set value of invalid register");
111 if(ptrace(PTRACE_SETREGS
, pid
, NULL
, ®isters
) == -1)
112 throw PTraceException(Misc::StreamAsString() << "Couldn't set register values: " << strerror(errno
));
115 Word
Portal::read_memory(Platform::MemoryAddress address
) const {
116 std::cout
<< "Portal::read_memory() called . . ." << std::endl
;
117 std::cout
<< "\tReading address " << std::hex
<< address
<< std::endl
;
118 Word return_value
= ptrace(PTRACE_PEEKDATA
, pid
, address
, NULL
);
119 if(return_value
== Word(-1) && errno
!= 0)
120 throw PTraceException(Misc::StreamAsString() << "Couldn't read memory: " << strerror(errno
));
124 void Portal::write_memory(Platform::MemoryAddress address
, Word value
) {
125 std::cout
<< "Portal::write_memory(address, Word) called . . ." << std::endl
;
126 std::cout
<< "\tWriting " << std::hex
<<value
<< " to " << address
<< std::endl
;
127 if(ptrace(PTRACE_POKEDATA
, pid
, address
, value
) == -1)
128 throw PTraceException(Misc::StreamAsString() << "Couldn't write memory: " << strerror(errno
));
131 void Portal::write_memory(Platform::MemoryAddress address
, Byte value
) {
132 std::cout
<< "Portal::write_memory(address, Byte) called . . ." << std::endl
;
133 std::cout
<< "\tWriting 0x" << std::hex
<< (int)value
<< " to " << address
<< std::endl
;
134 Word word_offset
= 0;
135 word_offset
= address
& 0x07;
136 Word current_value
= read_memory(address
- word_offset
);
138 std::cout
<< "\tUtilizing write_memory(address, Word); word_offset is: " << word_offset
<< std::endl
;
139 std::cout
<< "\tBefore byte clearing, current_value is: " << current_value
<< std::endl
;
140 current_value
&= ~(Word(0xff) << (word_offset
* CHAR_BIT
));
141 std::cout
<< "\tAfter byte clearing, current_value is: " << current_value
<< std::endl
;
142 current_value
|= (Word(value
) << (word_offset
* CHAR_BIT
));
143 std::cout
<< "\tAfter byte insertion, current_value is: " << current_value
<< std::endl
;
144 write_memory(address
- word_offset
, current_value
);
147 void Portal::attach() {
148 ptrace(PTRACE_ATTACH
, pid
, NULL
, NULL
);
151 void Portal::detach() {
152 ptrace(PTRACE_DETACH
, pid
, NULL
, NULL
);
155 std::size_t Portal::place_breakpoint(Platform::MemoryAddress address
, Misc::SmartPointer
<BreakpointObserver
> observer
) {
156 std::cout
<< "Portal::place_breakpoint() called . . ." << std::endl
;
157 std::cout
<< "\tPlacing breakpoint at " << std::hex
<< address
<< std::dec
<< std::endl
;
158 Misc::SmartPointer
<Breakpoint
> bp
= get_breakpoint_by_address(address
);
160 Byte original
= read_memory(address
) & 0xff;
161 bp
= new Breakpoint(address
, original
);
163 write_memory(address
, bp
->get_breakpoint_character());
165 bp
->add_observer(observer
);
169 void Portal::remove_breakpoint(Platform::MemoryAddress address
) {
170 std::cout
<< "Portal::remove_breakpoint() called . . ." << std::endl
;
171 std::cout
<< "\tRemoving breakpoint at " << std::hex
<< address
<< std::dec
<< std::endl
;
173 Misc::SmartPointer
<Breakpoint
> bp
= get_breakpoint_by_address(address
);
175 std::cout
<< "\tAsked to remove non-existent breakpoint!" << std::endl
;
178 Byte original
= bp
->get_original();
179 write_memory(bp
->get_address(), original
);
180 for(breakpoint_list_t::iterator i
= breakpoint_list
.begin(); i
!= breakpoint_list
.end(); i
++) {
181 if((*i
)->get_id() == bp
->get_id()) {
182 breakpoint_list
.erase(i
);
188 Misc::SmartPointer
<Breakpoint
> Portal::get_breakpoint_by_id(std::size_t which
) const {
189 for(breakpoint_list_t::const_iterator i
= breakpoint_list
.begin(); i
!= breakpoint_list
.end(); i
++) {
190 if((*i
)->get_id() == which
) return *i
;
195 Misc::SmartPointer
<Breakpoint
> Portal::get_breakpoint_by_address(Platform::MemoryAddress address
) const {
196 for(breakpoint_list_t::const_iterator i
= breakpoint_list
.begin(); i
!= breakpoint_list
.end(); i
++) {
197 if((*i
)->get_address() == address
) return *i
;
203 void Portal::handle_signal() {
205 int status
= wait_for_signal();
206 if(!WIFEXITED(status
) && WIFSTOPPED(status
)) {
207 siginfo_t signal_info
;
208 if(ptrace(PTRACE_GETSIGINFO
, pid
, NULL
, &signal_info
) == -1) {
209 throw PTraceException(Misc::StreamAsString() << "Failed to get signal information: " << strerror(errno
));
211 signal
= signal_info
.si_signo
;
215 std::cout
<< std::dec
;
217 std::cout
<< "Portal::handle_signal(): status: (" << status
<< "): ";
219 for(int mask
= 1 << 20; mask
> 0; mask
>>= 1) {
220 std::cout
<< ((status
& mask
)?"1":"0");
223 std::cout
<< ", signal: " << signal
<< std::endl
;
225 for(signal_observer_list_t::iterator i
= signal_observer_list
.begin(); i
!= signal_observer_list
.end(); i
++) {
226 if((*i
)->handle_signal(signal
, status
)) return;
228 std::cout
<< "\twarning: unhandled signal!" << std::endl
;
231 void Portal::continue_execution(int signal
) {
232 std::cout
<< "Portal::continue_execution() called . . ." << std::endl
;
233 if(ptrace(PTRACE_CONT
, pid
, NULL
, NULL
) == -1)
234 throw PTraceException(Misc::StreamAsString() << "Couldn't continue program execution: " << strerror(errno
));
235 std::cout
<< "\tExecution continued." << std::endl
;
238 void Portal::single_step() {
239 std::cout
<< "Portal::single_step() called . . ." << std::endl
;
240 if(ptrace(PTRACE_SINGLESTEP
, pid
, NULL
, NULL
) == -1)
241 throw PTraceException(Misc::StreamAsString() << "Couldn't single-step program:" << strerror(errno
));
242 wait_for_signal(); /* ptrace(PTRACE_SINGLESTEP throws a SIGTRAP when it's done, so wait for it. */
243 std::cout
<< "\tSingle-step successful. " << std::endl
;
246 int Portal::wait_for_signal() {
247 std::cout
<< "Portal::wait_for_signal() called . . ." << std::endl
;
249 if(waitpid(pid
, &status
, 0) == -1) throw PTraceException(Misc::StreamAsString() << "Couldn't waitpid() on child: " << strerror(errno
));
250 std::cout
<< "\tGot signal." << std::endl
;
254 void Portal::handle_breakpoint() {
255 std::cout
<< "Portal::handle_breakpoint() called . . ." << std::endl
;
256 Word ip
= get_register(
257 #if AESALON_PLATFORM == AESALON_PLATFORM_x86_64
259 #elif AESALON_PLATFORM == AESALON_PLATFORM_x86
263 /* Subtract one from the IP, since the 0xcc SIGTRAP instruction was executed . . . */
265 Misc::SmartPointer
<Breakpoint
> breakpoint
= get_breakpoint_by_address(ip
);
266 std::cout
<< "\tIP: " << std::hex
<< ip
<< std::dec
<< std::endl
;
267 if(!breakpoint
.is_valid()) {
268 /*Message(Message::DEBUG_MESSAGE, "handle_breakpoint() called on non-breakpoint");*/
271 Message(Message::DEBUG_MESSAGE
, "handle_breakpoint() found a breakpoint . . .");
273 /* ip is currently ($rip - 1), to use gdb notation. In other words, back up one byte. */
274 set_register(ASM::Register::RIP
, ip
);
276 breakpoint
->notify();
278 /* Write out the original instruction . . . */
279 write_memory(breakpoint
->get_address(), breakpoint
->get_original());
280 /* Single-step over that instruction . . . */
282 /* Only re-write out the trap instruction if the breakpoint is still valid. */
283 if(breakpoint
->is_valid()) {
284 /* Re-write out the trap instruction . . . */
285 write_memory(breakpoint
->get_address(), breakpoint
->get_breakpoint_character());
288 remove_breakpoint(breakpoint
->get_address());
290 /* And then TrapObserver will call continue_execution(). */
293 Word
Portal::get_libc_offset() {
294 if(libc_offset
!= 0) return libc_offset
;
297 std::string map_file
;
298 map_file
= Misc::StreamAsString() << "/proc/" << pid
<< "/maps";
299 std::ifstream
map_stream(map_file
.c_str());
300 if(!map_stream
.is_open()) throw PTraceException(Misc::StreamAsString() << "Couldn't open " << map_file
<< ", perhaps permissions are screwy?");
302 /*std::cout << "Memory map: " << std::endl;*/
304 /* Example memory map excerpt (from bash):
305 00400000-004d5000 r-xp 00000000 08:06 71555 /bin/bash
306 006d4000-006de000 rw-p 000d4000 08:06 71555 /bin/bash
307 006de000-006e3000 rw-p 00000000 00:00 0
308 015ba000-01a44000 rw-p 00000000 00:00 0 [heap]
309 7f7698328000-7f769832a000 r-xp 00000000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
310 7f769832a000-7f7698529000 ---p 00002000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
311 7f7698529000-7f769852a000 r--p 00001000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
312 7f769852a000-7f769852b000 rw-p 00002000 08:06 218888 /usr/lib/gconv/ISO8859-1.so
315 /* Get one line at a time, for EOF detection purposes. */
318 while(!map_stream
.eof() && map_stream
.getline(buffer
, 1024, '\n') && std::strlen(buffer
)) {
322 std::string mode
, device
, inode
, path
;
324 std::stringstream line_stream
;
325 line_stream
<< buffer
;
326 line_stream
>> std::hex
>> from
;
327 line_stream
>> spacer
;
328 line_stream
>> std::hex
>> to
;
330 line_stream
>> std::hex
>> offset
;
331 line_stream
>> device
;
332 line_stream
>> inode
;
334 /*std::cout << "\tLibrary path is \"" << path << "\"\n";
335 std::cout << "\tAddress range is: 0x" << std::hex << from << " to 0x" << to << " (size 0x" << to-from << ")" << std::endl;
336 std::cout << "\tMap mode is: \"" << mode << "\"\n";
337 std::cout << std::dec << std::endl;*/
338 if(Misc::String::begins_with(path
, "/lib/libc")) {
339 if(mode
== "r-xp") libc_offset
= from
;
348 } // namespace PTrace
349 } // namespace Monitor
350 } // namespace Aesalon