Example #1
0
//------------------------------------------------------------------------------
// Name: read_bytes(edb::address_t address, void *buf, std::size_t len)
// Desc: reads <len> bytes into <buf> starting at <address>
// Note: if the read failed, the part of the buffer that could not be read will
//       be filled with 0xff bytes
//------------------------------------------------------------------------------
bool DebuggerCore::read_bytes(edb::address_t address, void *buf, std::size_t len) {

	Q_CHECK_PTR(buf);

	bool ok = false;

	if(attached()) {

		if(len == 0) {
			return true;
		}

		memset(buf, 0xff, len);

		// might wanna make this more platform specific (e.g. Windows x86 user mode memory <= 0x7FFFFFFF)
		const edb::address_t max_address = std::numeric_limits<edb::address_t>::max();

		/*
		// I think we can safely assume this won't happen as long as
		// max_address is the biggest representable number ;)
		if(address > max_address || len > max_address) {
			return false;
		}
		*/

		edb::address_t cur_address = address;
		edb::address_t end_address;

		// check for max possible address (and overflow :s)
		// took a few hours to find that bug
		if(overflows<edb::address_t>(address, len, max_address)) {
			end_address = max_address;
		} else {
			end_address = address + len - 1;
		}

		len = end_address - address + 1;

		const MemoryRegions& regions = edb::v1::memory_regions();

		while(cur_address <= end_address) {
			bool part_ok = false;

			void* cur_dest = reinterpret_cast<quint8 *>(buf) + (cur_address - address);
			edb::address_t cur_end;
			std::size_t cur_len;

			MemRegion mem;
			if(regions.find_region(cur_address, mem)) {
				bool changed = false;
				if(!mem.readable()) {
					mem.set_permissions(true, mem.writable(), mem.executable());
					changed = true;
				}

				// special cases: first and last region (with unaligned address or end_address)
				if(overflows<edb::address_t>(mem.start, mem.size(), end_address)) {
					cur_end = end_address;
				} else {
					cur_end = mem.start + mem.size() - 1;
				}
				cur_len = cur_end - cur_address + 1;		

				SIZE_T bytes_read;
				part_ok = ReadProcessMemory(process_handle_, reinterpret_cast<void*>(cur_address), cur_dest, cur_len, &bytes_read);

				Q_ASSERT(bytes_read == cur_len);

				if(part_ok) {
					ok = true;
					Q_FOREACH(const Breakpoint::pointer &bp, breakpoints_) {
						if((bp->address() + breakpoint_size()) > cur_address && bp->address() <= cur_end) {
							// show the original bytes in the buffer..
							const QByteArray& bytes = bp->original_bytes();
							Q_ASSERT(bytes.size() == breakpoint_size());
							size_t offset = qMax(bp->address(), cur_address) - bp->address();
							const size_t bp_size = qMin<size_t>(breakpoint_size(), (end_address - bp->address() + 1)) - offset;
							const void* bp_src = bytes.data() + offset;
							void* bp_dest = reinterpret_cast<quint8 *>(buf) + (bp->address() + offset - address);
							memcpy(bp_dest, bp_src, bp_size);
						}
					}
				}

				if(changed) {
					mem.set_permissions(false, mem.writable(), mem.executable());
				}
			} else {
				// check next possible page
				const edb::address_t cur_base = cur_address - (cur_address % page_size());
				if(overflows<edb::address_t>(cur_base, page_size(), end_address)) {
					cur_end = end_address;
				} else {
					cur_end = cur_base + page_size() - 1;
				}
				cur_len = cur_end - cur_address + 1;
			}

			if(overflows<edb::address_t>(cur_address, cur_len, end_address)) {
				break;
			}

			cur_address += cur_len;
		}