() {
119 | @Override
120 | protected String computeValue() throws ElfException, IOException {
121 | parser.seek(ElfSegment.this.offset);
122 | StringBuilder buffer = new StringBuilder();
123 | int b;
124 | while ((b = parser.readUnsignedByte()) != 0)
125 | buffer.append((char) b);
126 | return buffer.toString();
127 | }
128 | };
129 | break;
130 | }
131 | }
132 |
133 | @Override
134 | public String toString() {
135 | String typeString;
136 | switch (type) {
137 | case PT_NULL:
138 | typeString = "PT_NULL";
139 | break;
140 | case PT_LOAD:
141 | typeString = "PT_LOAD";
142 | break;
143 | case PT_DYNAMIC:
144 | typeString = "PT_DYNAMIC";
145 | break;
146 | case PT_INTERP:
147 | typeString = "PT_INTERP";
148 | break;
149 | case PT_NOTE:
150 | typeString = "PT_NOTE";
151 | break;
152 | case PT_SHLIB:
153 | typeString = "PT_SHLIB";
154 | break;
155 | case PT_PHDR:
156 | typeString = "PT_PHDR";
157 | break;
158 | default:
159 | typeString = "0x" + Long.toHexString(type);
160 | break;
161 | }
162 |
163 | String pFlagsString = "";
164 | if ((flags & /* PF_R= */4) != 0) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "read";
165 | if ((flags & /* PF_W= */2) != 0) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "write";
166 | if ((flags & /* PF_X= */1) != 0) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "execute";
167 |
168 | if (pFlagsString.isEmpty()) pFlagsString = "0x" + Long.toHexString(flags);
169 |
170 | return "ElfProgramHeader[p_type=" + typeString + ", p_filesz=" + file_size + ", p_memsz=" + mem_size + ", p_flags=" + pFlagsString + ", p_align="
171 | + alignment + ", range=[0x" + Long.toHexString(virtual_address) + "-0x" + Long.toHexString(virtual_address + mem_size) + "]]";
172 | }
173 |
174 | /** Only for {@link #PT_INTERP} headers. */
175 | public String getIntepreter() throws IOException {
176 | return (ptInterpreter == null) ? null : ptInterpreter.getValue();
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/app/src/main/java/net/fornwall/jelf/ElfDynamicStructure.java:
--------------------------------------------------------------------------------
1 | package net.fornwall.jelf;
2 |
3 | import java.io.IOException;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | /**
8 | * http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#dynamic_section
9 | *
10 | * "If an object file participates in dynamic linking, its program header table will have an element of type PT_DYNAMIC. This ``segment'' contains the .dynamic
11 | * section. A special symbol, _DYNAMIC, labels the section, which contains an array of the following structures."
12 | *
13 | *
14 | * typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
15 | * extern Elf32_Dyn _DYNAMIC[];
16 | *
17 | * typedef struct { Elf64_Sxword d_tag; union { Elf64_Xword d_val; Elf64_Addr d_ptr; } d_un; } Elf64_Dyn;
18 | * extern Elf64_Dyn _DYNAMIC[];
19 | *
20 | *
21 | *
22 | * http://www.sco.com/developers/gabi/latest/ch5.dynamic.html:
23 | *
24 | * Name Value d_un Executable Shared Object
25 | * ----------------------------------------------------------------------
26 | * DT_NULL 0 ignored mandatory mandatory
27 | * DT_NEEDED 1 d_val optional optional
28 | * DT_PLTRELSZ 2 d_val optional optional
29 | * DT_PLTGOT 3 d_ptr optional optional
30 | * DT_HASH 4 d_ptr mandatory mandatory
31 | * DT_STRTAB 5 d_ptr mandatory mandatory
32 | * DT_SYMTAB 6 d_ptr mandatory mandatory
33 | * DT_RELA 7 d_ptr mandatory optional
34 | * DT_RELASZ 8 d_val mandatory optional
35 | * DT_RELAENT 9 d_val mandatory optional
36 | * DT_STRSZ 10 d_val mandatory mandatory
37 | * DT_SYMENT 11 d_val mandatory mandatory
38 | * DT_INIT 12 d_ptr optional optional
39 | * DT_FINI 13 d_ptr optional optional
40 | * DT_SONAME 14 d_val ignored optional
41 | * DT_RPATH* 15 d_val optional ignored
42 | * DT_SYMBOLIC* 16 ignored ignored optional
43 | * DT_REL 17 d_ptr mandatory optional
44 | * DT_RELSZ 18 d_val mandatory optional
45 | * DT_RELENT 19 d_val mandatory optional
46 | * DT_PLTREL 20 d_val optional optional
47 | * DT_DEBUG 21 d_ptr optional ignored
48 | * DT_TEXTREL* 22 ignored optional optional
49 | * DT_JMPREL 23 d_ptr optional optional
50 | * DT_BIND_NOW* 24 ignored optional optional
51 | * DT_INIT_ARRAY 25 d_ptr optional optional
52 | * DT_FINI_ARRAY 26 d_ptr optional optional
53 | * DT_INIT_ARRAYSZ 27 d_val optional optional
54 | * DT_FINI_ARRAYSZ 28 d_val optional optional
55 | * DT_RUNPATH 29 d_val optional optional
56 | * DT_FLAGS 30 d_val optional optional
57 | * DT_ENCODING 32 unspecified unspecified unspecified
58 | * DT_PREINIT_ARRAY 32 d_ptr optional ignored
59 | * DT_PREINIT_ARRAYSZ 33 d_val optional ignored
60 | * DT_LOOS 0x6000000D unspecified unspecified unspecified
61 | * DT_HIOS 0x6ffff000 unspecified unspecified unspecified
62 | * DT_LOPROC 0x70000000 unspecified unspecified unspecified
63 | * DT_HIPROC 0x7fffffff unspecified unspecified unspecified
64 | *
65 | */
66 | @SuppressWarnings("all")
67 | public class ElfDynamicStructure {
68 |
69 | public static final int DT_NULL = 0;
70 | public static final int DT_NEEDED = 1;
71 | public static final int DT_PLTRELSZ = 2;
72 | public static final int DT_PLTGOT = 3;
73 | public static final int DT_HASH = 4;
74 | /** DT_STRTAB entry holds the address, not offset, of the dynamic string table. */
75 | public static final int DT_STRTAB = 5;
76 | public static final int DT_SYMTAB = 6;
77 | public static final int DT_RELA = 7;
78 | public static final int DT_RELASZ = 8;
79 | public static final int DT_RELAENT = 9;
80 | /** The size in bytes of the {@link #DT_STRTAB} string table. */
81 | public static final int DT_STRSZ = 10;
82 | public static final int DT_SYMENT = 11;
83 | public static final int DT_INIT = 12;
84 | public static final int DT_FINI = 13;
85 | public static final int DT_SONAME = 14;
86 | public static final int DT_RPATH = 15;
87 | public static final int DT_RUNPATH = 29;
88 | public static final int DT_FLAGS_1 = 0x6ffffffb;
89 | public static final int DT_VERDEF = 0x6ffffffc; /* Address of version definition */
90 | public static final int DT_VERDEFNUM = 0x6ffffffd; /* Number of version definitions */
91 | public static final int DT_VERNEEDED = 0x6ffffffe;
92 | public static final int DT_VERNEEDNUM = 0x6fffffff;
93 |
94 | /** Some values of {@link #DT_FLAGS_1}. */
95 | public static final int DF_1_NOW = 0x00000001; /* Set RTLD_NOW for this object. */
96 | public static final int DF_1_GLOBAL = 0x00000002; /* Set RTLD_GLOBAL for this object. */
97 | public static final int DF_1_GROUP = 0x00000004; /* Set RTLD_GROUP for this object. */
98 | public static final int DF_1_NODELETE = 0x00000008; /* Set RTLD_NODELETE for this object. */
99 |
100 | /** For the {@link #DT_STRTAB}. Mandatory. */
101 | public long dt_strtab_offset;
102 | /** For the {@link #DT_STRSZ}. Mandatory. */
103 | public int dt_strtab_size;
104 |
105 | private MemoizedObject dtStringTable;
106 | private final int[] dtNeeded;
107 | public final List entries = new ArrayList<>();
108 |
109 | public static class ElfDynamicSectionEntry {
110 | public ElfDynamicSectionEntry(long d_tag, long d_val_or_ptr) {
111 | this.d_tag = d_tag;
112 | this.d_val_or_ptr = d_val_or_ptr;
113 | }
114 |
115 | public long d_tag;
116 | public long d_val_or_ptr;
117 |
118 | @Override
119 | public int hashCode() {
120 | final int prime = 31;
121 | int result = 1;
122 | result = prime * result + (int) (d_tag ^ (d_tag >>> 32));
123 | result = prime * result + (int) (d_val_or_ptr ^ (d_val_or_ptr >>> 32));
124 | return result;
125 | }
126 |
127 | @Override
128 | public boolean equals(Object obj) {
129 | if (this == obj) return true;
130 | if (obj == null) return false;
131 | if (getClass() != obj.getClass()) return false;
132 | ElfDynamicSectionEntry other = (ElfDynamicSectionEntry) obj;
133 | if (d_tag != other.d_tag) return false;
134 | if (d_val_or_ptr != other.d_val_or_ptr) return false;
135 | return true;
136 | }
137 |
138 | @Override
139 | public String toString() {
140 | return "ElfDynamicSectionEntry[" + d_tag + ", " + d_val_or_ptr + "]";
141 | }
142 | }
143 |
144 | public ElfDynamicStructure(final ElfParser parser, long offset, int size) {
145 | parser.seek(offset);
146 | int numEntries = size / 8;
147 |
148 | List dtNeededList = new ArrayList<>();
149 | // Except for the DT_NULL element at the end of the array, and the relative order of DT_NEEDED elements, entries
150 | // may appear in any order. So important to use lazy evaluation to only evaluating e.g. DT_STRTAB after the
151 | // necessary DT_STRSZ is read.
152 | loop: for (int i = 0; i < numEntries; i++) {
153 | long d_tag = parser.readIntOrLong();
154 | final long d_val_or_ptr = parser.readIntOrLong();
155 | entries.add(new ElfDynamicSectionEntry(d_tag, d_val_or_ptr));
156 | switch ((int) d_tag) {
157 | case DT_NULL:
158 | // A DT_NULL element ends the array (may be following DT_NULL values, but no need to look at them).
159 | break loop;
160 | case DT_NEEDED:
161 | dtNeededList.add((int) d_val_or_ptr);
162 | break;
163 | case DT_STRTAB: {
164 | dtStringTable = new MemoizedObject() {
165 | @Override
166 | protected ElfStringTable computeValue() throws ElfException, IOException {
167 | long fileOffsetForStringTable = parser.virtualMemoryAddrToFileOffset(d_val_or_ptr);
168 | return new ElfStringTable(parser, fileOffsetForStringTable, dt_strtab_size);
169 | }
170 | };
171 | dt_strtab_offset = d_val_or_ptr;
172 | }
173 | break;
174 | case DT_STRSZ:
175 | if (d_val_or_ptr > Integer.MAX_VALUE) throw new ElfException("Too large DT_STRSZ: " + d_val_or_ptr);
176 | dt_strtab_size = (int) d_val_or_ptr;
177 | break;
178 | }
179 | }
180 |
181 | dtNeeded = new int[dtNeededList.size()];
182 | for (int i = 0, len = dtNeeded.length; i < len; i++)
183 | dtNeeded[i] = dtNeededList.get(i);
184 | }
185 |
186 | public List getNeededLibraries() throws ElfException, IOException {
187 | List result = new ArrayList<>();
188 | ElfStringTable stringTable = dtStringTable.getValue();
189 | for (int i = 0; i < dtNeeded.length; i++)
190 | result.add(stringTable.get(dtNeeded[i]));
191 | return result;
192 | }
193 |
194 | @Override
195 | public String toString() {
196 | return "ElfDynamicStructure[]";
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/app/src/main/java/de/thwildau/mpekar/binarydroid/ui/disasm/DisassemblerFragment.java:
--------------------------------------------------------------------------------
1 | package de.thwildau.mpekar.binarydroid.ui.disasm;
2 |
3 | import android.arch.lifecycle.Observer;
4 | import android.arch.lifecycle.ViewModelProviders;
5 | import android.os.Bundle;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.TextView;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import de.thwildau.mpekar.binarydroid.R;
19 | import de.thwildau.mpekar.binarydroid.Utils;
20 | import de.thwildau.mpekar.binarydroid.assembly.ByteAccessor;
21 | import de.thwildau.mpekar.binarydroid.assembly.Disassembler;
22 | import de.thwildau.mpekar.binarydroid.model.Container;
23 | import de.thwildau.mpekar.binarydroid.model.SymbolItem;
24 |
25 | /**
26 | * Fragment responsible for showing the disassembly.
27 | */
28 | public class DisassemblerFragment extends ToolFragment {
29 |
30 | private RecyclerView.LayoutManager layoutManager;
31 | private RecyclerView recyclerView;
32 | private RecyclerView.Adapter adapter;
33 | private DisassemblerViewModel viewModel;
34 | private Map symbolMap;
35 |
36 | @Override
37 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
38 | @Nullable Bundle savedInstanceState) {
39 | View v = inflater.inflate(R.layout.fragment_disasm, container, false);
40 | recyclerView = v.findViewById(R.id.disasmrecycler);
41 |
42 | layoutManager = new LinearLayoutManager(getContext());
43 | recyclerView.setLayoutManager(this.layoutManager);
44 |
45 | viewModel = ViewModelProviders.of(getActivity()).get(DisassemblerViewModel.class);
46 |
47 | //TODO: if we want to support other architectures we cant rely
48 | // on a fixed instruction size (think about CISC architectures like x86)
49 | final int instructionSize = 4;
50 |
51 | // Update adapter whenever we get a (new) binary file.
52 | viewModel.getBinary().observe(this, new Observer() {
53 | @Override
54 | public void onChanged(@Nullable Container container) {
55 | final int wordSize = container.getWordSize();
56 |
57 | // Update symbol map
58 | symbolMap = new HashMap<>();
59 | for (SymbolItem symbol: container.getSymbols()) {
60 | int addr = (int) symbol.addr;
61 | // add Symbol only if absent
62 | if (!symbolMap.containsKey(addr)) {
63 | symbolMap.put(addr, symbol);
64 | }
65 | }
66 |
67 | // Set adapter
68 | adapter = new RecyclerView.Adapter() {
69 | @NonNull
70 | @Override
71 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
72 | View v = LayoutInflater.from(parent.getContext()).
73 | inflate(R.layout.fragment_disasm_line, parent, false);
74 | return new ViewHolder(v);
75 | }
76 |
77 | @Override
78 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
79 | Disassembler d = getDisassembler();
80 | ByteAccessor accessor = getAccessor();
81 |
82 | if (d != null && accessor != null) {
83 | int address = position * instructionSize;
84 |
85 | // Disassemble
86 | Disassembler.Instruction[] insns =
87 | d.disassemble(getAccessor(), address, instructionSize);
88 |
89 | // Get instruction
90 | Disassembler.Instruction insn;
91 | if (insns.length > 1) {
92 | // reserved for future; theoretically you can decode multiple
93 | // instructions with a single "disassemble"-call. This is
94 | // interesting when we add support for architectures without
95 | // fixed instruction sizes (like x86)..
96 | throw new RuntimeException("unexpected instruction count");
97 | } else if (insns.length == 1) {
98 | insn = insns[0];
99 | } else {
100 | // failed to decode, provide dummy instruction
101 | insn = Utils.dummyInstruction((short) 4);
102 | }
103 |
104 | SymbolItem symbolForAddress = getSymbolForAddress(address);
105 |
106 | ViewHolder viewHolder = (ViewHolder) holder;
107 | if (symbolForAddress == null) {
108 | viewHolder.addr.setText(Utils.l2s(address, wordSize));
109 | viewHolder.addr.setTextColor(
110 | getResources().getColor(R.color.hexaddr));
111 | } else {
112 | viewHolder.addr.setText(symbolForAddress.name);
113 | viewHolder.addr.setTextColor(
114 | getResources().getColor(R.color.colorAccent));
115 | }
116 | viewHolder.mnemonic.setText(insn.toString());
117 | }
118 | }
119 |
120 | @Override
121 | public int getItemCount() {
122 | ByteAccessor accessor = getAccessor();
123 | if (accessor != null) {
124 | // We will never have more instructions than the following:
125 | return (int) (accessor.getTotalBytes() / instructionSize);
126 | }
127 | return 0;
128 | }
129 |
130 | private Disassembler getDisassembler() {
131 | try {
132 | return viewModel.getDisasm().getValue();
133 | } catch (NullPointerException ex) {
134 | return null;
135 | }
136 | }
137 |
138 | private ByteAccessor getAccessor() {
139 | try {
140 | return viewModel.getAccessor().getValue();
141 | } catch (NullPointerException ex) {
142 | return null;
143 | }
144 | }
145 | };
146 | recyclerView.setAdapter(adapter);
147 | }
148 | });
149 |
150 | // Observe the address, when it changes we need to jump to the specified address
151 | viewModel.getAddress().observe(this, new Observer() {
152 | @Override
153 | public void onChanged(@Nullable Long newAddress) {
154 | // One "position" = one instruction. We assume instruction size is always 4 bytes.
155 | int position = (int) (newAddress / instructionSize);
156 | layoutManager.scrollToPosition(position);
157 | }
158 | });
159 |
160 | return v;
161 | }
162 |
163 | @Override
164 | public void onRunCommand(int commandId) {
165 | if (commandId == CMD_REFRESHVIEW && adapter != null) {
166 | adapter.notifyDataSetChanged();
167 | }
168 | }
169 |
170 | private SymbolItem getSymbolForAddress(int address) {
171 | return symbolMap.get(address);
172 | }
173 |
174 | class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
175 | final TextView addr;
176 | final TextView mnemonic;
177 |
178 | ViewHolder(View view) {
179 | super(view);
180 | this.addr = (TextView) view.findViewById(R.id.disasm_addr);
181 | this.mnemonic = (TextView) view.findViewById(R.id.disasm_mnemonic);
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------