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 }
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 }