Пример #1
0
        public void VisitPointer(Pointer ptr)
        {
            switch (ptr.Size)
            {
            case 2:
                fmt.WriteKeyword("dw");
                fmt.Write("\t");
                fmt.Write(string.Format("0x{0:X4}", rdr.ReadByte()));
                fmt.WriteLine();
                return;

            case 4:
                fmt.WriteKeyword("dd");
                fmt.Write("\t");
                fmt.Write(string.Format("0x{0:X8}", rdr.ReadUInt32()));
                fmt.WriteLine();
                return;

            case 8:
                fmt.WriteKeyword("dq");
                fmt.Write("\t");
                fmt.Write(string.Format("0x{0:X16}", rdr.ReadUInt32()));
                fmt.WriteLine();
                return;
            }
        }
Пример #2
0
 public static Elf32_Sym Load(ImageReader rdr)
 {
     var sym = new Elf32_Sym();
     sym.st_name = rdr.ReadUInt32();
     sym.st_value = rdr.ReadUInt32();
     sym.st_size = rdr.ReadUInt32();
     sym.st_info = rdr.ReadByte();
     sym.st_other = rdr.ReadByte();
     sym.st_shndx = rdr.ReadUInt16();
     return sym;
 }
Пример #3
0
        public static object ReadPointer(Type pointerType, int size, ImageReader rdr, ReaderContext ctx)
        {
            Debug.Print("Reading pointer at offset {0}, size {1}", rdr.Offset, size);
            uint newOffset;

            switch (size)
            {
            default:
                throw new InvalidOperationException("Field size must be > 0.");

            case 1: newOffset = rdr.ReadByte(); break;

            case 2: newOffset = rdr.ReadUInt16(); break;

            case 4: newOffset = rdr.ReadUInt32(); break;
            }
            Debug.Print("Structure of type {0} must start at offset {1:X}", pointerType.Name, newOffset);
            rdr        = rdr.Clone();
            rdr.Offset = newOffset;

            var dst = Activator.CreateInstance(pointerType);
            var sr  = new StructureReader(dst);

            sr.Read(rdr);
            return(dst);
        }
Пример #4
0
        public void WriteByteRange(MemoryArea image, Address begin, Address addrEnd, TextWriter writer)
        {
            ImageReader rdr = arch.CreateImageReader(image, begin);

            while (rdr.Address < addrEnd)
            {
                writer.Write("{0:X2} ", rdr.ReadByte());
            }
        }
Пример #5
0
        public void DumpData(SegmentMap map, Address address, long cbBytes, TextWriter stm)
        {
            ulong        cSkip = address.ToLinear() & 0x0F;
            ImageSegment segment;

            if (!map.TryFindSegment(address, out segment) || segment.MemoryArea == null)
            {
                return;
            }
            ImageReader rdr = arch.CreateImageReader(segment.MemoryArea, address);

            while (cbBytes > 0)
            {
                StringBuilder sb = new StringBuilder(0x12);
                try
                {
                    stm.Write("{0} ", rdr.Address);
                    for (int i = 0; i < 16; ++i)
                    {
                        if (cbBytes > 0 && cSkip == 0)
                        {
                            byte b = rdr.ReadByte();
                            stm.Write("{0:X2} ", b);
                            sb.Append(0x20 <= b && b < 0x7F
                                                                ? (char)b
                                                                : '.');
                            --cbBytes;
                        }
                        else
                        {
                            stm.Write("   ");
                            if (cSkip > 0)
                            {
                                sb.Append(' ');
                            }
                            --cSkip;
                        }
                    }
                }
                catch
                {
                    stm.WriteLine();
                    stm.WriteLine("...end of image");
                    return;
                }
                stm.WriteLine(sb.ToString());
            }
        }
Пример #6
0
        public void DumpData(LoadedImage image, Address address, long cbBytes, TextWriter stm)
        {
            ulong       cSkip = address.ToLinear() & 0x0F;
            ImageReader rdr   = arch.CreateImageReader(image, address);

            while (cbBytes > 0)
            {
                StringBuilder sb = new StringBuilder(0x12);
                try
                {
                    stm.Write("{0} ", rdr.Address);
                    for (int i = 0; i < 16; ++i)
                    {
                        if (cbBytes > 0 && cSkip == 0)
                        {
                            byte b = rdr.ReadByte();
                            stm.Write("{0:X2} ", b);
                            sb.Append(0x20 <= b && b < 0x7F
                                                                ? (char)b
                                                                : '.');
                            --cbBytes;
                        }
                        else
                        {
                            stm.Write("   ");
                            if (cSkip > 0)
                            {
                                sb.Append(' ');
                            }
                            --cSkip;
                        }
                    }
                }
                catch
                {
                    stm.WriteLine();
                    stm.WriteLine("...end of image");
                    return;
                }
                stm.WriteLine(sb.ToString());
            }
        }
Пример #7
0
        public static object ReadPointer(Type pointerType, int size, ImageReader rdr, ReaderContext ctx)
        {
            Debug.Print("Reading pointer at offset {0}, size {1}", rdr.Offset, size);
            uint newOffset;
            switch (size)
            {
            default:
                throw new InvalidOperationException("Field size must be > 0.");
            case 1: newOffset = rdr.ReadByte(); break;
            case 2: newOffset = rdr.ReadUInt16(); break;
            case 4: newOffset = rdr.ReadUInt32(); break;
            }
            Debug.Print("Structure of type {0} must start at offset {1:X}", pointerType.Name, newOffset);
            rdr = rdr.Clone();
            rdr.Offset = newOffset;

            var dst = Activator.CreateInstance(pointerType);
            var sr = new StructureReader(dst);
            sr.Read(rdr);
            return dst;
        }
Пример #8
0
        // Apply relocations to a segment.
        bool ApplyRelocations(ImageReader rdr, int cRelocations, NeSegment seg)
        {
            string module = "";
            Address address = null;
            NeRelocationEntry rep = null;
            for (int i = 0; i < cRelocations; i++)
            {
                rep = new NeRelocationEntry
                {
                    address_type = rdr.ReadByte(),
                    relocation_type = rdr.ReadByte(),
                    offset = rdr.ReadLeUInt16(),
                    target1 = rdr.ReadLeUInt16(),
                    target2 = rdr.ReadLeUInt16(),
                };

                // Get the target address corresponding to this entry.

                // If additive, there is no target chain list. Instead, add source
                //  and target.
                bool additive = (rep.relocation_type & NE_RELFLAG_ADDITIVE) != 0;
                Tuple<Address, ImportReference> impRef;
                uint lp;
                switch (rep.relocation_type & 3)
                {
                case NE_RELTYPE_ORDINAL:
                    module = moduleNames[rep.target1 - 1];
                    // Synthesize an import
                    lp = ((uint)rep.target1 << 16) | rep.target2;
                    if (importStubs.TryGetValue(lp, out impRef))
                    {
                        address = impRef.Item1;
                    }
                    else
                    {
                        address = addrImportStubs;
                        importStubs.Add(lp, new Tuple<Address, ImportReference>(
                            address,
                            new OrdinalImportReference(address, module, rep.target2)));
                        addrImportStubs += 8;
                    }
                    break;

                case NE_RELTYPE_NAME:
                    module = moduleNames[rep.target1 - 1];
                    uint offName = lfaNew + this.offImportedNamesTable + rep.target2;
                    var nameRdr = new LeImageReader(RawImage, offName);
                    byte fnNameLength = nameRdr.ReadByte();
                    var abFnName = nameRdr.ReadBytes(fnNameLength);
                    lp = ((uint)rep.target1 << 16) | rep.target2;
                    if (importStubs.TryGetValue(lp, out impRef))
                    {
                        address = impRef.Item1;
                    }
                    else
                    {
                        address = addrImportStubs;
                        string fnName = Encoding.ASCII.GetString(abFnName);
                        importStubs.Add(lp, new Tuple<Address, ImportReference>(
                            address,
                            new NamedImportReference(address, module, fnName)));
                    }
                    break;
                case NE_RELTYPE_INTERNAL:
                    if ((rep.target1 & 0xff) == 0xff)
                    {
                        throw new NotImplementedException();
                    }
                    else
                    {
                        address = segments[rep.target1 - 1].Address + rep.target2;
                    }
                    Debug.Print("{0}: {1:X4}:{2:X4} {3}",
                          i + 1,
                          address.Selector.Value,
                          address.Selector.Value,
                          "");
                    break;
                case NE_RELTYPE_OSFIXUP:
                    /* Relocation type 7:
                     *
                     *    These appear to be used as fixups for the Windows
                     * floating point emulator.  Let's just ignore them and
                     * try to use the hardware floating point.  Linux should
                     * successfully emulate the coprocessor if it doesn't
                     * exist.
                     */
                    /*
                   TRACE("%d: TYPE %d, OFFSET %04x, TARGET %04x %04x %s\n",
                         i + 1, rep->relocation_type, rep->offset,
                         rep->target1, rep->target2,
                         NE_GetRelocAddrName( rep->address_type, additive ) );
                   */
                    continue;
                }
                ushort offset = rep.offset;

                // Apparently, high bit of address_type is sometimes set;
                // we ignore it for now.
                if (rep.address_type > NE_RADDR_OFFSET32)
                {
                    diags.Error(
                        string.Format(
                            "Module {0}: unknown relocation address type {1:X2}. Please report",
                            module, rep.address_type));
                    return false;
                }

                if (additive)
                {
                    var sp = seg.Address + offset;
                    Debug.Print("    {0:X4}:{0:X4}", offset, offset);
                    byte b;
                    ushort w;
                    switch (rep.address_type & 0x7f)
                    {
                    case NE_RADDR_LOWBYTE:
                        b = image.ReadByte(sp);
                        image.WriteByte(sp, (byte)(b + address.Offset));
                        break;
                    case NE_RADDR_OFFSET16:
                        w = image.ReadLeUInt16(sp);
                        image.WriteLeUInt16(sp, (ushort)(w + address.Offset));
                        break;
                    case NE_RADDR_POINTER32:
                        w = image.ReadLeUInt16(sp);
                        image.WriteLeUInt16(sp, (ushort)(w + address.Offset));
                        image.WriteLeUInt16(sp + 2, address.Selector.Value);
                        break;
                    case NE_RADDR_SELECTOR:
                        // Borland creates additive records with offset zero. Strange, but OK.
                        w = image.ReadLeUInt16(sp);
                        if (w != 0)
                            diags.Error(string.Format("Additive selector to {0:X4}. Please report.", w));
                        else
                            image.WriteLeUInt16(sp, address.Selector.Value);
                        break;
                    default:
                        goto unknown;
                    }
                }
                else
                {
                    // Non-additive fixup.
                    do
                    {
                        var sp = seg.Address + offset;
                        ushort next_offset = image.ReadLeUInt16(sp);
                        Debug.Print("    {0:X4}:{0:X4}", offset, next_offset);
                        switch (rep.address_type & 0x7f)
                        {
                        case NE_RADDR_LOWBYTE:
                            image.WriteByte(sp, (byte)address.Offset);
                            break;
                        case NE_RADDR_OFFSET16:
                            image.WriteLeUInt16(sp, (ushort)address.Offset);
                            break;
                        case NE_RADDR_POINTER32:
                            image.WriteLeUInt16(sp, (ushort)address.Offset);
                            image.WriteLeUInt16(sp + 2, address.Selector.Value);
                            break;
                        case NE_RADDR_SELECTOR:
                            image.WriteLeUInt16(sp, address.Selector.Value);
                            break;
                        default:
                            goto unknown;
                        }
                        if (next_offset == offset) break;  // avoid infinite loop
                        if (next_offset >= seg.Alloc)
                            break;
                        offset = next_offset;
                    } while (offset != 0xffff);
                }
            }
            return true;

            unknown:
            var svc = Services.RequireService<IDiagnosticsService>();
            svc.Warn(string.Format("{0}: unknown ADDR TYPE {1},  " +
                "TYPE {2},  OFFSET {3:X4},  TARGET {4:X4} {5:X4}",
                seg.Address.Selector, rep.address_type, rep.relocation_type,
                rep.offset, rep.target1, rep.target2));
            return false;
        }
Пример #9
0
		public void ReadOptionalHeader(ImageReader rdr, short expectedMagic)
		{
			if (optionalHeaderSize <= 0)
				throw new BadImageFormatException("Optional header size should be larger than 0 in a PE executable image file.");

			short magic = rdr.ReadLeInt16();
			if (magic != expectedMagic) 
				throw new BadImageFormatException("Not a valid PE Header.");
			rdr.ReadByte();		// Linker major version
			rdr.ReadByte();		// Linker minor version
			rdr.ReadLeUInt32();		// code size (== .text section size)
			rdr.ReadLeUInt32();		// size of initialized data
			rdr.ReadLeUInt32();		// size of uninitialized data
			rvaStartAddress = rdr.ReadLeUInt32();
			uint rvaBaseOfCode = rdr.ReadLeUInt32();
            preferredBaseOfImage = innerLoader.ReadPreferredImageBase(rdr);
			rdr.ReadLeUInt32();		// section alignment
			rdr.ReadLeUInt32();		// file alignment
			rdr.ReadLeUInt16();		// OS major version
			rdr.ReadLeUInt16();		// OS minor version
			rdr.ReadLeUInt16();		// Image major version
			rdr.ReadLeUInt16();		// Image minor version
			rdr.ReadLeUInt16();		// Subsystem major version
			rdr.ReadLeUInt16();		// Subsystem minor version
			rdr.ReadLeUInt32();		// reserved
			uint sizeOfImage = rdr.ReadLeUInt32();
			uint sizeOfHeaders = rdr.ReadLeUInt32();
			uint checksum = rdr.ReadLeUInt32();
			ushort subsystem = rdr.ReadLeUInt16();
			ushort dllFlags = rdr.ReadLeUInt16();
            var stackReserve = rdr.Read(arch.WordWidth);
            var stackCommit = rdr.Read(arch.WordWidth);
            var heapReserve = rdr.Read(arch.WordWidth);
            var heapCommit = rdr.Read(arch.WordWidth);
			rdr.ReadLeUInt32();			// loader flags
			uint dictionaryCount = rdr.ReadLeUInt32();

            if (dictionaryCount == 0) return;
			rvaExportTable = rdr.ReadLeUInt32();
			sizeExportTable = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            rvaImportTable = rdr.ReadLeUInt32();
			uint importTableSize = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
			rvaResources = rdr.ReadLeUInt32();			// resource address
			rdr.ReadLeUInt32();			// resource size

            if (--dictionaryCount == 0) return;
			rvaExceptionTable = rdr.ReadLeUInt32();			// exception address
			sizeExceptionTable = rdr.ReadLeUInt32();			// exception size

            if (--dictionaryCount == 0) return;
			rdr.ReadLeUInt32();			// certificate address
			rdr.ReadLeUInt32();			// certificate size

            if (--dictionaryCount == 0) return;
            uint rvaBaseRelocAddress = rdr.ReadLeUInt32();
			uint baseRelocSize = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaDebug = rdr.ReadLeUInt32();
            uint cbDebug = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaArchitecture = rdr.ReadLeUInt32();
            uint cbArchitecture = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaGlobalPointer = rdr.ReadLeUInt32();
            uint cbGlobalPointer = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaTls = rdr.ReadLeUInt32();
            uint cbTls = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaLoadConfig = rdr.ReadLeUInt32();
            uint cbLoadConfig = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaBoundImport = rdr.ReadLeUInt32();
            uint cbBoundImport = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            uint rvaIat = rdr.ReadLeUInt32();
            uint cbIat = rdr.ReadLeUInt32();

            if (--dictionaryCount == 0) return;
            this.rvaDelayImportDescriptor = rdr.ReadLeUInt32();
            uint cbDelayImportDescriptor = rdr.ReadLeUInt32();
		}
Пример #10
0
        /// <summary>
        /// Reads the ELF header.
        /// </summary>
        /// <returns></returns>
        private Elf32_Ehdr ReadElfHeaderStart()
        {
            var rdr = new ImageReader(RawImage, 0);
            var h = new Elf32_Ehdr();

            h.e_ident = rdr.ReadBeUInt32();
            
            h.e_class = rdr.ReadByte();
            h.endianness = rdr.ReadByte();
            h.version = rdr.ReadByte();
            h.osAbi = rdr.ReadByte();

            rdr.Seek(8);             // 8 bytes of padding.

            // Now that we know the endianness, read the remaining fields in endian mode.
            rdr = CreateImageReader(h.endianness, rdr.Offset);
            h.e_type = rdr.ReadInt16();
            h.e_machine = rdr.ReadInt16();
            h.e_version = rdr.ReadInt32();
            h.e_entry = rdr.ReadUInt32();
            h.e_phoff = rdr.ReadUInt32();
            h.e_shoff = rdr.ReadUInt32();
            h.e_flags = rdr.ReadInt32();
            h.e_ehsize = rdr.ReadInt16();
            h.e_phentsize = rdr.ReadInt16();
            h.e_phnum = rdr.ReadInt16();
            h.e_shentsize = rdr.ReadInt16();
            h.e_shnum = rdr.ReadInt16();
            h.e_shstrndx = rdr.ReadInt16();

            Dump("e_type: {0}", h.e_type);
            Dump("e_machine: {0}", (MachineType) h.e_machine);
            Dump("e_version: {0}", h.e_version);
            Dump("e_entry: {0:X}", h.e_entry);
            Dump("e_phoff: {0:X}", h.e_phoff);
            Dump("e_shoff: {0:X}", h.e_shoff);
            Dump("e_flags: {0:X}", h.e_flags);
            Dump("e_ehsize: {0}", h.e_ehsize);
            Dump("e_phentsize: {0}", h.e_phentsize);
            Dump("e_phnum: {0}", h.e_phnum);
            Dump("e_shentsize: {0}", h.e_shentsize);
            Dump("e_shnum: {0}", h.e_shnum);
            Dump("e_shstrndx: {0}", h.e_shstrndx);
            
            return h;
        }
Пример #11
0
        private MachineOperand ParseOperandInner(byte addressMode, byte operandBits, PrimitiveType dataWidth, ImageReader rdr)
        {
            Constant offset;
            switch (addressMode)
            {
            case 0: // Data register direct.
                return DataRegisterOperand(operandBits, 0);
            case 1: // Address register direct
                return new RegisterOperand(AddressRegister(operandBits, 0));
            case 2:  // Address register indirect
                return MemoryOperand.Indirect(dataWidth, AddressRegister(operandBits, 0));
            case 3:  // Address register indirect with postincrement.
                return MemoryOperand.PostIncrement(dataWidth, AddressRegister(operandBits, 0));
            case 4:  // Address register indirect with predecrement.
                return MemoryOperand.PreDecrement(dataWidth, AddressRegister(operandBits, 0));
            case 5: // Address register indirect with displacement.
                offset = Constant.Int16(rdr.ReadBeInt16());
                return MemoryOperand.Indirect(dataWidth, AddressRegister(operandBits, 0), offset);
            case 6: // Address register indirect with index
                return AddressRegisterIndirectWithIndex(dataWidth, rdr);
            case 7:
                switch (operandBits)
                {
                case 0: // Absolute short address
                    return new M68kAddressOperand(rdr.ReadBeUInt16());
                case 1: // Absolute long address
                    return new M68kAddressOperand(rdr.ReadBeUInt32());
                case 2: // Program counter with displacement
                    var off = rdr.Address - dasm.instr.Address;
                    off += rdr.ReadBeInt16();
                    return new MemoryOperand(dataWidth, Registers.pc, Constant.Int16((short) off));
                case 3:
                    // Program counter with index
                    var addrExt = rdr.Address;
                    ushort extension = rdr.ReadBeUInt16();

                    if (EXT_FULL(extension))
                    {
                        if (EXT_EFFECTIVE_ZERO(extension))
                        {
                            return new M68kImmediateOperand(Constant.Word32(0));
                        }
                        Constant @base = null;
                        Constant outer = null;
                        if (EXT_BASE_DISPLACEMENT_PRESENT(extension))
                            @base = EXT_BASE_DISPLACEMENT_LONG(extension)
                                ? rdr.ReadBe(PrimitiveType.Word32)
                                : rdr.ReadBe(PrimitiveType.Int16);
                        if (EXT_OUTER_DISPLACEMENT_PRESENT(extension))
                            outer = EXT_OUTER_DISPLACEMENT_LONG(extension)
                                ? rdr.ReadBe(PrimitiveType.Word32)
                                : rdr.ReadBe(PrimitiveType.Int16);
                        RegisterStorage base_reg = EXT_BASE_REGISTER_PRESENT(extension)
                            ? Registers.pc
                            : null;
                        RegisterStorage index_reg = null;
                        PrimitiveType index_width = null;
                        int index_scale = 0;
                        if (EXT_INDEX_REGISTER_PRESENT(extension))
                        {
                            index_reg = EXT_INDEX_AR(extension)
                                ? Registers.AddressRegister((int)EXT_INDEX_REGISTER(extension))
                                : Registers.DataRegister((int)EXT_INDEX_REGISTER(extension));
                            index_width = EXT_INDEX_LONG(extension) ? PrimitiveType.Word32 : PrimitiveType.Int16;
                            index_scale = (EXT_INDEX_SCALE(extension) != 0)
                                ? 1 << EXT_INDEX_SCALE(extension)
                                : 0;
                        }
                       return new IndexedOperand(dataWidth, @base, outer, base_reg, index_reg, index_width, index_scale,
                           (extension & 7) > 0 && (extension & 7) < 4,
                           (extension & 7) > 4);
                    }
                    return new IndirectIndexedOperand(
                        EXT_8BIT_DISPLACEMENT(extension),
                        Registers.pc,
                        EXT_INDEX_AR(extension) ? Registers.AddressRegister((int)EXT_INDEX_REGISTER(extension)) : Registers.DataRegister((int)EXT_INDEX_REGISTER(extension)),
                        EXT_INDEX_LONG(extension) ? PrimitiveType.Word32 : PrimitiveType.Int16,
                        1 << EXT_INDEX_SCALE(extension));

                case 4:
                    //  Immediate
                    if (dataWidth.Size == 1)        // don't want the instruction stream to get misaligned!
                        rdr.ReadByte();
                    return new M68kImmediateOperand(rdr.ReadBe(dataWidth));
                default:
                    throw new NotImplementedException(string.Format("Address mode {0}:{1} not implemented.", addressMode, operandBits));
                }
            default:
                throw new NotImplementedException(string.Format("Address mode {0:X1} not implemented.", addressMode));
            }
        }
Пример #12
0
 private static M68kImmediateOperand GetImmediate(ImageReader rdr, PrimitiveType type)
 {
     if (type.Size == 1)
     {
         rdr.ReadByte();     // skip a byte so we get the appropriate lsb byte and align the word stream.
     }
     return new M68kImmediateOperand(rdr.ReadBe(type));
 }
Пример #13
0
		private static string ReadSectionName(ImageReader rdr)
		{
			byte [] bytes = new Byte[8];
			for (int b = 0; b < bytes.Length; ++b)
			{
				bytes[b] = rdr.ReadByte();
			}

			Encoding asc = Encoding.ASCII;
			char [] chars = asc.GetChars(bytes);
			int i;
			for (i = chars.Length - 1; i >= 0; --i)
			{
				if (chars[i] != 0)
				{
					++i;
					break;
				}
			}
			return new String(chars, 0, i);
		}
Пример #14
0
            /// <summary>
            /// Paints a line of the memory control, starting with the address. 
            /// </summary>
            /// <remarks>
            /// The strategy is to find any items present at the current address, and try
            /// to paint as many adjacent items as possible.
            /// </remarks>
            /// <param name="g"></param>
            /// <param name="rc"></param>
            /// <param name="rdr"></param>
            private Address PaintLine(Graphics g, Rectangle rc, ImageReader rdr, Point ptAddr, bool render)
            {
                StringBuilder sbCode = new StringBuilder(" ");

                // Draw the address part.

                rc.X = 0;
                string s = string.Format("{0}", rdr.Address);
                int cx = (int) g.MeasureString(s + "X", ctrl.Font, rc.Width, StringFormat.GenericTypographic).Width;
                if (!render && new Rectangle(rc.X, rc.Y, cx, rc.Height).Contains(ctrl.ptDown))
                {
                    return rdr.Address;
                }
                else
                {
                    g.FillRectangle(SystemBrushes.Window, rc.X, rc.Y, cx, rc.Height);
                    g.DrawString(s, ctrl.Font, SystemBrushes.ControlText, rc.X, rc.Y, StringFormat.GenericTypographic);
                }
                cx -= cellSize.Width / 2;
                rc = new Rectangle(cx, rc.Top, rc.Width - cx, rc.Height);

                uint rowBytesLeft = ctrl.cbRow;
                ulong linearSelected = ctrl.addrSelected != null ? ctrl.addrSelected.ToLinear() : ~0UL;
                ulong linearAnchor = ctrl.addrAnchor != null ? ctrl.addrAnchor.ToLinear() : ~0UL;
                ulong linearBeginSelection = Math.Min(linearSelected, linearAnchor);
                ulong linearEndSelection = Math.Max(linearSelected, linearAnchor);

                do
                {
                    Address addr = rdr.Address;
                    ulong linear = addr.ToLinear();

                    ImageMapItem item;
                    if (!ctrl.ImageMap.TryFindItem(addr, out item))
                        break;
                    ulong cbIn = (linear - item.Address.ToLinear());			// # of bytes 'inside' the block we are.
                    uint cbToDraw = 16; // item.Size - cbIn;

                    // See if the chunk goes off the edge of the line. If so, clip it.
                    if (cbToDraw > rowBytesLeft)
                        cbToDraw = rowBytesLeft;

                    // Now paint the bytes in this span.

                    for (int i = 0; i < cbToDraw; ++i)
                    {
                        Address addrByte = rdr.Address;
                        ctrl.ImageMap.TryFindItem(addrByte, out item);
                        bool isSelected = linearBeginSelection <= addrByte.ToLinear() && addrByte.ToLinear() <= linearEndSelection;
                        bool isCursor = addrByte.ToLinear() == linearSelected;
                        if (rdr.IsValid)
                        {
                            byte b = rdr.ReadByte();
                            s = string.Format("{0:X2}", b);
                            char ch = (char) b;
                            sbCode.Append(Char.IsControl(ch) ? '.' : ch);
                        }
                        else
                        {
                            s = "??";
                            sbCode.Append(' ');
                        }

                        cx = cellSize.Width * 3;
                        Rectangle rcByte = new Rectangle(
                            rc.Left,
                            rc.Top,
                            cx,
                            rc.Height);

                        if (!render && rcByte.Contains(ptAddr))
                            return addrByte;

                        var theme = GetBrushTheme(item, isSelected);
                        g.FillRectangle(theme.Background, rc.Left, rc.Top, cx, rc.Height);
                        if (!isSelected && theme.StartMarker != null && addrByte.ToLinear() == item.Address.ToLinear())
                        {
                            var pts = new Point[] 
                            {
                                rc.Location,
                                rc.Location,
                                rc.Location,
                            };
                            pts[1].Offset(4, 0);
                            pts[2].Offset(0, 4);
                            g.FillClosedCurve(theme.StartMarker, pts);
                        }
                        g.DrawString(s, ctrl.Font, theme.Foreground, rc.Left + cellSize.Width / 2, rc.Top, StringFormat.GenericTypographic);
                        if (isCursor)
                        {
                            ControlPaint.DrawFocusRectangle(g, rc);
                        }
                        rc = new Rectangle(rc.X + cx, rc.Y, rc.Width - cx, rc.Height);
                    }
                    rowBytesLeft -= cbToDraw;
                } while (rowBytesLeft > 0);

                if (render)
                {
                    g.FillRectangle(SystemBrushes.Window, rc);
                    g.DrawString(sbCode.ToString(), ctrl.Font, SystemBrushes.WindowText, rc.X + cellSize.Width, rc.Top, StringFormat.GenericTypographic);
                }
                return null;
            }