basic POC for string removal
This commit is contained in:
parent
925429c4a9
commit
9b2796b2a1
@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
@ -557,6 +558,246 @@ func (mc *MachoContext) RemoveExportTrie() {
|
||||
}
|
||||
}
|
||||
|
||||
// this function breaks the file by adding another segment at
|
||||
// the end of the file
|
||||
func (mc *MachoContext) RemoveStrings() {
|
||||
// add a new writable segment with a section
|
||||
// loop over the instructions for adrp,add instructions
|
||||
// if the access points to old cstring section, update
|
||||
// with new values from the new segment and section.
|
||||
|
||||
// data references, e.g., pointer to string, are compiled
|
||||
// into Rebase symbol, so actively check for rebase and
|
||||
// rewrite the rebase value to new segment, section offset.
|
||||
|
||||
// save the strings into a file for recovery. should keep linear
|
||||
// format as before, or customized order, if want complex.
|
||||
|
||||
// __LINKEDIT must be at the end of the section for the binary
|
||||
// to be able to resign, so we have to move __LINKEDIT down
|
||||
// by how much? by the page aligned size of the added segment
|
||||
|
||||
// but __LINKEDIT also contains data for link time data
|
||||
// (fixed ups and bytecode chain) so have to modify
|
||||
// their reference down too,
|
||||
// symtab and dysymtab can be ignored, by removing them lmao
|
||||
|
||||
fmt.Println("TODO: Removing strings")
|
||||
|
||||
// identify the cstring region
|
||||
|
||||
var cstring *Section64;
|
||||
for _, command := range mc.commands {
|
||||
switch command.(type) {
|
||||
case *Segment64:
|
||||
var segment = command.(*Segment64)
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__cstring")) == 0 {
|
||||
cstring = section.(*Section64)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// no section __cstring in __TEXT
|
||||
if cstring == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get last segment, as the start point we extend from
|
||||
// this assumes that the segment are ordered correctly,
|
||||
// first segment offset is lower then second segment offset,
|
||||
// and so on, yielding last segment is the last part of the
|
||||
// binary. Our new segment add another part to the binary
|
||||
// at the end.
|
||||
|
||||
// last segment is always the linkedits
|
||||
last_segment := mc.Segments()[len(mc.Segments()) - 1]
|
||||
fmt.Printf("last segment %v %s\n", last_segment, string(last_segment.SegName()))
|
||||
|
||||
// all data must be inside the segment (or in header)
|
||||
|
||||
// occupy the segment of linkedit and move linkedit down
|
||||
|
||||
segstart := last_segment.Vmaddr()
|
||||
|
||||
// segment must be page aligned, and the size is based on
|
||||
// the section size
|
||||
secstart := segstart
|
||||
secsize := cstring.Size()
|
||||
|
||||
filestart := last_segment.Fileoff()
|
||||
// align to page address, not sure if neccessary, because the
|
||||
// loader can pick up from anywhere and load in memory (mmap)
|
||||
if filestart % 0x4000 != 0 {
|
||||
filestart += 0x4000 - (filestart % 0x4000)
|
||||
}
|
||||
|
||||
fmt.Printf("section size %x\n", secsize)
|
||||
secsize_aligned := uint64(0)
|
||||
if secsize % 0x4000 == 0 {
|
||||
// very rare, but possible, it occupies whole pages
|
||||
secsize_aligned = secsize
|
||||
} else {
|
||||
secsize_aligned = secsize + (0x4000 - (secsize % 0x4000))
|
||||
}
|
||||
filesize := secsize_aligned
|
||||
|
||||
segname := make([]byte, 16)
|
||||
copy(segname, []byte("__BSHIELD"))
|
||||
secname := make([]byte, 16)
|
||||
copy(secname, []byte("__secrets"))
|
||||
|
||||
|
||||
fmt.Printf("segstart %x\n", segstart)
|
||||
fmt.Printf("file_start %x\n", filestart)
|
||||
|
||||
// size of the section and segment is defined by the total
|
||||
// space for strings required
|
||||
|
||||
section := Section64{
|
||||
sectname: secname,
|
||||
segname: segname,
|
||||
addr: secstart,
|
||||
size: secsize,
|
||||
offset: uint32(filestart),
|
||||
align: 3, // idk, see AddSection
|
||||
reloff: 0,
|
||||
nreloc: 0,
|
||||
flags: 0,
|
||||
reserved1: 0,
|
||||
reserved2: 0,
|
||||
reserved3: 0,
|
||||
}
|
||||
string_segment := Segment64{
|
||||
c: LoadCmd{cmd: LC_SEGMENT_64, cmdsize: 0},
|
||||
segname: segname,
|
||||
vmaddr: segstart,
|
||||
vmsize: secsize_aligned,
|
||||
fileoff: filestart,
|
||||
filesize: filesize,
|
||||
maxprot: 3, // read/write
|
||||
initprot: 3, // read/write
|
||||
nsects: 1,
|
||||
flags: 0,
|
||||
sections: []*Section64{§ion},
|
||||
}
|
||||
|
||||
// rewrite the header to be correct
|
||||
// mc.AddLoadCmd(&string_segment)
|
||||
|
||||
edit_segname := make([]byte, 16)
|
||||
copy(edit_segname, []byte("__LINKEDIT"))
|
||||
edit_segment := Segment64{
|
||||
c: LoadCmd{cmd: LC_SEGMENT_64, cmdsize: 0},
|
||||
segname: edit_segname,
|
||||
vmaddr: segstart + secsize_aligned, // move down
|
||||
vmsize: last_segment.Vmsize(),
|
||||
fileoff: filestart + filesize,
|
||||
filesize: last_segment.Filesize(),
|
||||
maxprot: 1, // read/write
|
||||
initprot: 1, // read/write
|
||||
nsects: 0,
|
||||
flags: 0,
|
||||
sections: []*Section64{},
|
||||
}
|
||||
|
||||
// modify the segment list
|
||||
mc.segments[len(mc.segments) - 1] = &string_segment
|
||||
mc.segments = append(mc.segments, &edit_segment)
|
||||
|
||||
// modify the command list
|
||||
for i, cmd := range mc.commands {
|
||||
if cmd.(*Segment64) == last_segment.(*Segment64) {
|
||||
mc.commands = append(mc.commands[:i + 1], mc.commands[i:]...)
|
||||
mc.commands[i] = &string_segment
|
||||
mc.commands[i + 1] = &edit_segment
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// modify offset in other commands to use new link edit offset
|
||||
|
||||
edit_offset_migrate := func (file_offset uint64) uint64 {
|
||||
// they should keep the old offset,
|
||||
// but the base related to linkedit is modified
|
||||
relative_offset := file_offset - last_segment.Fileoff()
|
||||
return relative_offset + edit_segment.Fileoff()
|
||||
}
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if lcmd, ok := cmd.(*LinkEdit); ok {
|
||||
lcmd.dataoff = uint32(edit_offset_migrate(uint64(lcmd.dataoff)))
|
||||
}
|
||||
if lcmd, ok := cmd.(*Symtab); ok {
|
||||
lcmd.stroff = uint32(edit_offset_migrate(uint64(lcmd.stroff)))
|
||||
lcmd.symoff = uint32(edit_offset_migrate(uint64(lcmd.symoff)))
|
||||
}
|
||||
if lcmd, ok := cmd.(*DySymtab); ok {
|
||||
lcmd.indirectsymoff = uint32(edit_offset_migrate(uint64(lcmd.indirectsymoff)))
|
||||
}
|
||||
}
|
||||
|
||||
mc.RewriteHeader()
|
||||
|
||||
|
||||
tmp_file := mc.file.Name()
|
||||
|
||||
// has to reopen file as append
|
||||
mc.file.Close()
|
||||
mc.file, _ = os.OpenFile(tmp_file, os.O_RDWR | os.O_APPEND, 0644)
|
||||
|
||||
// make extra space
|
||||
expected_end := edit_segment.Fileoff() + edit_segment.Filesize()
|
||||
end, _ := mc.file.Seek(0, io.SeekEnd)
|
||||
if end < int64(expected_end) {
|
||||
mc.file.WriteAt(make([]byte, expected_end - uint64(end)), end)
|
||||
}
|
||||
|
||||
// close and reopen as read/write, the buffer at the end is now empty
|
||||
mc.file.Close()
|
||||
mc.file, _ = os.OpenFile(tmp_file, os.O_RDWR, 0644)
|
||||
|
||||
// peek at old link edit and move down
|
||||
old_linkedit := make([]byte, last_segment.Filesize())
|
||||
mc.file.ReadAt(old_linkedit, int64(last_segment.Fileoff()))
|
||||
mc.file.WriteAt(old_linkedit, int64(edit_segment.Fileoff()))
|
||||
|
||||
// prepare dummy bytes into new string segment, 0 for now
|
||||
dummy := make([]byte, edit_segment.Fileoff() - string_segment.Fileoff())
|
||||
copy(dummy, []byte("We R BShield\n"))
|
||||
mc.file.WriteAt(dummy, int64(string_segment.Fileoff()))
|
||||
|
||||
// loop over __TEXT,__text and find all occurances of (adrp, add)
|
||||
// edit the offset to points to new region
|
||||
// because adrp sets the register to the address page at its address
|
||||
// (for page align 0x4000), e.g.,
|
||||
// `adrp x0` instruction at 0x100003f70, yields x0 = 0x100003000
|
||||
// technically, adrp can offset as far as 33-bit, roughly 4GB memory
|
||||
// so we should be very free, because very few program goes this far
|
||||
// but if this happens, god bless you
|
||||
|
||||
// for the demo, we can try to hard code, (addresses are virtual)
|
||||
// text should be in 0x100003f70
|
||||
// string to reference is 0x100008000
|
||||
|
||||
// encoding ADRP is actually hard hmmge?
|
||||
|
||||
// revert to offset
|
||||
mc.file.WriteAt([]byte{0x20, 0x00, 0x00, 0xb0}, int64(0x000003f70))
|
||||
mc.file.WriteAt([]byte{0x00, 0x00, 0x00, 0x91}, int64(0x000003f74))
|
||||
|
||||
// modify the rebase table (for both opcode and fixups chain versions)
|
||||
// this is for pointer references
|
||||
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
|
||||
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
var ret Section
|
||||
|
Loading…
Reference in New Issue
Block a user