mirror of
https://github.com/Binject/debug
synced 2026-06-08 10:28:33 +00:00
dd7757eeb3
- implement relocation add/replace APIs and serialization updates per format - generate dyld rebase/bind info from Mach-O relocs with optional ordinals - update ELF dynamic relocation tags and safe relayout handling - add/extend relocation tests and stabilize existing test suite
361 lines
12 KiB
Go
361 lines
12 KiB
Go
package macho
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"sort"
|
|
)
|
|
|
|
// Bytes - Returns the bytes of an assembled *macho.File
|
|
func (machoFile *File) Bytes() ([]byte, error) {
|
|
var bytesWritten uint64
|
|
w := bytes.NewBuffer(nil)
|
|
|
|
relocData, relocStart, err := machoFile.prepareRelocationData()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := machoFile.refreshSegmentLoadBytes(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Write entire file header.
|
|
buf := &bytes.Buffer{}
|
|
if err := binary.Write(buf, machoFile.ByteOrder, machoFile.FileHeader); err != nil {
|
|
panic(err)
|
|
}
|
|
headerLength := len(buf.Bytes())
|
|
binary.Write(w, machoFile.ByteOrder, machoFile.FileHeader)
|
|
bytesWritten += uint64(headerLength)
|
|
//log.Printf("%x: Wrote file header of size: %v", bytesWritten, bytesWritten)
|
|
|
|
// Reserved 4 bytes at end of header
|
|
w.Write([]byte{0, 0, 0, 0})
|
|
bytesWritten += 4
|
|
|
|
// Write Load Commands Loop
|
|
for _, singleLoad := range machoFile.Loads {
|
|
buf2 := &bytes.Buffer{}
|
|
err = binary.Write(buf2, machoFile.ByteOrder, singleLoad.Raw())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
LoadCmdLen := len(buf2.Bytes())
|
|
binary.Write(w, machoFile.ByteOrder, singleLoad.Raw())
|
|
bytesWritten += uint64(LoadCmdLen)
|
|
//log.Printf("%x: Wrote Load Command, total size of: %v", bytesWritten, LoadCmdLen)
|
|
}
|
|
|
|
// Shellcode gets caved in between the final load command and the first section
|
|
if len(machoFile.Insertion) > 0 {
|
|
binary.Write(w, machoFile.ByteOrder, machoFile.Insertion)
|
|
bytesWritten += uint64(len(machoFile.Insertion))
|
|
}
|
|
|
|
// Sort Sections
|
|
sortedSections := machoFile.Sections[:]
|
|
sort.Slice(sortedSections, func(a, b int) bool { return machoFile.Sections[a].Offset < machoFile.Sections[b].Offset })
|
|
|
|
/*
|
|
var caveOffset, caveSize uint64
|
|
for _, s := range sortedSections {
|
|
if s.SectionHeader.Seg == "__TEXT" && s.Name == "__text" {
|
|
caveOffset = bytesWritten
|
|
caveSize = uint64(s.Offset) - caveOffset
|
|
log.Printf("Code Cave Size: %d - %d = %d\n", s.Offset, caveOffset, caveSize)
|
|
log.Println("Shellcode Size: ", len(machoFile.Insertion))
|
|
break
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Write Sections
|
|
for _, s := range sortedSections {
|
|
|
|
if bytesWritten > uint64(s.Offset) {
|
|
log.Printf("Overlapping Sections in Generated macho: %+v\n", s.Name)
|
|
continue
|
|
}
|
|
if bytesWritten < uint64(s.Offset) {
|
|
pad := make([]byte, uint64(s.Offset)-bytesWritten)
|
|
w.Write(pad)
|
|
bytesWritten += uint64(len(pad))
|
|
//log.Printf("%x: wrote %d padding bytes\n", bytesWritten, len(pad))
|
|
}
|
|
section, err := ioutil.ReadAll(s.Open())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
binary.Write(w, machoFile.ByteOrder, section)
|
|
bytesWritten += uint64(len(section))
|
|
//log.Printf("%x: wrote %d bytes section/segment named: %s %s\n", bytesWritten, uint64(len(section)), s.Name, s.Seg)
|
|
}
|
|
// Write Dynamic Loader Info if it exists
|
|
if machoFile.DylinkInfo != nil {
|
|
// Write Rebase if it exists
|
|
if len(machoFile.DylinkInfo.RebaseDat) > 0 {
|
|
//log.Printf("Rebase Offset: %d", machoFile.DylinkInfo.RebaseOffset)
|
|
if int64(machoFile.DylinkInfo.RebaseOffset)-int64(bytesWritten) > 0 {
|
|
padA := make([]byte, machoFile.DylinkInfo.RebaseOffset-bytesWritten)
|
|
w.Write(padA)
|
|
bytesWritten += uint64(len(padA))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padA))
|
|
}
|
|
//log.Printf("Rebase: %+v \n", machoFile.DylinkInfo.RebaseDat)
|
|
w.Write(machoFile.DylinkInfo.RebaseDat)
|
|
bytesWritten += uint64(machoFile.DylinkInfo.RebaseLen)
|
|
//log.Printf("%x: Wrote raw Rebase, length of: %d", bytesWritten, machoFile.DylinkInfo.RebaseLen)
|
|
}
|
|
//Binding
|
|
if len(machoFile.DylinkInfo.BindingInfoDat) > 0 {
|
|
//log.Printf("Binding Offset: %d", machoFile.DylinkInfo.BindingInfoOffset)
|
|
if int64(machoFile.DylinkInfo.BindingInfoOffset)-int64(bytesWritten) > 0 {
|
|
padB := make([]byte, machoFile.DylinkInfo.BindingInfoOffset-bytesWritten)
|
|
w.Write(padB)
|
|
bytesWritten += uint64(len(padB))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padB))
|
|
}
|
|
//log.Printf("Binding Info: %+v \n", machoFile.DylinkInfo.BindingInfoDat)
|
|
w.Write(machoFile.DylinkInfo.BindingInfoDat)
|
|
bytesWritten += uint64(machoFile.DylinkInfo.BindingInfoLen)
|
|
//log.Printf("%x: Wrote raw Binding Info, length of: %d", bytesWritten, machoFile.DylinkInfo.BindingInfoLen)
|
|
}
|
|
//Lazy
|
|
if len(machoFile.DylinkInfo.LazyBindingDat) > 0 {
|
|
//log.Printf("Lazy Offset: %d", machoFile.DylinkInfo.LazyBindingOffset)
|
|
if int64(machoFile.DylinkInfo.LazyBindingOffset)-int64(bytesWritten) > 0 {
|
|
padD := make([]byte, machoFile.DylinkInfo.LazyBindingOffset-bytesWritten)
|
|
w.Write(padD)
|
|
bytesWritten += uint64(len(padD))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padD))
|
|
}
|
|
//log.Printf("Lazy Binding Data: %+v \n", machoFile.DylinkInfo.LazyBindingDat)
|
|
w.Write(machoFile.DylinkInfo.LazyBindingDat)
|
|
bytesWritten += uint64(machoFile.DylinkInfo.LazyBindingLen)
|
|
//log.Printf("%x: Wrote raw lazybinding, length of: %d", bytesWritten, machoFile.DylinkInfo.LazyBindingLen)
|
|
}
|
|
//Export
|
|
if len(machoFile.DylinkInfo.ExportInfoDat) > 0 {
|
|
//log.Printf("Export Offset: %d", machoFile.DylinkInfo.ExportInfoOffset)
|
|
if int64(machoFile.DylinkInfo.ExportInfoOffset)-int64(bytesWritten) > 0 {
|
|
padE := make([]byte, machoFile.DylinkInfo.ExportInfoOffset-bytesWritten)
|
|
w.Write(padE)
|
|
bytesWritten += uint64(len(padE))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padE))
|
|
}
|
|
//log.Printf("Export Info: %+v \n", machoFile.DylinkInfo.ExportInfoDat)
|
|
w.Write(machoFile.DylinkInfo.ExportInfoDat)
|
|
bytesWritten += uint64(machoFile.DylinkInfo.ExportInfoLen)
|
|
//log.Printf("%x: Wrote raw Export Info, length of: %d", bytesWritten, machoFile.DylinkInfo.ExportInfoLen)
|
|
}
|
|
//Weak
|
|
if len(machoFile.DylinkInfo.WeakBindingDat) > 0 {
|
|
//log.Printf("Weak Offset: %d", machoFile.DylinkInfo.WeakBindingOffset)
|
|
if int64(machoFile.DylinkInfo.WeakBindingOffset)-int64(bytesWritten) > 0 {
|
|
padC := make([]byte, machoFile.DylinkInfo.WeakBindingOffset-bytesWritten)
|
|
w.Write(padC)
|
|
bytesWritten += uint64(len(padC))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padC))
|
|
}
|
|
//log.Printf("Weak Binding: %+v \n", machoFile.DylinkInfo.WeakBindingDat)
|
|
w.Write(machoFile.DylinkInfo.WeakBindingDat)
|
|
bytesWritten += uint64(machoFile.DylinkInfo.WeakBindingLen)
|
|
//log.Printf("%x: Wrote raw Weak Binding, length of: %d", bytesWritten, machoFile.DylinkInfo.WeakBindingLen)
|
|
}
|
|
}
|
|
|
|
// Write the Func Starts if they exist
|
|
if machoFile.FuncStarts != nil {
|
|
//log.Printf("new pad: %d", machoFile.FuncStarts.Offset-bytesWritten)
|
|
if int64(machoFile.FuncStarts.Offset)-int64(bytesWritten) > 0 {
|
|
padY := make([]byte, machoFile.FuncStarts.Offset-bytesWritten)
|
|
w.Write(padY)
|
|
bytesWritten += uint64(len(padY))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padY))
|
|
}
|
|
//log.Printf("FuncStarts: %+v \n", machoFile.FuncStarts)
|
|
w.Write(machoFile.FuncStarts.RawDat)
|
|
bytesWritten += uint64(machoFile.FuncStarts.Len)
|
|
//log.Printf("%x: Wrote raw funcstarts, length of: %d", bytesWritten, machoFile.FuncStarts.Len)
|
|
}
|
|
|
|
// Write the Data in Code Entries if they exist
|
|
if machoFile.DataInCode != nil {
|
|
if int64(machoFile.DataInCode.Offset)-int64(bytesWritten) > 0 {
|
|
padZ := make([]byte, machoFile.DataInCode.Offset-bytesWritten)
|
|
w.Write(padZ)
|
|
bytesWritten += uint64(len(padZ))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padZ))
|
|
}
|
|
//log.Printf("DataInCode: %+v \n", machoFile.DataInCode)
|
|
w.Write(machoFile.DataInCode.RawDat)
|
|
bytesWritten += uint64(machoFile.DataInCode.Len)
|
|
//log.Printf("%x: Wrote raw dataincode, length of: %d", bytesWritten, machoFile.DataInCode.Len)
|
|
}
|
|
|
|
// Write Symbols is next I think
|
|
symtab := machoFile.Symtab
|
|
//log.Printf("Bytes written: %d", bytesWritten)
|
|
//log.Printf("Indirect symbol offset: %d", machoFile.Dysymtab.DysymtabCmd.Indirectsymoff)
|
|
//log.Printf("Locrel offset: %d", machoFile.Dysymtab.Locreloff)
|
|
//log.Printf("Symtab offset: %d", symtab.Symoff)
|
|
//log.Printf("String table offset: %d", symtab.Stroff)
|
|
if int64(symtab.Symoff)-int64(bytesWritten) > 0 {
|
|
pad := make([]byte, uint64(symtab.Symoff)-bytesWritten)
|
|
w.Write(pad)
|
|
bytesWritten += (uint64(symtab.Symoff) - bytesWritten)
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, uint64(symtab.Symoff)-bytesWritten)
|
|
}
|
|
w.Write(symtab.RawSymtab)
|
|
bytesWritten += uint64(len(symtab.RawSymtab))
|
|
//log.Printf("%x: Wrote raw symtab, length of: %d", bytesWritten, len(symtab.RawSymtab))
|
|
|
|
// Write DySymTab next!
|
|
dysymtab := machoFile.Dysymtab
|
|
if int64(dysymtab.Indirectsymoff)-int64(bytesWritten) > 0 {
|
|
pad2 := make([]byte, uint64(dysymtab.Indirectsymoff)-bytesWritten)
|
|
w.Write(pad2)
|
|
bytesWritten += uint64(len(pad2))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(pad2))
|
|
}
|
|
w.Write(dysymtab.RawDysymtab)
|
|
bytesWritten += uint64(len(dysymtab.RawDysymtab))
|
|
//log.Printf("%x: Wrote raw indirect symbols, length of: %d", bytesWritten, len(dysymtab.RawDysymtab))
|
|
|
|
// Write StringTab!
|
|
if int64(symtab.Stroff)-int64(bytesWritten) > 0 {
|
|
pad3 := make([]byte, uint64(symtab.Stroff)-bytesWritten)
|
|
w.Write(pad3)
|
|
bytesWritten += uint64(len(pad3))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(pad3))
|
|
}
|
|
w.Write(symtab.RawStringtab)
|
|
bytesWritten += uint64(len(symtab.RawStringtab))
|
|
//log.Printf("%x: Wrote raw stringtab, length of: %d", bytesWritten, len(symtab.RawStringtab))
|
|
|
|
// Write The Signature Block, if it exists
|
|
//log.Printf("SigBlock Dat: %v", machoFile.SigBlock)
|
|
if machoFile.SigBlock != nil {
|
|
if int64(machoFile.SigBlock.Offset)-int64(bytesWritten) > 0 {
|
|
padX := make([]byte, int64(machoFile.SigBlock.Offset)-int64(bytesWritten))
|
|
w.Write(padX)
|
|
bytesWritten += uint64(len(padX))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padX))
|
|
}
|
|
w.Write(machoFile.SigBlock.RawDat)
|
|
bytesWritten += uint64(machoFile.SigBlock.Len)
|
|
//log.Printf("%x: Wrote raw sigblock, length of: %d", bytesWritten, machoFile.SigBlock.Len)
|
|
}
|
|
|
|
// Write 0s to the end of the final segment
|
|
if int64(FinalSegEnd)-int64(bytesWritten) > 0 {
|
|
pad4 := make([]byte, uint64(FinalSegEnd)-bytesWritten)
|
|
w.Write(pad4)
|
|
bytesWritten += uint64(len(pad4))
|
|
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(pad4))
|
|
}
|
|
|
|
if len(relocData) > 0 {
|
|
if bytesWritten < relocStart {
|
|
pad := make([]byte, relocStart-bytesWritten)
|
|
w.Write(pad)
|
|
bytesWritten += uint64(len(pad))
|
|
}
|
|
w.Write(relocData)
|
|
bytesWritten += uint64(len(relocData))
|
|
}
|
|
|
|
//log.Println("All done!")
|
|
machoBytes := w.Bytes()
|
|
return machoBytes, nil
|
|
}
|
|
|
|
// WriteFile - Creates a new file and writes it using the Bytes func above
|
|
func (machoFile *File) WriteFile(destFile string) error {
|
|
f, err := os.Create(destFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
machoData, err := machoFile.Bytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = f.Write(machoData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WriteFatFile - Creates a new Fat file and multiple machos into it
|
|
func (FatyFile *FatFile) WriteFatFile(destFile string) error {
|
|
f, err := os.Create(destFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
w := bufio.NewWriter(f)
|
|
bytesWritten := uint64(0)
|
|
var FatyMachos []File
|
|
var FatyMachosOffsets []uint32
|
|
|
|
// Fat Header First
|
|
// Magic Bytes
|
|
binary.Write(w, binary.BigEndian, FatyFile.Magic)
|
|
bytesWritten += 4
|
|
// Number of Fat Arches [4 bytes]
|
|
FatArches := uint32(len(FatyFile.Arches))
|
|
binary.Write(w, binary.BigEndian, FatArches)
|
|
bytesWritten += 4
|
|
w.Flush()
|
|
// Arch Size
|
|
for _, arch := range FatyFile.Arches {
|
|
log.Printf("Arch details: %v\n", arch)
|
|
FatyMachos = append(FatyMachos, *(arch.File))
|
|
//Cpu Type
|
|
binary.Write(w, binary.BigEndian, uint32(arch.Cpu))
|
|
bytesWritten += 4
|
|
//Sub CPU type
|
|
binary.Write(w, binary.BigEndian, uint32(arch.SubCpu))
|
|
bytesWritten += 4
|
|
//FileOffset
|
|
FatyMachosOffsets = append(FatyMachosOffsets, arch.Offset)
|
|
binary.Write(w, binary.BigEndian, uint32(arch.Offset))
|
|
bytesWritten += 4
|
|
//Size
|
|
binary.Write(w, binary.BigEndian, uint32(arch.Size))
|
|
bytesWritten += 4
|
|
//Align
|
|
binary.Write(w, binary.BigEndian, uint32(arch.Align))
|
|
bytesWritten += 4
|
|
w.Flush()
|
|
}
|
|
// End of Fat Headers
|
|
|
|
// Write each Macho File at its Offset
|
|
for index, mach := range FatyMachos {
|
|
// Pad to the offset
|
|
if bytesWritten < uint64(FatyMachosOffsets[index]) {
|
|
pad := make([]byte, uint64(FatyMachosOffsets[index])-bytesWritten)
|
|
w.Write(pad)
|
|
bytesWritten += uint64(len(pad))
|
|
}
|
|
// Write the Mach-o file
|
|
machOut, err := mach.Bytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
binary.Write(w, binary.BigEndian, machOut)
|
|
bytesWritten += uint64(len(machOut))
|
|
w.Flush()
|
|
}
|
|
|
|
return nil
|
|
}
|