Exemple #1
0
func (loader *PELoader) resolveThunkTable(
	ws *workspace.Workspace,
	mod *workspace.LoadedModule,
	moduleName string,
	rvaTable AS.RVA) error {

	var offset AS.RVA = rvaTable
	for {
		rvaImport, e := mod.MemReadRva(ws, offset)
		check(e)

		if rvaImport == 0x0 {
			break
		}

		if Flags64(rvaImport).isSet(FLAG_IMPORT_BY_ORDINAL) {
			logrus.Infof("  import by ordinal: %03x", uint64(rvaImport)&uint64(0x7FFFFFFF))
			// TODO: replace thunk with handler
			// notes:
			//    32: PUSH 0xAABBCCDD --> 68 DD CC BB AA
			//        JMP  0xAABBCCDD --> E9 D9 CC BB AA  ugh, relative jump. do a push/ret instead.
			//        RET             --> C3
			//
			mod.Imports[offset] = workspace.LinkedSymbol{
				ModuleName: moduleName,
				SymbolName: fmt.Sprintf("ordinal-%x", uint64(rvaImport)&uint64(0x7FFFFFFF)),
			}
		} else {
			d, e := mod.MemRead(ws, rvaImport, 0x100)
			check(e)

			p := bytes.NewBuffer(d)
			var importByName ImageImportByName
			binary.Read(p, binary.LittleEndian, &importByName.Hint)

			importByName.Name, e = readAscii(d[2:])
			check(e)

			logrus.Infof("  import by name: %s@%s", importByName.Name, rvaImport)
			// TODO: replace thunk with handler
			mod.Imports[offset] = workspace.LinkedSymbol{
				ModuleName: moduleName,
				SymbolName: importByName.Name,
			}
		}

		offset += 4
	}
	return nil
}
Exemple #2
0
func (loader *PELoader) resolveImports(
	ws *workspace.Workspace,
	mod *workspace.LoadedModule,
	dataDirectory [16]pe.DataDirectory) error {

	// since we always map at ImageBase, we don't need to apply (32bit) relocs
	// TODO: check 64bit reloc types

	importDirectory := dataDirectory[1]
	importRva := AS.RVA(importDirectory.VirtualAddress)
	importSize := importDirectory.Size

	logrus.Infof("import rva: %s", importRva)
	logrus.Infof("import size: 0x%x", importSize)

	d, e := mod.MemRead(ws, AS.RVA(importDirectory.VirtualAddress), uint64(importDirectory.Size))
	check(e)

	p := bytes.NewBuffer(d)
	for {
		var dir ImageImportDirectory
		binary.Read(p, binary.LittleEndian, &dir.rvaOriginalThunkTable)
		logrus.Infof("rva import lookup table: 0x%x", dir.rvaOriginalThunkTable)
		if dir.rvaOriginalThunkTable == 0 {
			break
		}
		binary.Read(p, binary.LittleEndian, &dir.TimeDateStamp)
		logrus.Infof("time date stamp: 0x%x", dir.TimeDateStamp)

		binary.Read(p, binary.LittleEndian, &dir.ForwarderChain)
		logrus.Infof("forwarder chain: 0x%x", dir.ForwarderChain)

		binary.Read(p, binary.LittleEndian, &dir.rvaModuleName)

		moduleNameBuf, e := mod.MemRead(ws, AS.RVA(dir.rvaModuleName), 0x100)
		check(e)
		moduleName, e := readAscii(moduleNameBuf)
		check(e)

		logrus.Infof("module name: %s", string(moduleName))

		binary.Read(p, binary.LittleEndian, &dir.rvaThunkTable)
		loader.resolveThunkTable(ws, mod, moduleName, AS.RVA(dir.rvaThunkTable))
	}

	return nil
}
Exemple #3
0
func (loader *PELoader) resolveExports(
	ws *workspace.Workspace,
	mod *workspace.LoadedModule,
	dataDirectory [16]pe.DataDirectory) error {
	exportDirectory := dataDirectory[0]
	exportRva := AS.RVA(exportDirectory.VirtualAddress)
	exportSize := exportDirectory.Size

	logrus.Infof("export rva: %s", exportRva)
	logrus.Infof("export size: 0x%x", exportSize)

	d, e := mod.MemRead(ws, AS.RVA(exportDirectory.VirtualAddress), uint64(exportDirectory.Size))
	check(e)

	p := bytes.NewBuffer(d)
	var dir ImageExportDirectory

	binary.Read(p, binary.LittleEndian, &dir.Characteristics)
	binary.Read(p, binary.LittleEndian, &dir.TimeDateStamp)
	binary.Read(p, binary.LittleEndian, &dir.MajorVersion)
	binary.Read(p, binary.LittleEndian, &dir.MinorVersion)
	binary.Read(p, binary.LittleEndian, &dir.rvaName)
	binary.Read(p, binary.LittleEndian, &dir.Base)
	binary.Read(p, binary.LittleEndian, &dir.NumberOfFunctions)
	binary.Read(p, binary.LittleEndian, &dir.NumberOfNames)
	binary.Read(p, binary.LittleEndian, &dir.rvaAddressOfFunctions)
	binary.Read(p, binary.LittleEndian, &dir.rvaAddressOfNames)
	binary.Read(p, binary.LittleEndian, &dir.rvaAddressOfNameOrdinals)

	if dir.rvaAddressOfFunctions == 0 {
		panic("address of functions is NULL")
	}

	exportModuleNameBuf, e := mod.MemRead(ws, AS.RVA(dir.rvaName), 0x100)
	check(e)
	exportModuleName, e := readAscii(exportModuleNameBuf)
	logrus.Infof("export name: %s", string(exportModuleName))
	check(e)

	logrus.Infof("time date stamp: 0x%x", dir.TimeDateStamp)

	// note closure over dir, mod, ws
	readFunctionRva := func(i uint32) (AS.RVA, error) {
		if i > dir.NumberOfFunctions {
			panic("function index too large")
		}
		// sizeof(RVA) is always 4 bytes, even on x64
		return mod.MemReadRva(ws, AS.RVA(dir.rvaAddressOfFunctions+4*i))
	}

	// isForwardedExport returns true when the provided RVA falls within the
	//  export directory table, which is used to signify that an export is
	//  fowarded to another module.
	// implementation: note closure over loader
	isForwardedExport := func(rvaFn AS.RVA) bool {
		if uint32(rvaFn) < exportDirectory.VirtualAddress {
			return false
		}
		if uint32(rvaFn) >= exportDirectory.VirtualAddress+exportDirectory.Size {
			return false
		}
		return true
	}

	// implementation: node closure over mod, ws
	readForwardedSymbol := func(rvaFn AS.RVA) (workspace.LinkedSymbol, error) {
		var forwardedSymbol workspace.LinkedSymbol
		forwardedNameBuf, e := mod.MemRead(ws, AS.RVA(rvaFn), 0x100)
		check(e)
		forwardedName, e := readAscii(forwardedNameBuf)
		check(e)

		i := strings.LastIndex(forwardedName, ".")
		if i == -1 {
			panic("expected to find a '.' in the module name")
		}
		if i >= len(forwardedName) {
			panic("module name ends in period")
		}

		forwardedSymbol.ModuleName = forwardedName[:i]
		forwardedSymbol.SymbolName = forwardedName[i+1:]
		return forwardedSymbol, nil
	}

	// resolve exports by ordinals first
	for i := uint32(0); i < dir.NumberOfFunctions; i++ {
		ordinal := uint16(i + dir.Base)
		rvaFn, e := readFunctionRva(i)
		check(e)

		isForwarded := isForwardedExport(rvaFn)

		sym := workspace.ExportedSymbol{
			RVA:         rvaFn,
			IsForwarded: isForwarded,
		}

		if isForwarded {
			fsym, e := readForwardedSymbol(rvaFn)
			check(e)
			sym.ForwardedSymbol = fsym
		}
		mod.ExportsByOrdinal[ordinal] = sym

		if isForwarded {
			logrus.Infof(" export: (ordinal) %x: %s -> %s.%s",
				ordinal, rvaFn, sym.ForwardedSymbol.ModuleName, sym.ForwardedSymbol.SymbolName)
		} else {
			logrus.Infof(" export: (ordinal) %x: %s", ordinal, rvaFn)
		}
	}

	// resolve exports by name
	for i := uint32(0); i < dir.NumberOfNames; i++ {
		// sizeof(RVA) is always 4 bytes, even on x64
		rvaName, e := mod.MemReadRva(ws, AS.RVA(dir.rvaAddressOfNames+4*i))
		check(e)
		// sizeof(ordinal) is always 2 bytes
		nameOrdinal, e := mod.MemReadShort(ws, AS.RVA(dir.rvaAddressOfNameOrdinals+2*i))
		check(e)
		rvaFn, e := readFunctionRva(uint32(nameOrdinal))
		check(e)

		nameBuf, e := mod.MemRead(ws, AS.RVA(rvaName), 0x100)
		check(e)
		name, e := readAscii(nameBuf)
		check(e)

		isForwarded := isForwardedExport(rvaFn)

		sym := workspace.ExportedSymbol{
			RVA:         rvaFn,
			IsForwarded: isForwarded,
		}

		if isForwarded {
			fsym, e := readForwardedSymbol(rvaFn)
			check(e)
			sym.ForwardedSymbol = fsym
		}
		mod.ExportsByName[name] = sym

		if isForwarded {
			logrus.Infof(" export: %s: %s -> %s.%s",
				name, rvaFn, sym.ForwardedSymbol.ModuleName, sym.ForwardedSymbol.SymbolName)
		} else {
			logrus.Infof(" export: %s: %s", name, rvaFn)
		}
	}

	return nil
}