-
Notifications
You must be signed in to change notification settings - Fork 66
/
lldbinit.py
5584 lines (4938 loc) · 209 KB
/
lldbinit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'''
_ _ _ _ _ _ _
| | |_| | |_|_|___|_| |_
| | | . | . | | | | _|
|_|_|___|___|_|_|_|_|_|
lldbinit v3.1
A gdbinit clone for LLDB aka how to make LLDB a bit more useful and less crappy
Available at https://github.com/gdbinit/lldbinit
Original lldbinit code by Deroko @ https://github.com/deroko/lldbinit
gdbinit available @ https://github.com/gdbinit/Gdbinit
(c) Deroko 2014, 2015, 2016
(c) fG! 2017-2023 - [email protected] - https://reverse.put.as
No original license by Deroko.
All my modifications are under MIT license:
Copyright 2017-2023 Pedro Vilaca
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
Huge thanks to Deroko for his original effort!
To list all implemented commands use 'lldbinitcmds' command.
How to install it:
------------------
$ cp lldbinit.py ~
$ echo "command script import ~/lldbinit.py" >>$HOME/.lldbinit
or
just copy it somewhere and use "command script import path_to_script" when you want to load it.
Notes:
------
Version 3.0+ drops support for ARM32 and assumes ARM64 instead
KNOWN BUGS:
-----------
'''
if __name__ == "__main__":
print("Run only as script from LLDB... Not as standalone program!")
import lldb
import sys
import re
import os
import time
import struct
import argparse
import subprocess
import tempfile
import termios
import fcntl
import json
import hashlib
try:
import keystone
CONFIG_KEYSTONE_AVAILABLE = 1
except ImportError:
CONFIG_KEYSTONE_AVAILABLE = 0
pass
VERSION = "3.1"
BUILD = "383"
#
# User configurable options
#
CONFIG_ENABLE_COLOR = 1
# light or dark mode
CONFIG_APPEARANCE = "light"
# display the instruction bytes in disassembler output
CONFIG_DISPLAY_DISASSEMBLY_BYTES = 1
# the maximum number of lines to display in disassembler output
CONFIG_DISASSEMBLY_LINE_COUNT = 8
# x/i and disas output customization - doesn't affect context disassembler output
CONFIG_USE_CUSTOM_DISASSEMBLY_FORMAT = 1
# enable all the register command shortcuts
CONFIG_ENABLE_REGISTER_SHORTCUTS = 1
# display stack contents on context stop
CONFIG_DISPLAY_STACK_WINDOW = 0
CONFIG_DISPLAY_FLOW_WINDOW = 0
# display data contents on context stop - an address for the data must be set with "datawin" command
CONFIG_DISPLAY_DATA_WINDOW = 0
# disassembly flavor 'intel' or 'att' - default is Intel unless AT&T syntax is your cup of tea
CONFIG_FLAVOR = "intel"
# setup the logging level, which is a bitmask of any of the following possible values (don't use spaces, doesn't seem to work)
#
# LOG_VERBOSE LOG_PROCESS LOG_THREAD LOG_EXCEPTIONS LOG_SHLIB LOG_MEMORY LOG_MEMORY_DATA_SHORT LOG_MEMORY_DATA_LONG LOG_MEMORY_PROTECTIONS LOG_BREAKPOINTS LOG_EVENTS LOG_WATCHPOINTS
# LOG_STEP LOG_TASK LOG_ALL LOG_DEFAULT LOG_NONE LOG_RNB_MINIMAL LOG_RNB_MEDIUM LOG_RNB_MAX LOG_RNB_COMM LOG_RNB_REMOTE LOG_RNB_EVENTS LOG_RNB_PROC LOG_RNB_PACKETS LOG_RNB_ALL LOG_RNB_DEFAULT
# LOG_DARWIN_LOG LOG_RNB_NONE
#
# to see log (at least in macOS)
# $ log stream --process debugserver --style compact
# (or whatever style you like)
CONFIG_LOG_LEVEL = "LOG_NONE"
# removes the offsets and modifies the module name position
# reference: https://lldb.llvm.org/formats.html
CUSTOM_DISASSEMBLY_FORMAT = "\"{${function.initial-function}{${function.name-without-args}} @ {${module.file.basename}}:\n}{${function.changed}\n{${function.name-without-args}} @ {${module.file.basename}}:\n}{${current-pc-arrow} }${addr-file-or-load}: \""
# the colors definitions - don't mess with this
if CONFIG_ENABLE_COLOR:
RESET = "\033[0m"
BOLD = "\033[1m"
UNDERLINE = "\033[4m"
REVERSE = "\033[7m"
BLACK = "\033[30m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
else:
RESET = ""
BOLD = ""
UNDERLINE = ""
REVERSE = ""
BLACK = ""
RED = ""
GREEN = ""
YELLOW = ""
BLUE = ""
MAGENTA = ""
CYAN = ""
WHITE = ""
# default colors - modify as you wish
# since these are just strings modes can be combined
if CONFIG_APPEARANCE == "light":
COLOR_REGVAL = BLACK
COLOR_REGNAME = GREEN
COLOR_CPUFLAGS = BOLD + UNDERLINE + MAGENTA
COLOR_SEPARATOR = BOLD + BLUE
COLOR_HIGHLIGHT_LINE = RED
COLOR_REGVAL_MODIFIED = RED
COLOR_SYMBOL_NAME = BLUE
COLOR_CURRENT_PC = RED
COLOR_CONDITIONAL_YES = REVERSE + GREEN
COLOR_CONDITIONAL_NO = REVERSE + RED
COLOR_HEXDUMP_HEADER = BLUE
COLOR_HEXDUMP_ADDR = BLACK
COLOR_HEXDUMP_DATA = BLACK
COLOR_HEXDUMP_ASCII = BLACK
COLOR_COMMENT = GREEN
elif CONFIG_APPEARANCE == "dark":
COLOR_REGVAL = WHITE
COLOR_REGNAME = GREEN
COLOR_CPUFLAGS = BOLD + UNDERLINE + MAGENTA
COLOR_SEPARATOR = CYAN
COLOR_HIGHLIGHT_LINE = RED
COLOR_REGVAL_MODIFIED = RED
COLOR_SYMBOL_NAME = BLUE
COLOR_CURRENT_PC = RED
COLOR_CONDITIONAL_YES = REVERSE + GREEN
COLOR_CONDITIONAL_NO = REVERSE + RED
COLOR_HEXDUMP_HEADER = BLUE
COLOR_HEXDUMP_ADDR = WHITE
COLOR_HEXDUMP_DATA = WHITE
COLOR_HEXDUMP_ASCII = WHITE
COLOR_COMMENT = GREEN # XXX: test and change
else:
print("[-] Invalid CONFIG_APPEARANCE value.")
# configure the separator character between the "windows" and their size
SEPARATOR = "-"
# minimum terminal width 120 chars
I386_TOP_SIZE = 81
I386_STACK_SIZE = I386_TOP_SIZE - 1
I386_BOTTOM_SIZE = 87
# minimum terminal width 125 chars
X64_TOP_SIZE = 119
X64_STACK_SIZE = X64_TOP_SIZE - 1
X64_BOTTOM_SIZE = 125
# minimum terminal width 108 chars
ARM_TOP_SIZE = 102
ARM_STACK_SIZE = ARM_TOP_SIZE - 1
ARM_BOTTOM_SIZE = 108
# turn on debugging output - you most probably don't need this
DEBUG = 0
#
# Don't mess after here unless you know what you are doing!
#
DATA_WINDOW_ADDRESS = 0
POINTER_SIZE = 8
old_x86 = { "eax": 0, "ecx": 0, "edx": 0, "ebx": 0, "esp": 0, "ebp": 0, "esi": 0, "edi": 0, "eip": 0,
"eflags": 0, "cs": 0, "ds": 0, "fs": 0, "gs": 0, "ss": 0, "es": 0 }
old_x64 = { "rax": 0, "rcx": 0, "rdx": 0, "rbx": 0, "rsp": 0, "rbp": 0, "rsi": 0, "rdi": 0, "rip": 0,
"r8": 0, "r9": 0, "r10": 0, "r11": 0, "r12": 0, "r13": 0, "r14": 0, "r15": 0,
"rflags": 0, "cs": 0, "fs": 0, "gs": 0 }
old_arm64 = { "x0": 0, "x1": 0, "x2": 0, "x3": 0, "x4": 0, "x5": 0, "x6": 0, "x7": 0, "x8": 0, "x9": 0, "x10": 0,
"x11": 0, "x12": 0, "x13": 0, "x14": 0, "x15": 0, "x16": 0, "x17": 0, "x18": 0, "x19": 0, "x20": 0,
"x21": 0, "x22": 0, "x23": 0, "x24": 0, "x25": 0, "x26": 0, "x27": 0, "x28": 0, "fp": 0, "lr": 0,
"sp": 0, "pc": 0, "cpsr": 0 }
GlobalListOutput = []
int3patches = {}
crack_cmds = []
crack_cmds_noret = []
modules_list = []
g_current_target = ""
g_target_hash = ""
g_home = ""
g_db = ""
g_dbdata = {}
# dyld modes
dyld_mode_dict = {
0: "dyld_image_adding",
1: "dyld_image_removing",
2: "dyld_image_info_change",
3: "dyld_image_dyld_moved"
}
MIN_COLUMNS = 125
MIN_ROWS = 25
LLDB_MAJOR = 0
LLDB_MINOR = 0
def __lldb_init_module(debugger, internal_dict):
''' we can execute lldb commands using debugger.HandleCommand() which makes all output to default
lldb console. With SBDebugger.GetCommandinterpreter().HandleCommand() we can consume all output
with SBCommandReturnObject and parse data before we send it to output (eg. modify it);
in practice there is nothing here in initialization or anywhere else that we want to modify
'''
# don't load if we are in Xcode since it is not compatible and will block Xcode
if os.getenv('PATH').startswith('/Applications/Xcode'):
return
# test terminal - idea from https://github.com/ant4g0nist/lisa.py/
try:
tty_rows, tty_columns = struct.unpack("hh", fcntl.ioctl(1, termios.TIOCGWINSZ, "1234"))
# i386 is fine with 87x21
# x64 is fine with 125x23
# aarch64 is fine with 108x26
if tty_columns < MIN_COLUMNS or tty_rows < MIN_ROWS:
print("\033[1m\033[31m[!] current terminal size is {:d}x{:d}".format(tty_columns, tty_rows))
print("[!] lldbinit is best experienced with a terminal size at least {}x{}\033[0m".format(MIN_COLUMNS, MIN_ROWS))
except Exception as e:
print("\033[1m\033[31m[-] failed to find out terminal size.")
print("[!] lldbinit is best experienced with a terminal size at least {}x{}\033[0m".format(MIN_COLUMNS, MIN_ROWS))
global g_home, LLDB_MAJOR, LLDB_MINOR
if g_home == "":
g_home = os.getenv('HOME')
res = lldb.SBCommandReturnObject()
ci = debugger.GetCommandInterpreter()
# settings
ci.HandleCommand("settings set target.x86-disassembly-flavor " + CONFIG_FLAVOR, res)
ci.HandleCommand("settings set prompt \"(lldbinit) \"", res)
ci.HandleCommand("settings set stop-disassembly-count 0", res)
# set the log level - must be done on startup?
ci.HandleCommand("settings set target.process.extra-startup-command QSetLogging:bitmask=" + CONFIG_LOG_LEVEL + ";", res)
if CONFIG_USE_CUSTOM_DISASSEMBLY_FORMAT == 1:
ci.HandleCommand("settings set disassembly-format " + CUSTOM_DISASSEMBLY_FORMAT, res)
# the hook that makes everything possible :-)
ci.HandleCommand("command script add -h '(lldbinit)' -f lldbinit.HandleProcessLaunchHook HandleProcessLaunchHook", res)
ci.HandleCommand("command script add -h '(lldbinit) The main lldbinit hook.' -f lldbinit.HandleHookStopOnTarget HandleHookStopOnTarget", res)
ci.HandleCommand("command script add -h '(lldbinit) Display the current disassembly/CPU context.' -f lldbinit.HandleHookStopOnTarget context", res)
ci.HandleCommand("command alias -h '(lldbinit) Alias to context command.' -- ctx HandleHookStopOnTarget", res)
# commands
ci.HandleCommand("command script add -h '(lldbinit) Print list of available commands.' -f lldbinit.cmd_lldbinitcmds lldbinitcmds", res)
ci.HandleCommand("command script add -h '(lldbinit) Connect to debugserver running on iPhone.' -f lldbinit.cmd_IphoneConnect iphone", res)
#
# comments commands
#
ci.HandleCommand("command script add -h '(lldbinit) Add disassembly comment.' -f lldbinit.cmd_addcomment acm", res)
ci.HandleCommand("command script add -h '(lldbinit) Remove disassembly comment.' -f lldbinit.cmd_delcomment dcm", res)
ci.HandleCommand("command script add -h '(lldbinit) List disassembly comments.' -f lldbinit.cmd_listcomments lcm", res)
# fuck the nazis :-)
# save session, restore session, list session commands
ci.HandleCommand("command script add -h '(lldbinit) Save breakpoint session.' -f lldbinit.cmd_save_session ss", res)
ci.HandleCommand("command script add -h '(lldbinit) Restore breakpoint session.' -f lldbinit.cmd_restore_session rs", res)
ci.HandleCommand("command script add -h '(lldbinit) List breakpoint sessions.' -f lldbinit.cmd_list_sessions ls", res)
#
# dump memory commands
#
ci.HandleCommand("command script add -h '(lldbinit) Memory hex dump in byte format.' -f lldbinit.cmd_db db", res)
ci.HandleCommand("command script add -h '(lldbinit) Memory hex dump in word format.' -f lldbinit.cmd_dw dw", res)
ci.HandleCommand("command script add -h '(lldbinit) Memory hex dump in double word format.' -f lldbinit.cmd_dd dd", res)
ci.HandleCommand("command script add -h '(lldbinit) Memory hex dump in quad word format.' -f lldbinit.cmd_dq dq", res)
# XXX: fix help
ci.HandleCommand("command script add -h '(lldbinit) Disassemble instructions at address.' -f lldbinit.cmd_DumpInstructions u", res)
ci.HandleCommand("command script add -h '(lldbinit) Memory search.' -f lldbinit.cmd_findmem findmem", res)
ci.HandleCommand("command script add -h '(lldbinit) Display process memory regions.' -f lldbinit.cmd_showregions showregions", res)
#
# Settings related commands
#
ci.HandleCommand("command script add -h '(lldbinit) Configure lldb and lldbinit options.' -f lldbinit.cmd_enable enable", res)
ci.HandleCommand("command script add -h '(lldbinit) Configure lldb and lldbinit options.' -f lldbinit.cmd_disable disable", res)
ci.HandleCommand("command script add -h '(lldbinit) Set number of instruction lines in code window.' -f lldbinit.cmd_contextcodesize contextcodesize", res)
# a few settings aliases
ci.HandleCommand("command alias -h '(lldbinit) Enable the stop on library load events.' -- enablesolib enable solib", res)
ci.HandleCommand("command alias -h '(lldbinit) Disable the stop on library load events.' -- disablesolib disable solib", res)
ci.HandleCommand("command alias -h '(lldbinit) Enable target ASLR.' -- enableaslr enable aslr", res)
ci.HandleCommand("command alias -h '(lldbinit) Disable target ASLR.' -- disableaslr disable aslr", res)
#
# Breakpoint related commands
#
# replace the default alias with our own version
ci.HandleCommand("command unalias b", res)
# software breakpoints
ci.HandleCommand("command script add -h '(lldbinit) Set a software breakpoint.' -f lldbinit.cmd_bp b", res)
# alias "bp" command that exists in gdbinit
ci.HandleCommand("command alias -h '(lldbinit) Alias to b.' -- bp b", res)
ci.HandleCommand("command script add -h '(lldbinit) Set a temporary software breakpoint.' -f lldbinit.cmd_bpt bpt", res)
ci.HandleCommand("command script add -h '(lldbinit) Set a temporary breakpoint on next instruction.' -f lldbinit.cmd_bpn bpn", res)
# hardware breakpoints
ci.HandleCommand("command script add -h '(lldbinit) Set an hardware breakpoint.' -f lldbinit.cmd_bh bh", res)
ci.HandleCommand("command script add -h '(lldbinit) Set a temporary hardware breakpoint.' -f lldbinit.cmd_bht bht", res)
# module breakpoints
ci.HandleCommand("command script add -h '(lldbinit) Breakpoint on module load.' -f lldbinit.cmd_bm bm", res)
ci.HandleCommand("command script add -h '(lldbinit) Clear all module load breakpoints.' -f lldbinit.cmd_bmc bmc", res)
ci.HandleCommand("command script add -h '(lldbinit) List all on module load breakpoints.' -f lldbinit.cmd_bml bml", res)
ci.HandleCommand("command script add -h '(lldbinit) Enable anti-anti-debugging measures.' -f lldbinit.cmd_antidebug antidebug", res)
ci.HandleCommand("command script add -h '(lldbinit) Print all images available at gdb_image_notifier() breakpoint.' -f lldbinit.cmd_print_notifier_images print_images", res)
# disable a breakpoint or all
ci.HandleCommand("command script add -h '(lldbinit) Disable a breakpoint.' -f lldbinit.cmd_bpd bpd", res)
ci.HandleCommand("command script add -h '(lldbinit) Disable all breakpoints.' -f lldbinit.cmd_bpda bpda", res)
# clear a breakpoint or all
ci.HandleCommand("command script add -h '(lldbinit) Clear a breakpoint.' -f lldbinit.cmd_bpc bpc", res)
ci.HandleCommand("command alias -h '(lldbinit) Clear all breakpoints' -- bpca breakpoint delete", res)
# enable a breakpoint or all
ci.HandleCommand("command script add -h '(lldbinit) Enable a breakpoint.' -f lldbinit.cmd_bpe bpe", res)
ci.HandleCommand("command script add -h '(lldbinit) Enable all breakpoints.' -f lldbinit.cmd_bpea bpea", res)
# commands to set temporary int3 patches and restore original bytes
ci.HandleCommand("command script add -h '(lldbinit) Patch memory address with INT3.' -f lldbinit.cmd_int3 int3", res)
ci.HandleCommand("command script add -h '(lldbinit) Restore original byte at address patched with INT3.' -f lldbinit.cmd_rint3 rint3", res)
ci.HandleCommand("command script add -h '(lldbinit) List all INT3 patched addresses.' -f lldbinit.cmd_listint3 listint3", res)
ci.HandleCommand("command script add -h '(lldbinit) Patch memory address with NOP.' -f lldbinit.cmd_nop nop", res)
ci.HandleCommand("command script add -h '(lldbinit) Patch memory address with NULL.' -f lldbinit.cmd_null null", res)
# change eflags commands
ci.HandleCommand("command script add -h '(lldbinit) Change adjust CPU flag.' -f lldbinit.cmd_cfa cfa", res)
ci.HandleCommand("command script add -h '(lldbinit) Change carry CPU flag.' -f lldbinit.cmd_cfc cfc", res)
ci.HandleCommand("command script add -h '(lldbinit) Change direction CPU flag.' -f lldbinit.cmd_cfd cfd", res)
ci.HandleCommand("command script add -h '(lldbinit) Change interrupt CPU flag.' -f lldbinit.cmd_cfi cfi", res)
ci.HandleCommand("command script add -h '(lldbinit) Change overflow CPU flag.' -f lldbinit.cmd_cfo cfo", res)
ci.HandleCommand("command script add -h '(lldbinit) Change parity CPU flag.' -f lldbinit.cmd_cfp cfp", res)
ci.HandleCommand("command script add -h '(lldbinit) Change sign CPU flag.' -f lldbinit.cmd_cfs cfs", res)
ci.HandleCommand("command script add -h '(lldbinit) Change trap CPU flag.' -f lldbinit.cmd_cft cft", res)
ci.HandleCommand("command script add -h '(lldbinit) Change zero CPU flag.' -f lldbinit.cmd_cfz cfz", res)
# change NZCV flags - exclusive commands to AArch64 (Z, C are common)
ci.HandleCommand("command script add -h '(lldbinit) Change negative CPU flag.' -f lldbinit.cmd_cfn cfn", res)
ci.HandleCommand("command script add -h '(lldbinit) Change overflow CPU flag.' -f lldbinit.cmd_cfv cfv", res)
# skip/step current instruction commands
ci.HandleCommand("command script add -h '(lldbinit) Skip current instruction.' -f lldbinit.cmd_skip skip", res)
ci.HandleCommand("command script add -h '(lldbinit) Step over calls and loop instructions.' -f lldbinit.cmd_stepo stepo", res)
# cracking friends
ci.HandleCommand("command script add -h '(lldbinit) Return from current function.' -f lldbinit.cmd_crack crack", res)
ci.HandleCommand("command script add -h '(lldbinit) Set a breakpoint and return from that function.' -f lldbinit.cmd_crackcmd crackcmd", res)
ci.HandleCommand("command script add -h '(lldbinit) Set a breakpoint and set a register value. doesn't return from function.' -f lldbinit.cmd_crackcmd_noret crackcmd_noret", res)
# alias for existing breakpoint commands
# list all breakpoints
ci.HandleCommand("command script add -h '(lldbinit) List breakpoints.' -f lldbinit.cmd_bpl bpl", res)
# to set breakpoint commands - I hate typing too much
ci.HandleCommand("command alias -h '(lldbinit) breakpoint command add alias.' -- bcmd breakpoint command add", res)
# launch process and stop at entrypoint (not exactly as gdb command that just inserts breakpoint)
# replace the default run alias with our version
ci.HandleCommand("command unalias r", res)
ci.HandleCommand("command unalias run", res)
ci.HandleCommand("command script add -h '(lldbinit) Start the target and stop at entrypoint.' -f lldbinit.cmd_run r", res)
ci.HandleCommand("command alias -h '(lldbinit) Start the target and stop at entrypoint.' -- run r", res)
# usually it will be inside dyld and not the target main()
ci.HandleCommand("command alias -h '(lldbinit) Start target and stop at entrypoint.' -- break_entrypoint process launch --stop-at-entry", res)
ci.HandleCommand("command script add -h '(lldbinit) Show otool output of Mach-O load commands.' -f lldbinit.cmd_show_loadcmds show_loadcmds", res)
ci.HandleCommand("command script add -h '(lldbinit) Show otool output of Mach-O header.' -f lldbinit.cmd_show_header show_header", res)
ci.HandleCommand("command script add -h '(lldbinit) Test function - do not use :-).' -f lldbinit.cmd_tester tester", res)
ci.HandleCommand("command script add -h '(lldbinit) Set start address to display on data window.' -f lldbinit.cmd_datawin datawin", res)
# used mostly for aliases below but can be called as other commands
ci.HandleCommand("command script add -h '(lldbinit) Update register function to be used by all the register alias.' -f lldbinit.cmd_update_register update_register", res)
# shortcut command to modify registers content
if CONFIG_ENABLE_REGISTER_SHORTCUTS == 1:
# x64
ci.HandleCommand("command alias -h '(lldbinit) Update RIP register.' -- rip update_register rip", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RAX register.' -- rax update_register rax", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RBX register.' -- rbx update_register rbx", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RBP register.' -- rbp update_register rbp", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RSP register.' -- rsp update_register rsp", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RDI register.' -- rdi update_register rdi", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RSI register.' -- rsi update_register rsi", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RDX register.' -- rdx update_register rdx", res)
ci.HandleCommand("command alias -h '(lldbinit) Update RCX register.' -- rcx update_register rcx", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R8 register.' -- r8 update_register r8", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R9 register.' -- r9 update_register r9", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R10 register.' -- r10 update_register r10", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R11 register.' -- r11 update_register r11", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R12 register.' -- r12 update_register r12", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R13 register.' -- r13 update_register r13", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R14 register.' -- r14 update_register r14", res)
ci.HandleCommand("command alias -h '(lldbinit) Update R15 register.' -- r15 update_register r15", res)
# x86
ci.HandleCommand("command alias -h '(lldbinit) Update EIP register.' -- eip update_register eip", res)
ci.HandleCommand("command alias -h '(lldbinit) Update EAX register.' -- eax update_register eax", res)
ci.HandleCommand("command alias -h '(lldbinit) Update EBX register.' -- ebx update_register ebx", res)
ci.HandleCommand("command alias -h '(lldbinit) Update EBP register.' -- ebp update_register ebp", res)
ci.HandleCommand("command alias -h '(lldbinit) Update ESP register.' -- esp update_register esp", res)
ci.HandleCommand("command alias -h '(lldbinit) Update EDI register.' -- edi update_register edi", res)
ci.HandleCommand("command alias -h '(lldbinit) Update ESI register.' -- esi update_register esi", res)
ci.HandleCommand("command alias -h '(lldbinit) Update EDX register.' -- edx update_register edx", res)
ci.HandleCommand("command alias -h '(lldbinit) Update ECX register.' -- ecx update_register ecx", res)
# ARM64
ci.HandleCommand("command alias -h '(lldbinit) Update X0 register.' -- x0 update_register x0", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X1 register.' -- x1 update_register x1", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X2 register.' -- x2 update_register x2", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X3 register.' -- x3 update_register x3", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X4 register.' -- x4 update_register x4", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X5 register.' -- x5 update_register x5", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X6 register.' -- x6 update_register x6", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X7 register.' -- x7 update_register x7", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X8 register.' -- x8 update_register x8", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X9 register.' -- x9 update_register x9", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X10 register.' -- x10 update_register x10", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X11 register.' -- x11 update_register x11", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X12 register.' -- x12 update_register x12", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X13 register.' -- x13 update_register x13", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X14 register.' -- x14 update_register x14", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X15 register.' -- x15 update_register x15", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X16 register.' -- x16 update_register x16", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X17 register.' -- x17 update_register x17", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X18 register.' -- x18 update_register x18", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X19 register.' -- x19 update_register x19", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X20 register.' -- x20 update_register x20", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X21 register.' -- x21 update_register x21", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X22 register.' -- x22 update_register x22", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X23 register.' -- x23 update_register x23", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X24 register.' -- x24 update_register x24", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X25 register.' -- x25 update_register x25", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X26 register.' -- x26 update_register x26", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X27 register.' -- x27 update_register x27", res)
ci.HandleCommand("command alias -h '(lldbinit) Update X28 register.' -- x28 update_register x28", res)
if CONFIG_KEYSTONE_AVAILABLE == 1:
ci.HandleCommand("command script add -h '(lldbinit) 32 bit x86 interactive Keystone based assembler.' -f lldbinit.cmd_asm32 asm32", res)
ci.HandleCommand("command script add -h '(lldbinit) 64 bit x86 interactive Keystone based assembler.' -f lldbinit.cmd_asm64 asm64", res)
ci.HandleCommand("command script add -h '(lldbinit) 32 bit ARM interactive Keystone based assembler.' -f lldbinit.cmd_arm32 arm32", res)
ci.HandleCommand("command script add -h '(lldbinit) 64 bit ARM interactive Keystone based assembler.' -f lldbinit.cmd_arm64 arm64", res)
ci.HandleCommand("command script add -h '(lldbinit) 32 bit ARM Thumb interactive Keystone based assembler.' -f lldbinit.cmd_armthumb armthumb", res)
# add the hook - we don't need to wait for a target to be loaded
# Note: since I removed the original stop-disassembly-count trick to allegedly avoid
# double loading it had a side effect to keep adding multiple copies of the hook
# when doing multiple imports of the script (for testing mostly)
# a check is now implemented where the hook is only added if no previous hook exist
ci.HandleCommand("target stop-hook list", res)
if res.Succeeded():
# XXX: older lldb crashes if we set the -s option...
# if "HandleProcessLaunchHook" not in res.GetOutput():
# ci.HandleCommand("target stop-hook add -n _dyld_start -s /usr/lib/dyld -o 'HandleProcessLaunchHook'", res)
if "HandleHookStopOnTarget" not in res.GetOutput():
ci.HandleCommand("target stop-hook add -o 'HandleHookStopOnTarget'", res)
else:
print("[-] error: failed to list stop hooks and our hook isn't loaded")
ci.HandleCommand("command script add -h '(lldbinit) Display lldbinit banner.' --function lldbinit.cmd_banner banner", res)
# custom commands
ci.HandleCommand("command script add -h '(lldbinit) Fix return breakpoint.' -f lldbinit.cmd_fixret fixret", res)
# displays the version banner when lldb is loaded
LLDB_MAJOR, LLDB_MINOR = get_lldb_version(debugger)
debugger.HandleCommand("banner")
return
def get_lldb_version(debugger):
lldb_versions_match = re.search(r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', debugger.GetVersionString())
lldb_version = 0
lldb_minor = 0
if len(lldb_versions_match.groups()) >= 1 and lldb_versions_match.groups()[0]:
lldb_major = int(lldb_versions_match.groups()[0])
if len(lldb_versions_match.groups()) >= 5 and lldb_versions_match.groups()[4]:
lldb_minor = int(lldb_versions_match.groups()[4])
return lldb_major, lldb_minor
def cmd_banner(debugger,command,result,dict):
lldbver = debugger.GetVersionString().split('\n')[0]
print(GREEN + "[+] Loaded lldbinit version " + VERSION + "." + BUILD + " @ " + lldbver + RESET)
def cmd_lldbinitcmds(debugger, command, result, dict):
'''Display all available lldbinit commands.'''
help_table = [
[ "lldbinitcmds", "this command" ],
[ "----[ Settings ]----", ""],
[ "enable", "configure lldb and lldbinit options" ],
[ "disable", "configure lldb and lldbinit options" ],
[ "contextcodesize", "set number of instruction lines in code window" ],
[ "enablesolib/disablesolib", "enable/disable the stop on library load events" ],
[ "enableaslr/disableaslr", "enable/disable process ASLR" ],
[ "datawin", "set start address to display on data window" ],
[ "----[ Breakpoints ]----", ""],
[ "b", "breakpoint address" ],
[ "bpt", "set a temporary software breakpoint" ],
[ "bh", "set an hardware breakpoint" ],
[ "bht", "set a temporary hardware breakpoint" ],
[ "bpc", "clear breakpoint" ],
[ "bpca", "clear all breakpoints" ],
[ "bpd", "disable breakpoint" ],
[ "bpda", "disable all breakpoints" ],
[ "bpe", "enable a breakpoint" ],
[ "bpea", "enable all breakpoints" ],
[ "bcmd", "alias to breakpoint command add"],
[ "bpl", "list all breakpoints"],
[ "bpn", "temporarly breakpoint next instruction" ],
[ "bm", "breakpoint on module load" ],
[ "bmc", "clear all module load breakpoints" ],
[ "bml", "list all on module load breakpoints" ],
[ "break_entrypoint", "launch target and stop at entrypoint" ],
[ "skip", "skip current instruction" ],
[ "int3", "patch memory address with INT3" ],
[ "rint3", "restore original byte at address patched with INT3" ],
[ "listint3", "list all INT3 patched addresses" ],
[ "lb", "load breakpoints from file and apply them (currently only func names are applied)" ],
[ "lbrva", "load breakpoints from file and apply to main executable, only RVA in this case" ],
[ "print_images", "print all images available at gdb_image_notifier() breakpoint"],
[ "----[ Memory ]----", ""],
[ "nop", "patch memory address with NOP" ],
[ "null", "patch memory address with NULL" ],
[ "db/dw/dd/dq", "memory hex dump in different formats" ],
[ "findmem", "search memory" ],
[ "showregions", "display process memory regions" ],
[ "----[ Disassembly ]----", ""],
[ "u", "dump instructions" ],
[ "ctx/context", "show current instruction pointer CPU context" ],
[ "stepo", "step over calls and loop instructions" ],
[ "acm", "add disassembly comment" ],
[ "dcm", "remove disassembly comment" ],
[ "lcm", "list disassembly comments" ],
[ "----[ Registers and CPU Flags ]----", ""],
[ "rip/rax/rbx/etc", "shortcuts to modify x64 registers" ],
[ "eip/eax/ebx/etc", "shortcuts to modify x86 registers" ],
[ "x{0..28}", "shortcuts to modify ARM64 registers" ],
[ "cfa/cfc/cfd/cfi/cfo/cfp/cfs/cft/cfz", "change x86/x64 CPU flags" ],
[ "cfn/cfz/cfc/cfv", "change AArch64 CPU flags (NZCV register)"],
[ "----[ File headers ]----", ""],
[ "show_loadcmds", "show otool output of Mach-O load commands" ],
[ "show_header", "show otool output of Mach-O header" ],
[ "----[ Cracking ]----", ""],
[ "crack", "return from current function" ],
[ "crackcmd", "set a breakpoint and return from that function" ],
[ "crackcmd_noret", "set a breakpoint and set a register value. doesn't return from function" ],
[ "----[ Misc ]----", ""],
[ "iphone", "connect to debugserver running on iPhone" ],
[ "----[ Assembler ]----", ""],
[ "asm32/asm64", "x86/x64 assembler using keystone" ],
[ "arm32/arm64/armthumb", "ARM assembler using keystone" ]
]
print("lldbinit available commands:")
for row in help_table:
if not row[1]:
print(" {: <20} {: <30}".format(*row))
else:
print(" {: <20} - {: <30}".format(*row))
print("\nUse \'cmdname help\' for extended command help.")
# placeholder to make tests
def cmd_tester(debugger, command, result, dict):
print("test")
return
# -------------------------
# Settings related commands
# -------------------------
def cmd_enable(debugger, command, result, dict):
'''Enable certain lldb and lldbinit options. Use \'enable help\' for more information.'''
help = """
Enable certain lldb and lldbinit configuration options.
Syntax: enable <setting>
Available settings:
solib: enable stop on library events trick.
aslr: enable process aslr.
stack: enable stack window in context display.
data: enable data window in context display, configure address with datawin.
flow: enable call targets and objective-c class/methods window in context display.
"""
global CONFIG_DISPLAY_STACK_WINDOW
global CONFIG_DISPLAY_FLOW_WINDOW
global CONFIG_DISPLAY_DATA_WINDOW
cmd = command.split()
if len(cmd) == 0:
print("[-] error: command requires argument.")
print("")
print(help)
return
if cmd[0] == "solib":
debugger.HandleCommand("settings set target.process.stop-on-sharedlibrary-events true")
print("[+] Enabled stop on library events trick.")
elif cmd[0] == "aslr":
debugger.HandleCommand("settings set target.disable-aslr false")
print("[+] Enabled ASLR.")
elif cmd[0] == "stack":
CONFIG_DISPLAY_STACK_WINDOW = 1
print("[+] Enabled stack window in context display.")
elif cmd[0] == "flow":
CONFIG_DISPLAY_FLOW_WINDOW = 1
print("[+] Enabled indirect control flow window in context display.")
elif cmd[0] == "data":
CONFIG_DISPLAY_DATA_WINDOW = 1
print("[+] Enabled data window in context display. Configure address with \'datawin\' cmd.")
elif cmd[0] == "help":
print(help)
else:
print("[-] error: unrecognized command.")
print(help)
return
def cmd_disable(debugger, command, result, dict):
'''Disable certain lldb and lldbinit options. Use \'disable help\' for more information.'''
help = """
Disable certain lldb and lldbinit configuration options.
Syntax: disable <setting>
Available settings:
solib: disable stop on library events trick.
aslr: disable process aslr.
stack: disable stack window in context display.
data: disable data window in context display.
flow: disable call targets and objective-c class/methods window in context display.
"""
global CONFIG_DISPLAY_STACK_WINDOW
global CONFIG_DISPLAY_FLOW_WINDOW
global CONFIG_DISPLAY_DATA_WINDOW
cmd = command.split()
if len(cmd) == 0:
print("[-] error: command requires argument.")
print("")
print(help)
return
if cmd[0] == "solib":
debugger.HandleCommand("settings set target.process.stop-on-sharedlibrary-events false")
print("[+] Disabled stop on library events trick.")
elif cmd[0] == "aslr":
debugger.HandleCommand("settings set target.disable-aslr true")
print("[+] Disabled ASLR.")
elif cmd[0] == "stack":
CONFIG_DISPLAY_STACK_WINDOW = 0
print("[+] Disabled stack window in context display.")
elif cmd[0] == "flow":
CONFIG_DISPLAY_FLOW_WINDOW = 0
print("[+] Disabled indirect control flow window in context display.")
elif cmd[0] == "data":
CONFIG_DISPLAY_DATA_WINDOW = 0
print("[+] Disabled data window in context display.")
elif cmd[0] == "help":
print(help)
else:
print("[-] error: unrecognized command.")
print(help)
return
def cmd_contextcodesize(debugger, command, result, dict):
'''Set the number of disassembly lines in code window. Use \'contextcodesize help\' for more information.'''
help = """
Configures the number of disassembly lines displayed in code window. Default is 8.
Syntax: contextcodesize <line_count>
Note: expressions supported, do not use spaces between operators.
"""
global CONFIG_DISASSEMBLY_LINE_COUNT
cmd = command.split()
if len(cmd) != 1:
print("[-] error: please insert the number of disassembly lines to display.")
print("")
print(help)
return
if cmd[0] == "help":
print(help)
print("\nCurrent configuration value is: {:d}".format(CONFIG_DISASSEMBLY_LINE_COUNT))
return
value = evaluate(cmd[0])
if value is None:
print("[-] error: invalid input value.")
print("")
print(help)
return
CONFIG_DISASSEMBLY_LINE_COUNT = value
return
# ---------------------------------
# Color and output related commands
# ---------------------------------
# append data to the output that we display at the end of the hook-stop
def output(x):
global GlobalListOutput
GlobalListOutput.append(x)
# ---------------------------
# Breakpoint related commands
# ---------------------------
ANTIDEBUG_SYSCTL_OBJS = []
# the second step breakpoint callback of the sysctl antidebug bypass
# we are at the return address of sysctl symbol
# and we simply remove the P_TRACED flag if it exists
def antidebug_callback_step2(frame, bp_loc, dict):
P_TRACED = 0x800
global ANTIDEBUG_SYSCTL_OBJS
# print("[+] Hit antidebug_callback_step2")
for i in ANTIDEBUG_SYSCTL_OBJS:
ANTIDEBUG_SYSCTL_OBJS.remove(i)
# offset to kp_proc.p_flag - this should be stable
offset = 0x20
target = get_target()
error = lldb.SBError()
# read the current value so we can modify and write again
value = get_process().ReadUnsignedFromMemory(i+offset, 4, error)
# remove P_TRACED flag if it exists
if value & P_TRACED:
print("[+] Hit sysctl antidebug request")
value = value ^ P_TRACED
# WriteMemory accepts a string so we need to pack this
patch = struct.pack("I", value)
result = target.GetProcess().WriteMemory(i+offset, patch, error)
if not error.Success():
print("[-] error: Failed to write memory at 0x{:x}.".format(i+offset))
return
get_process().Continue()
# the first step breakpoint callback of the sysctl antidebug bypass
# this deals with the breakpoint at sysctl symbol
# the purpose is to verify the request and set a second stage on return address
# where the debug flag is removed
def antidebug_callback_step1(frame, bp_loc, dict):
global ANTIDEBUG_SYSCTL_OBJS
error = lldb.SBError()
if frame is None:
return 0
target = get_target()
if is_x64():
src_reg = "rdi"
dst_reg = "rdx"
elif is_arm():
src_reg = "x0"
dst_reg = "x2"
else:
print("[-] error: unsupported architecture")
return 0
mib_addr = int(frame.FindRegister(src_reg).GetValue(), 16)
mib0 = get_process().ReadUnsignedFromMemory(mib_addr, 4, error)
if not error.Success():
print("[-] error: failed to read mib0")
return
mib1 = get_process().ReadUnsignedFromMemory(mib_addr+4, 4, error)
if not error.Success():
print("[-] error: failed to read mib1")
return
mib2 = get_process().ReadUnsignedFromMemory(mib_addr+8, 4, error)
if not error.Success():
print("[-] error: failed to read mib2")
return
# check if it's a potential AmIBeingDebugged request
# it's a common request from some apps
# so we need to verify on the return and remove the flag
# CTL_KERN (1) - KERN_PROC (14) - KERN_PROC_PID (1)
if mib0 == 1 and mib1 == 14 and mib2 == 1:
# print("[+] Hit sysctl antidebug request")
# the pointer to the sysctl output oldp
oldp = int(frame.FindRegister(dst_reg).GetValue(), 16)
if oldp == 0:
print("[!] warning: oldp == 0")
get_process().Continue()
ANTIDEBUG_SYSCTL_OBJS.append(oldp)
# set a temporary breakpoint on the ret
# temporary because we can't sync this with other sysctl calls
# and we don't want to tamper with the rest of the results - just with the P_TRACED flag
mem_sbaddr = lldb.SBAddress(int(frame.FindRegister('pc').GetValue(), 16), target)
# flavor only relevant for x86, ignored when aarch64
inst = target.ReadInstructions(mem_sbaddr, 64, "intel")
for i in inst:
# print(hex(i.GetAddress().GetLoadAddress(target)), i.GetMnemonic(target))
# the properties seem broken in newer lldb versions because this will fail
# if we use i.mnemonic - the SBTarget will be NULL
# what's going on with lldb regressions?
# x64 - ret ; aarch64 - retab
if i.GetMnemonic(target).startswith('ret'):
# print(hex(i.addr.GetLoadAddress(target)), i.GetMnemonic(target))
nextbp = target.BreakpointCreateByAddress(i.GetAddress().GetLoadAddress(target))
nextbp.SetOneShot(True)
nextbp.SetThreadID(get_frame().GetThread().GetThreadID())
# this will generate a traceback on newer lldb versions
# it seems we can't set another callback while inside a callback
# lldb regressions ftw...
nextbp.SetScriptCallbackFunction('lldbinit.antidebug_callback_step2')
# everything automatic here so continue in any case
get_process().Continue()
# bypass PT_DENY_ATTACH via ptrace() call
def antidebug_ptrace_callback(frame, bp_loc, dict):
PT_DENY_ATTACH = 31
error = lldb.SBError()
if is_x64():
src_reg = "rdi"
elif is_arm():
src_reg = "x0"
request = int(frame.FindRegister(src_reg).GetValue(), 16)
if request == PT_DENY_ATTACH:
print("[+] Hit ptrace anti-debug request")
if is_x64():
src_reg = "rax"
elif is_arm():
src_reg = "x0"
# we are essentially bypassing the whole call to return a value of 0
result = frame.registers[0].GetChildMemberWithName(src_reg).SetValueFromCString("0x0", error)
if not result:
print("[-] error: failed to write to {} register".format(src_reg))
return 0
# and return immediately to the caller without executing any ptrace() code
get_thread().ReturnFromFrame(frame, frame.registers[0].GetChildMemberWithName(src_reg))
get_process().Continue()
# debugger detection via the mach exception ports
def antidebug_task_exception_ports_callback(frame, bp_loc, dict):
if frame is None:
return 0
if is_x64():
src_reg = "rsi"
elif is_arm():
src_reg = "x1"
exception_mask = int(frame.FindRegister(src_reg).GetValue(), 16)
if exception_mask != 0x0:
print("[+] Hit {} antidebug request".format(get_frame().symbol.name))
error = lldb.SBError()
result = frame.registers[0].GetChildMemberWithName(src_reg).SetValueFromCString("0x0", error)
if not result:
print("[-] error: failed to write to {} register".format(src_reg))
return 0
get_process().Continue()
def cmd_antidebug(debugger, command, result, dict):
'''Enable anti-anti-debugging. Use \'antidebug help\' for more information.'''
help = """
Enable anti-anti-debugging measures.
Bypasses debugger detection via sysctl, ptrace PT_DENY_ATTACH, and task exception ports.
Syntax: antidebug
"""
cmd = command.split()
if len(cmd) > 0 and cmd[0] == "help":
print(help)
return
target = get_target()
for m in target.module_iter():
if m.file.fullpath == "/usr/lib/dyld":
# sysctl
bp = target.BreakpointCreateByName("sysctl", '/usr/lib/system/libsystem_c.dylib')
bp.SetScriptCallbackFunction('lldbinit.antidebug_callback_step1')
# PT_DENY_ATTACH
bp2 = target.BreakpointCreateByName("ptrace", "/usr/lib/system/libsystem_kernel.dylib")
bp2.SetScriptCallbackFunction("lldbinit.antidebug_ptrace_callback")
# mach exception ports
bp3 = target.BreakpointCreateByName("task_get_exception_ports", "/usr/lib/system/libsystem_kernel.dylib")
bp3.SetScriptCallbackFunction("lldbinit.antidebug_task_exception_ports_callback")
bp4 = target.BreakpointCreateByName("task_set_exception_ports", "/usr/lib/system/libsystem_kernel.dylib")
bp4.SetScriptCallbackFunction("lldbinit.antidebug_task_exception_ports_callback")
print("[+] Enabled anti-anti-debugging measures")
break
# the callback for the specific module loaded breakpoint
# supports x64, i386, arm64
def module_breakpoint_callback(frame, bp_loc, dict):
global modules_list
# rdx contains the module address
# rdx+8 contains pointer to the module name string
if frame is None:
return 0
error = lldb.SBError()
i386 = is_i386()
x64 = is_x64()
arm = is_arm()
if not i386 and not x64 and not arm:
print("[-] error: unsupported architecture.")
# for i386 we need the stack pointer to retrieve arguments
if i386:
sp = frame.FindRegister("esp")
sp = int(sp.GetValue(), 16)
# static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
# static void lldb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
if x64:
pc = frame.FindRegister("rdi")
mode = int(pc.GetValue(), 16)
elif arm:
# argument registers from x0 to x7
pc = frame.FindRegister("x0")
mode = int(pc.GetValue(), 16)
elif i386:
mode = get_process().ReadUnsignedFromMemory(sp+4, 4, error)
if not error.Success():
print("[-] error: failed to read mode from stack.")
return
# only interested in new images
# enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1, dyld_image_info_change=2, dyld_image_dyld_moved=3 };
if mode != 0:
get_process().Continue()
return
# infoCount argument
if x64:
pc = frame.FindRegister("rsi")
infoCount = int(pc.GetValue(), 16)
elif arm:
pc = frame.FindRegister("x1")
infoCount = int(pc.GetValue(), 16)
elif i386:
infoCount = get_process().ReadUnsignedFromMemory(sp+8, 4, error)
if not error.Success():
print("[-] error: failed to read infoCount from stack.")
return
# info argument
if x64:
pc = frame.FindRegister("rdx")
info = int(pc.GetValue(), 16)
elif arm:
pc = frame.FindRegister("x2")
info = int(pc.GetValue(), 16)
elif i386:
info = get_process().ReadUnsignedFromMemory(sp+12, 4, error)
if not error.Success():
print("[-] error: failed to read address from rdx.")
return
# set values according to target platform