basic POC for string removal
This commit is contained in:
parent
925429c4a9
commit
9b2796b2a1
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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 {
|
func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
|
||||||
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||||
var ret Section
|
var ret Section
|
||||||
|
Loading…
Reference in New Issue
Block a user