raco exe: fix stripping of signature from Mach-O executables
To strip a signature, the old implementation effectively guessed at the padding that was added to the original linkedit segment to acommodate 16-byte alignment of the signature. The repaired stripping works out the actual end of the linkedit segment based on the various load commands that refer to it.
This commit is contained in:
parent
442c2fdfaf
commit
c082d77c97
|
@ -36,6 +36,26 @@
|
|||
|
||||
(define move-link-edit? #t)
|
||||
|
||||
;; To add a segment to a Mach-O executable, we need to add a load command.
|
||||
;; To support code signing of the resulting executable, we need to place the
|
||||
;; new segment before the linkedit segment, because code signing wants the
|
||||
;; linkedit segment last.
|
||||
;;
|
||||
;; Also, we need to strip away any existing signature. We expect any
|
||||
;; existing signature to be at the end of the linkedit segment. The
|
||||
;; signature is 16-byte aligned, which means that some padding may
|
||||
;; have been added to the linkedit segment, and we have to undo that
|
||||
;; when removing the signature. In other words, we have to recognize
|
||||
;; everything that contributes to the linkedit segment and find the
|
||||
;; contibution that is otherwise last; that's the job of
|
||||
;; `linkedit-limit-offset`, below.
|
||||
;;
|
||||
;; Since the new segment is written before the linkedit segment, we
|
||||
;; need to shift all the offsets that refer to file positions of
|
||||
;; things within the linkedit segment. The `...-pos` variables
|
||||
;; generally retain the location in a file of an offset that needs to
|
||||
;; be updated.
|
||||
;;
|
||||
(define (add-plt-segment file segdata)
|
||||
(let-values ([(p out) (open-input-output-file file #:exists 'update)])
|
||||
(dynamic-wind
|
||||
|
@ -68,7 +88,8 @@
|
|||
[code-signature-size 0]
|
||||
[code-signature-lc-sz 0]
|
||||
[code-sign-drs-pos #f]
|
||||
[code-sign-drs-offset 0])
|
||||
[code-sign-drs-offset 0]
|
||||
[linkedit-limit-offset 0])
|
||||
;; (printf "~a cmds, length 0x~x\n" cnt cmdssz)
|
||||
(read-ulong p) ; flags
|
||||
(when (equal? exe-id #xFeedFacf)
|
||||
|
@ -124,11 +145,36 @@
|
|||
(when 64? (read-ulong p)))
|
||||
(loop (sub1 nsects)))))))]
|
||||
[(2)
|
||||
;; Symbol table
|
||||
(set! sym-tab-pos pos)]
|
||||
;; LC_SYMTAB, symbol table
|
||||
(set! sym-tab-pos pos)
|
||||
(let ([symoffset (read-ulong p)]
|
||||
[nsyms (read-ulong p)]
|
||||
[stroffset (read-ulong p)]
|
||||
[strsize (read-ulong p)]
|
||||
[symsize (if link-edit-64? 16 12)])
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ symoffset (* nsyms symsize))))
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ stroffset strsize))))]
|
||||
[(#xB)
|
||||
;; Dysym
|
||||
(set! dysym-pos pos)]
|
||||
;; LC_DYSYMTAB, Dysym
|
||||
(set! dysym-pos pos)
|
||||
;; Skip over counts:
|
||||
(for ([i 6]) (read-ulong p))
|
||||
;; Check that unhandled counts are zero; we could handle
|
||||
;; more of these, and it's just a matter of working out
|
||||
;; the size to multiply each count
|
||||
(for ([i 3])
|
||||
(read-ulong p)
|
||||
(unless (zero? (read-ulong p))
|
||||
(error 'check-header "unhandled LC_DYSYMTAB count is not 0")))
|
||||
;; indirect syms
|
||||
(let ([offset (read-ulong p)]
|
||||
[count (read-ulong p)])
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ offset (* count 4)))))
|
||||
;; Two more:
|
||||
(for ([i 2])
|
||||
(read-ulong p)
|
||||
(unless (zero? (read-ulong p))
|
||||
(error 'check-header "unhandled LC_DYSYMTAB count is not 0")))]
|
||||
[(#x16)
|
||||
;; 2-level hints table
|
||||
(set! hints-pos pos)]
|
||||
|
@ -145,15 +191,27 @@
|
|||
[exportbindoff (read-ulong p)]
|
||||
[exportbindsize (read-ulong p)])
|
||||
(set! dyld-info-pos pos)
|
||||
(set! dyld-info-offs (vector rebaseoff bindoff weakbindoff lazybindoff exportbindoff)))]
|
||||
(set! dyld-info-offs (vector rebaseoff bindoff weakbindoff lazybindoff exportbindoff))
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset
|
||||
(+ rebaseoff rebasesize)
|
||||
(+ bindoff bindsize)
|
||||
(+ weakbindoff weakbindsize)
|
||||
(+ lazybindoff lazybindsize)
|
||||
(+ exportbindoff exportbindsize))))]
|
||||
[(#x26)
|
||||
;; LC_FUNCTION_STARTS
|
||||
(set! function-starts-offset (read-ulong p))
|
||||
(set! function-starts-pos pos)]
|
||||
(let ([offset (read-ulong p)]
|
||||
[size (read-ulong p)])
|
||||
(set! function-starts-offset offset)
|
||||
(set! function-starts-pos pos)
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ offset size))))]
|
||||
[(#x29)
|
||||
;; LC_DATA_IN_CODE
|
||||
(set! data-in-code-offset (read-ulong p))
|
||||
(set! data-in-code-pos pos)]
|
||||
(let ([offset (read-ulong p)]
|
||||
[size (read-ulong p)])
|
||||
(set! data-in-code-offset offset)
|
||||
(set! data-in-code-pos pos)
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ offset size))))]
|
||||
[(#x1D)
|
||||
;; LC_CODE_SIGNATURE
|
||||
(if (= cnt 1)
|
||||
|
@ -161,21 +219,29 @@
|
|||
[size (read-ulong p)])
|
||||
(file-position p (+ offset size))
|
||||
(if (eof-object? (read-byte p))
|
||||
(let ([extra (detect-linkedit-padding p
|
||||
link-edit-offset
|
||||
link-edit-len
|
||||
offset
|
||||
(if link-edit-64? 8 4))])
|
||||
(begin
|
||||
(unless ((abs (- offset linkedit-limit-offset)) . < . 16)
|
||||
(error 'check-header
|
||||
"code signature does not line up with end of other linkedit blocks: ~s vs. ~s"
|
||||
offset linkedit-limit-offset))
|
||||
(set! code-signature-pos pos)
|
||||
(set! code-signature-lc-sz sz)
|
||||
(set! code-signature-size (+ size extra)))
|
||||
;; Claim a larger size to account for padding:
|
||||
(set! code-signature-size (- link-edit-len (- linkedit-limit-offset link-edit-offset))))
|
||||
(log-warning "WARNING: code signature is not at end of file")))
|
||||
(log-warning "WARNING: code signature is not last load command"))]
|
||||
[(#x2B)
|
||||
;; LC_DYLIB_CODE_SIGN_DRS
|
||||
(set! code-sign-drs-pos pos)
|
||||
(let ([offset (read-ulong p)])
|
||||
(set! code-sign-drs-offset offset))]
|
||||
(let ([offset (read-ulong p)]
|
||||
[size (read-ulong p)])
|
||||
(set! code-sign-drs-offset offset)
|
||||
(set! code-sign-drs-pos pos)
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ offset size))))]
|
||||
[(#x1E #x2E)
|
||||
;; LC_SEGMENT_SPLIT_INFO or LC_LINKER_OPTIMIZATION_HINT
|
||||
(let ([offset (read-ulong p)]
|
||||
[size (read-ulong p)])
|
||||
(set! linkedit-limit-offset (max linkedit-limit-offset (+ offset size))))]
|
||||
[else
|
||||
(void)])
|
||||
(file-position p (+ pos sz))
|
||||
|
@ -312,32 +378,6 @@
|
|||
(close-input-port p)
|
||||
(close-output-port out)))))
|
||||
|
||||
(define (detect-linkedit-padding p offset size lc-offset alignment)
|
||||
;; To add a code signature, link-edit size may have been rounded up
|
||||
;; to a multiple of 16. Look for extra \0s before the code-signature
|
||||
;; offset.
|
||||
(unless (zero? (modulo size 16))
|
||||
(error 'detect-linkedit-padding "expected a multiple of 16 for current size"))
|
||||
(define orig-pos (file-position p))
|
||||
(file-position p (- lc-offset 16))
|
||||
(define bstr (read-bytes 16 p))
|
||||
(file-position p orig-pos)
|
||||
(if (= alignment 8)
|
||||
(if (regexp-match? #px#"\0{9}$" bstr)
|
||||
8 ; must be an extra word
|
||||
0)
|
||||
(cond
|
||||
[(regexp-match? #px#"\0{14}$" bstr)
|
||||
;; three extra words
|
||||
12]
|
||||
[(regexp-match? #px#"\0{10}$" bstr)
|
||||
;; two extra words
|
||||
8]
|
||||
[(regexp-match? #px#"\0{6}$" bstr)
|
||||
;; an extra word
|
||||
4]
|
||||
[else 0])))
|
||||
|
||||
(define (fix-offset p pos out d base delta)
|
||||
(when (and out (not (zero? delta)))
|
||||
(file-position p (+ pos d))
|
||||
|
|
Loading…
Reference in New Issue
Block a user