Skip to content

Commit 2325783

Browse files
committed
Parse the MachO LC_THREAD/LC_UNIXTHREAD command
New object: MachO::ThreadCommand Resolve: #89
1 parent 81440ce commit 2325783

19 files changed

+486
-551
lines changed

api/python/MachO/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ set(LIEF_PYTHON_MACHO_SRC
2020
"${CMAKE_CURRENT_LIST_DIR}/objects/pyRelocationDyld.cpp"
2121
"${CMAKE_CURRENT_LIST_DIR}/objects/pyBindingInfo.cpp"
2222
"${CMAKE_CURRENT_LIST_DIR}/objects/pyExportInfo.cpp"
23+
"${CMAKE_CURRENT_LIST_DIR}/objects/pyThreadCommand.cpp"
2324
"${CMAKE_CURRENT_LIST_DIR}/pyMachOStructures.cpp"
2425
)
2526

api/python/MachO/objects/pyBinary.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ void init_MachO_Binary_class(py::module& m) {
188188
"Return binary's " RST_CLASS_REF(lief.MachO.VersionMin) " if any.",
189189
py::return_value_policy::reference)
190190

191+
.def_property_readonly("has_thread_command",
192+
&Binary::has_thread_command,
193+
"``True`` if the binary has a " RST_CLASS_REF(lief.MachO.ThreadCommand) " command.",
194+
py::return_value_policy::reference_internal)
195+
196+
.def_property_readonly("thread_command",
197+
static_cast<no_const_getter<ThreadCommand&>>(&Binary::thread_command),
198+
"Return binary's " RST_CLASS_REF(lief.MachO.ThreadCommand) " if any.",
199+
py::return_value_policy::reference)
200+
191201
.def("virtual_address_to_offset",
192202
&Binary::virtual_address_to_offset,
193203
"Convert the virtual address to an offset in the binary",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* Copyright 2017 R. Thomas
2+
* Copyright 2017 Quarkslab
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include <algorithm>
17+
18+
#include <string>
19+
#include <sstream>
20+
21+
#include "LIEF/visitors/Hash.hpp"
22+
#include "LIEF/MachO/ThreadCommand.hpp"
23+
24+
#include "pyMachO.hpp"
25+
26+
template<class T>
27+
using getter_t = T (ThreadCommand::*)(void) const;
28+
29+
template<class T>
30+
using setter_t = void (ThreadCommand::*)(T);
31+
32+
33+
void init_MachO_ThreadCommand_class(py::module& m) {
34+
35+
py::class_<ThreadCommand, LoadCommand>(m, "ThreadCommand")
36+
37+
.def_property("flavor",
38+
static_cast<getter_t<uint32_t>>(&ThreadCommand::flavor),
39+
static_cast<setter_t<uint32_t>>(&ThreadCommand::flavor),
40+
"",
41+
py::return_value_policy::reference_internal)
42+
43+
44+
.def_property("state",
45+
static_cast<getter_t<const std::vector<uint8_t>&>>(&ThreadCommand::state),
46+
static_cast<setter_t<const std::vector<uint8_t>&>>(&ThreadCommand::state),
47+
"",
48+
py::return_value_policy::reference_internal)
49+
50+
51+
.def_property("count",
52+
static_cast<getter_t<uint32_t>>(&ThreadCommand::count),
53+
static_cast<setter_t<uint32_t>>(&ThreadCommand::count),
54+
"",
55+
py::return_value_policy::reference_internal)
56+
57+
.def_property_readonly("pc",
58+
static_cast<getter_t<uint64_t>>(&ThreadCommand::pc),
59+
py::return_value_policy::reference_internal)
60+
61+
.def("__eq__", &ThreadCommand::operator==)
62+
.def("__ne__", &ThreadCommand::operator!=)
63+
.def("__hash__",
64+
[] (const ThreadCommand& thread) {
65+
return LIEF::Hash::hash(thread);
66+
})
67+
68+
69+
.def("__str__",
70+
[] (const ThreadCommand& thread)
71+
{
72+
std::ostringstream stream;
73+
stream << thread;
74+
std::string str = stream.str();
75+
return str;
76+
});
77+
78+
}

api/python/MachO/pyMachO.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void init_MachO_module(py::module& m) {
4646
init_MachO_RelocationDyld_class(LIEF_MachO_module);
4747
init_MachO_BindingInfo_class(LIEF_MachO_module);
4848
init_MachO_ExportInfo_class(LIEF_MachO_module);
49+
init_MachO_ThreadCommand_class(LIEF_MachO_module);
4950

5051

5152
// Enums

api/python/MachO/pyMachO.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void init_MachO_RelocationObject_class(py::module&);
4646
void init_MachO_RelocationDyld_class(py::module&);
4747
void init_MachO_BindingInfo_class(py::module&);
4848
void init_MachO_ExportInfo_class(py::module&);
49+
void init_MachO_ThreadCommand_class(py::module&);
4950

5051
// Enums
5152
void init_MachO_Structures_enum(py::module&);

doc/sphinx/api/cpp/macho.rst

+9
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ Export Info
196196

197197
----------
198198

199+
200+
Thread Command
201+
**************
202+
203+
.. doxygenclass:: LIEF::MachO::ThreadCommand
204+
:project: lief
205+
206+
----------
207+
199208
Utilities
200209
*********
201210

doc/sphinx/api/python/macho.rst

+11
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,17 @@ Export Info
218218
----------
219219

220220

221+
Thread Command
222+
**************
223+
224+
.. autoclass:: lief.MachO.ThreadCommand
225+
:members:
226+
:inherited-members:
227+
:undoc-members:
228+
229+
----------
230+
231+
221232
Enums
222233
*****
223234

examples/python/macho_reader.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from lief import MachO
1414

1515
from lief import Logger
16-
Logger.set_level(lief.LOGGING_LEVEL.GLOBAL)
16+
Logger.set_level(lief.LOGGING_LEVEL.INFO)
1717

1818
terminal_rows, terminal_columns = 100, 100
1919
try:
@@ -252,6 +252,23 @@ def print_main_command(binary):
252252
print("")
253253

254254

255+
@exceptions_handler(Exception)
256+
def print_thread_command(binary):
257+
258+
format_str = "{:<13} {:<30}"
259+
format_hex = "{:<13} 0x{:<28x}"
260+
format_dec = "{:<13} {:<30d}"
261+
262+
print("== Thread Command ==")
263+
cmd = binary.thread_command
264+
265+
print(format_hex.format("Flavor:", cmd.flavor))
266+
print(format_hex.format("Count:", cmd.count))
267+
print(format_hex.format("PC:", cmd.pc))
268+
269+
print("")
270+
271+
255272
@exceptions_handler(Exception)
256273
def print_dylinker(binary):
257274
print("== Dylinker ==")
@@ -483,6 +500,10 @@ def main():
483500
action='store_true', dest='show_version_min',
484501
help="Display the 'Version Min' command")
485502

503+
parser.add_argument('--thread-command',
504+
action='store_true', dest='show_thread_command',
505+
help="Display the 'Thread Command' command")
506+
486507
parser.add_argument("binary",
487508
metavar="<macho-file>",
488509
help='Target Mach-O File')
@@ -542,6 +563,9 @@ def main():
542563
if (args.show_relocs or args.show_all) and len(binary.relocations) > 0:
543564
print_relocations(binary)
544565

566+
if (args.show_thread_command or args.show_all) and binary.has_thread_command:
567+
print_thread_command(binary)
568+
545569

546570
if __name__ == "__main__":
547571
main()

include/LIEF/MachO/Binary.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "LIEF/MachO/FunctionStarts.hpp"
4040
#include "LIEF/MachO/SourceVersion.hpp"
4141
#include "LIEF/MachO/VersionMin.hpp"
42+
#include "LIEF/MachO/ThreadCommand.hpp"
4243

4344
namespace LIEF {
4445
namespace MachO {
@@ -247,6 +248,14 @@ class DLL_PUBLIC Binary : public LIEF::Binary {
247248
VersionMin& version_min(void);
248249
const VersionMin& version_min(void) const;
249250

251+
252+
//! @brief ``true`` if the binary has a MachO::ThreadCommand command.
253+
bool has_thread_command(void) const;
254+
255+
//! @brief Return the MachO::ThreadCommand command
256+
ThreadCommand& thread_command(void);
257+
const ThreadCommand& thread_command(void) const;
258+
250259
template<class T>
251260
bool has_command(void) const;
252261

0 commit comments

Comments
 (0)