Ejemplo n.º 1
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
}