Example #1
0
    def calculate(self):
        addr_space = utils.load_as(self._config)

        # we currently don't use this on x64 because for some reason the 
        # x64 version actually doesn't create a DisplayVersion value 
        memory_model = addr_space.profile.metadata.get('memory_model')
        if memory_model == '32bit':
            regapi = registryapi.RegistryApi(self._config)
            regapi.reset_current()
            regapi.set_current(hive_name = "software")
            x86key = "Microsoft\\Windows\\CurrentVersion\\Uninstall"
            x64key = "Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
            for subkey in regapi.reg_get_all_subkeys(None, key = x86key):
                if str(subkey.Name) == "TrueCrypt":
                    subpath = x86key + "\\" + subkey.Name
                    version = regapi.reg_get_value("software", 
                                            key = subpath, 
                                            value = "DisplayVersion")
                    if version:
                        yield "Registry Version", "{0} Version {1}".format(
                            str(subkey.Name),
                            version)

        scanner = TrueCryptPassphrase(self._config)
        for offset, passphrase in scanner.calculate():
            yield "Password", "{0} at offset {1:#x}".format(
                        passphrase, offset)

        for proc in tasks.pslist(addr_space):
            if str(proc.ImageFileName).lower() == "truecrypt.exe":     
                yield "Process", "{0} at {1:#x} pid {2}".format(
                        proc.ImageFileName,
                        proc.obj_offset, 
                        proc.UniqueProcessId)   

        scanner = svcscan.SvcScan(self._config)
        for service in scanner.calculate():
            name = str(service.ServiceName.dereference())
            if name == "truecrypt":
                yield "Service", "{0} state {1}".format(
                        name, 
                        service.State)

        for mod in modules.lsmod(addr_space):
            basename = str(mod.BaseDllName or '').lower()
            fullname = str(mod.FullDllName or '').lower()
            if (basename.endswith("truecrypt.sys") or 
                        fullname.endswith("truecrypt.sys")):
                yield "Kernel Module",  "{0} at {1:#x} - {2:#x}".format(
                        mod.BaseDllName, 
                        mod.DllBase, 
                        mod.DllBase + mod.SizeOfImage)

        scanner = filescan.SymLinkScan(self._config)
        for symlink in scanner.calculate():
            object_header = symlink.get_object_header()
            if "TrueCryptVolume" in str(symlink.LinkTarget or ''):
                yield "Symbolic Link", "{0} -> {1} mounted {2}".format(
                        str(object_header.NameInfo.Name or ''), 
                        str(symlink.LinkTarget or ''), 
                        str(symlink.CreationTime or ''))

        scanner = filescan.FileScan(self._config)
        for fileobj in scanner.calculate():
            filename = str(fileobj.file_name_with_device() or '')
            if "TrueCryptVolume" in filename:
                yield "File Object", "{0} at {1:#x}".format(
                        filename,
                        fileobj.obj_offset)
        
        scanner = filescan.DriverScan(self._config)
        for driver in scanner.calculate():
            object_header = driver.get_object_header() 
            driverext = driver.DriverExtension
            drivername = str(driver.DriverName or '')
            servicekey = str(driverext.ServiceKeyName or '')
            if (drivername.endswith("truecrypt") or 
                        servicekey.endswith("truecrypt")):
                yield "Driver", "{0} at {1:#x} range {2:#x} - {3:#x}".format(
                        drivername, 
                        driver.obj_offset, 
                        driver.DriverStart, 
                        driver.DriverStart + driver.DriverSize)
                for device in driver.devices():
                    header = device.get_object_header()
                    devname = str(header.NameInfo.Name or '')
                    type = devicetree.DEVICE_CODES.get(device.DeviceType.v())
                    yield "Device", "{0} at {1:#x} type {2}".format(
                        devname or "<HIDDEN>", 
                        device.obj_offset, 
                        type or "UNKNOWN")
                    if type == "FILE_DEVICE_DISK":
                        data = addr_space.read(device.DeviceExtension, 2000)
                        ## the file-hosted container path. no other fields in
                        ## the struct are character based, so we should not 
                        ## hit false positives on this scan. 
                        offset = data.find("\\\x00?\x00?\x00\\\x00")
                        if offset == -1:
                            container = "<HIDDEN>"
                        else:
                            container = obj.Object("String", length = 255, 
                                        offset = device.DeviceExtension + offset, 
                                        encoding = "utf16",
                                        vm = addr_space)
                        yield "Container", "Path: {0}".format(container)
Example #2
0
                self.suspicious[suspiciousline] = {
                    "reason":
                    "Process flagged: " + self.suspiciouspids[
                        thread.Cid.UniqueProcess.v()]["reason"],
                    "color":
                    "YELLOW"
                }
            if image == "UNKNOWN" or thread.Cid.UniqueProcess.v() not in pids:
                self.suspicious[suspiciousline] = {
                    "reason": "UNKNOWN IMAGE",
                    "color": "YELLOW"
                }

        data = []
        if "Symlink" in self._config.TYPE:
            data = filescan.SymLinkScan(self._config).calculate()
        for link in data:
            objct = link.get_object_header()
            line = "[{6}SYMLINK]{0} {1}->{2}{0} POffset: {3}/Ptr: {4}/Hnd: {5}".format(
                "" if body else "|", str(objct.NameInfo.Name or ''),
                str(link.LinkTarget or ''), link.obj_offset,
                objct.PointerCount, objct.HandleCount, self._config.MACHINE)
            yield self.getoutput(line, link.CreationTime, body=body)

        data = []
        if "TimeDateStamp" in self._config.TYPE:
            data = moddump.ModDump(self._config).calculate()
        for aspace, procs, mod_base, mod_name in data:
            mod_name = str(mod_name or '')
            space = tasks.find_space(aspace, procs, mod_base)
            if space != None:
Example #3
0
    def calculate(self):
        if (self._config.HIVE
                or self._config.USER) and "Registry" not in self._config.TYPE:
            debug.error(
                "You must use --registry in conjuction with -H/--hive and/or -U/--user"
            )
        if self._config.TYPE != None:
            for t in self._config.TYPE.split(","):
                if t.strip() not in self.types and t.strip() != "Registry":
                    debug.error(
                        "You have entered an incorrect type: {0}".format(t))

        addr_space = utils.load_as(self._config)
        version = (
            addr_space.profile.metadata.get('major', 0),
            addr_space.profile.metadata.get('minor', 0),
        )

        pids = {}  # dictionary of process IDs/ImageFileName

        body = False
        if self._config.OUTPUT == "body":
            body = True
        if self._config.MACHINE != "":
            self._config.update("MACHINE", "{0} ".format(self._config.MACHINE))

        if "ImageDate" in self._config.TYPE:
            im = imageinfo.ImageInfo(self._config).get_image_time(addr_space)
            yield self.getoutput(
                "[{0}LIVE RESPONSE]{1} (System time){1}".format(
                    self._config.MACHINE, "" if body else "|"),
                im['ImageDatetime'],
                body=body,
            )

        if version <= (6, 1) and "IEHistory" in self._config.TYPE:
            self._config.update("LEAK", True)
            data = iehistory.IEHistory(self._config).calculate()
            for process, record in data:
                ## Extended fields are available for these records
                if record.obj_name == "_URL_RECORD":
                    line = "[{6}IEHISTORY]{0} {1}->{5}{0} PID: {2}/Cache type \"{3}\" at {4:#x}".format(
                        "" if body else "|",
                        process.ImageFileName,
                        process.UniqueProcessId,
                        record.Signature,
                        record.obj_offset,
                        record.Url,
                        self._config.MACHINE,
                    )

                    yield self.getoutput(
                        line,
                        record.LastModified,
                        end=record.LastAccessed,
                        body=body,
                    )
            self._config.remove_option("REDR")
            self._config.remove_option("LEAK")

        psx = []
        if ("Process" in self._config.Type
                or "TimeDateStamp" in self._config.Type
                or "LoadTime" in self._config.Type
                or "_CM_KEY_BODY" in self._config.Type):
            psx = psxview.PsXview(self._config).calculate()
        for offset, eprocess, ps_sources in psx:
            pids[eprocess.UniqueProcessId.v()] = eprocess.ImageFileName
            if "Process" in self._config.TYPE:
                line = "[{5}PROCESS]{0} {1}{0} PID: {2}/PPID: {3}/POffset: 0x{4:08x}".format(
                    "" if body else "|",
                    eprocess.ImageFileName,
                    eprocess.UniqueProcessId,
                    eprocess.InheritedFromUniqueProcessId,
                    offset,
                    self._config.MACHINE,
                )

                yield self.getoutput(line,
                                     eprocess.CreateTime,
                                     end=eprocess.ExitTime,
                                     body=body)

            if not hasattr(eprocess.obj_vm, "vtop"):
                eprocess = taskmods.DllList(
                    self._config).virtual_process_from_physical_offset(
                        addr_space, eprocess.obj_offset)
                if eprocess == None:
                    continue
            else:
                ps_ad = eprocess.get_process_address_space()
                if ps_ad == None:
                    continue

            if version[0] == 5 and "Process" in self._config.TYPE:
                line = "[{5}PROCESS LastTrimTime]{0} {1}{0} PID: {2}/PPID: {3}/POffset: 0x{4:08x}".format(
                    "" if body else "|",
                    eprocess.ImageFileName,
                    eprocess.UniqueProcessId,
                    eprocess.InheritedFromUniqueProcessId,
                    offset,
                    self._config.MACHINE,
                )
                yield self.getoutput(line, eprocess.Vm.LastTrimTime, body=body)

            if (eprocess.ObjectTable.HandleTableList
                    and "_CM_KEY_BODY" in self._config.TYPE):
                for handle in eprocess.ObjectTable.handles():
                    if not handle.is_valid():
                        continue

                    name = ""
                    object_type = handle.get_object_type()
                    if object_type == "Key":
                        key_obj = handle.dereference_as("_CM_KEY_BODY")
                        name = key_obj.full_key_name()
                        line = "[{6}Handle (Key)]{0} {1}{0} {2} PID: {3}/PPID: {4}/POffset: 0x{5:08x}".format(
                            "" if body else "|",
                            name,
                            eprocess.ImageFileName,
                            eprocess.UniqueProcessId,
                            eprocess.InheritedFromUniqueProcessId,
                            offset,
                            self._config.MACHINE,
                        )
                        yield self.getoutput(
                            line,
                            key_obj.KeyControlBlock.KcbLastWriteTime,
                            body=body,
                        )

            if eprocess.Peb == None or eprocess.Peb.ImageBaseAddress == None:
                continue
            # Get DLL PE timestamps for Wow64 processes (excluding 64-bit ones)
            if eprocess.IsWow64 and "TimeDateStamp" in self._config.TYPE:
                for vad, address_space in eprocess.get_vads(
                        vad_filter=eprocess._mapped_file_filter):
                    if vad.FileObject.FileName:
                        name = str(vad.FileObject.FileName).lower()
                        basename = ntpath.basename(name)
                        if not basename.endswith("dll") or basename in [
                                "wow64cpu.dll",
                                "ntdll.dll",
                                "wow64.dll",
                                "wow64win.dll",
                        ]:
                            continue
                        data = ps_ad.zread(vad.Start, vad.Length)
                        bufferas = addrspace.BufferAddressSpace(self._config,
                                                                data=data)
                        try:
                            pe_file = obj.Object("_IMAGE_DOS_HEADER",
                                                 offset=0,
                                                 vm=bufferas)
                            header = pe_file.get_nt_header()
                        except ValueError as ve:
                            continue
                        line = "[{7}PE HEADER 32-bit (dll)]{0} {4}{0} Process: {1}/PID: {2}/PPID: {3}/Process POffset: 0x{5:08x}/DLL Base: 0x{6:08x}".format(
                            "" if body else "|",
                            eprocess.ImageFileName,
                            eprocess.UniqueProcessId,
                            eprocess.InheritedFromUniqueProcessId,
                            basename,
                            offset,
                            vad.Start,
                            self._config.MACHINE,
                        )
                        yield self.getoutput(line,
                                             header.FileHeader.TimeDateStamp,
                                             body=body)

            # get DLL PE timestamps
            mods = dict()
            if ("TimeDateStamp" in self._config.TYPE
                    or "LoadTime" in self._config.TYPE):
                mods = dict((mod.DllBase.v(), mod)
                            for mod in eprocess.get_load_modules())
            for mod in list(mods.values()):
                basename = str(mod.BaseDllName or "")
                if basename == str(eprocess.ImageFileName):
                    line = "[{7}PE HEADER (exe)]{0} {4}{0} Process: {1}/PID: {2}/PPID: {3}/Process POffset: 0x{5:08x}/DLL Base: 0x{6:08x}".format(
                        "" if body else "|",
                        eprocess.ImageFileName,
                        eprocess.UniqueProcessId,
                        eprocess.InheritedFromUniqueProcessId,
                        basename,
                        offset,
                        mod.DllBase.v(),
                        self._config.MACHINE,
                    )
                else:
                    line = "[{7}PE HEADER (dll)]{0} {4}{0} Process: {1}/PID: {2}/PPID: {3}/Process POffset: 0x{5:08x}/DLL Base: 0x{6:08x}".format(
                        "" if body else "|",
                        eprocess.ImageFileName,
                        eprocess.UniqueProcessId,
                        eprocess.InheritedFromUniqueProcessId,
                        basename,
                        offset,
                        mod.DllBase.v(),
                        self._config.MACHINE,
                    )
                if "TimeDateStamp" in self._config.TYPE:
                    yield self.getoutput(line, mod.TimeDateStamp, body=body)
                    line2 = "[{7}PE DEBUG]{0} {4}{0} Process: {1}/PID: {2}/PPID: {3}/Process POffset: 0x{5:08x}/DLL Base: 0x{6:08x}".format(
                        "" if body else "|",
                        eprocess.ImageFileName,
                        eprocess.UniqueProcessId,
                        eprocess.InheritedFromUniqueProcessId,
                        basename,
                        offset,
                        mod.DllBase.v(),
                        self._config.MACHINE,
                    )
                    yield self.getoutput(
                        line2,
                        mod.get_debug_directory().TimeDateStamp,
                        body=body,
                    )
                if (hasattr(mod, "LoadTime")
                        and "LoadTime" in self._config.TYPE):
                    temp = line.replace(
                        "[{0}PE HEADER ".format(self._config.MACHINE),
                        "[{0}DLL LOADTIME ".format(self._config.MACHINE),
                    )
                    yield self.getoutput(temp, mod.LoadTime, body=body)

        # Get Sockets and Evtlogs XP/2k3 only
        if version[0] == 5:
            # socks = sockets.Sockets(self._config).calculate()
            socks = []
            if "Socket" in self._config.TYPE:
                socks = sockscan.SockScan(self._config).calculate(
                )  # you can use sockscan instead if you uncomment
            for sock in socks:
                la = "{0}:{1}".format(sock.LocalIpAddress, sock.LocalPort)
                line = "[{6}SOCKET]{0} LocalIP: {2}/Protocol: {3}({4}){0} PID: {1}/POffset: 0x{5:#010x}".format(
                    "" if body else "|",
                    sock.Pid,
                    la,
                    sock.Protocol,
                    protos.protos.get(sock.Protocol.v(), "-"),
                    sock.obj_offset,
                    self._config.MACHINE,
                )

                yield self.getoutput(line, sock.CreateTime, body=body)

            stuff = []
            if "EvtLog" in self._config.TYPE:
                evt = evtlogs.EvtLogs(self._config)
                stuff = evt.calculate()
            for name, buf in stuff:
                for fields in evt.parse_evt_info(name, buf, rawtime=True):
                    line = "[{8}EVT LOG]{0} {1}{0} {2}/{3}/{4}/{5}/{6}/{7}".format(
                        "" if body else "|",
                        fields[1],
                        fields[2],
                        fields[3],
                        fields[4],
                        fields[5],
                        fields[6],
                        fields[7],
                        self._config.MACHINE,
                    )
                    yield self.getoutput(line, fields[0], body=body)
        elif version <= (6, 1):
            # Vista+
            nets = []
            if "Socket" in self._config.TYPE:
                nets = netscan.Netscan(self._config).calculate()
            for net_object, proto, laddr, lport, raddr, rport, state in nets:

                if net_object.CreateTime.v() == 0:
                    continue

                if raddr == "*" and rport == "*":
                    conn = "{0}:{1}".format(laddr, lport)
                    socket_type = "SOCKET"
                else:
                    conn = "{0}:{1} -> {2}:{3}".format(laddr, lport, raddr,
                                                       rport)
                    socket_TYPE = "CONNECTION"

                line = (
                    "[{6}NETWORK {7}]{0} {2}{0} {1}/{3}/{4}/{5:<#10x}".format(
                        "" if body else "|",
                        net_object.Owner.UniqueProcessId,
                        conn,
                        proto,
                        state,
                        net_object.obj_offset,
                        self._config.MACHINE,
                        socket_type,
                    ))

                yield self.getoutput(line, net_object.CreateTime, body=body)

        # Get threads
        threads = []
        if "Thread" in self._config.TYPE:
            threads = modscan.ThrdScan(self._config).calculate()
        for thread in threads:
            image = pids.get(thread.Cid.UniqueProcess.v(), "UNKNOWN")
            line = "[{4}THREAD]{0} {1}{0} PID: {2}/TID: {3}".format(
                "" if body else "|",
                image,
                thread.Cid.UniqueProcess,
                thread.Cid.UniqueThread,
                self._config.MACHINE,
            )
            yield self.getoutput(line,
                                 thread.CreateTime,
                                 end=thread.ExitTime,
                                 body=body)

        data = []
        if "Symlink" in self._config.TYPE:
            data = filescan.SymLinkScan(self._config).calculate()
        for link in data:
            objct = link.get_object_header()
            line = "[{6}SYMLINK]{0} {1}->{2}{0} POffset: {3}/Ptr: {4}/Hnd: {5}".format(
                "" if body else "|",
                str(objct.NameInfo.Name or ''),
                str(link.LinkTarget or ''),
                link.obj_offset,
                objct.PointerCount,
                objct.HandleCount,
                self._config.MACHINE,
            )
            yield self.getoutput(line, link.CreationTime, body=body)

        data = []
        if "TimeDateStamp" in self._config.TYPE:
            data = moddump.ModDump(self._config).calculate()
        for aspace, procs, mod_base, mod_name in data:
            mod_name = str(mod_name or '')
            space = tasks.find_space(aspace, procs, mod_base)
            if space != None:
                try:
                    pe_file = obj.Object("_IMAGE_DOS_HEADER",
                                         offset=mod_base,
                                         vm=space)
                    header = pe_file.get_nt_header()
                except ValueError as ve:
                    continue
                line = (
                    "[{3}PE HEADER (module)]{0} {1}{0} Base: {2:#010x}".format(
                        "" if body else "|",
                        mod_name,
                        mod_base,
                        self._config.MACHINE,
                    ))
                yield self.getoutput(line,
                                     header.FileHeader.TimeDateStamp,
                                     body=body)

        uastuff = []
        if "Userassist" in self._config.TYPE:
            uastuff = userassist.UserAssist(self._config).calculate()
        for win7, reg, key in uastuff:
            ts = "{0}".format(key.LastWriteTime)
            for v in rawreg.values(key):
                tp, dat = rawreg.value_data(v)
                subname = v.Name
                if tp == 'REG_BINARY':
                    dat_raw = dat
                    try:
                        subname = codecs.encode(subname, 'rot_13')
                    except UnicodeDecodeError:
                        pass
                    if win7:
                        guid = subname.split("\\")[0]
                        if guid in userassist.folder_guids:
                            subname = subname.replace(
                                guid, userassist.folder_guids[guid])
                    bufferas = addrspace.BufferAddressSpace(self._config,
                                                            data=dat_raw)
                    uadata = obj.Object("_VOLUSER_ASSIST_TYPES",
                                        offset=0,
                                        vm=bufferas)
                    ID = "N/A"
                    count = "N/A"
                    fc = "N/A"
                    tf = "N/A"
                    lw = "N/A"
                    if (len(dat_raw) < bufferas.profile.get_obj_size(
                            '_VOLUSER_ASSIST_TYPES') or uadata == None):
                        continue
                    else:
                        if hasattr(uadata, "ID"):
                            ID = "{0}".format(uadata.ID)
                        if hasattr(uadata, "Count"):
                            count = "{0}".format(uadata.Count)
                        else:
                            count = "{0}".format(
                                uadata.CountStartingAtFive
                                if uadata.CountStartingAtFive < 5 else
                                uadata.CountStartingAtFive - 5)
                        if hasattr(uadata, "FocusCount"):
                            seconds = (uadata.FocusTime + 500) / 1000.0
                            time = (datetime.timedelta(seconds=seconds)
                                    if seconds > 0 else uadata.FocusTime)
                            fc = "{0}".format(uadata.FocusCount)
                            tf = "{0}".format(time)
                        lw = "{0}".format(uadata.LastUpdated)

                subname = subname.replace("|", "%7c")
                line = "[{7}USER ASSIST]{0} {2}{0} Registry: {1}/ID: {3}/Count: {4}/FocusCount: {5}/TimeFocused: {6}".format(
                    "" if body else "|",
                    reg,
                    subname,
                    ID,
                    count,
                    fc,
                    tf,
                    self._config.MACHINE,
                )
                yield self.getoutput(line, uadata.LastUpdated, body=body)

        shimdata = []
        if "Shimcache" in self._config.TYPE:
            shimdata = shimcache.ShimCache(self._config).calculate()
        for path, lm, lu in shimdata:
            line = "[{2}SHIMCACHE]{0} {1}{0} ".format("" if body else "|",
                                                      path,
                                                      self._config.MACHINE)
            if lu:
                yield self.getoutput(line, lm, end=lu, body=body)
            else:
                yield self.getoutput(line, lm, body=body)

        if ("_HBASE_BLOCK" in self._config.TYPE
                or "_CMHIVE" in self._config.TYPE
                or "Registry" in self._config.TYPE):
            regapi = registryapi.RegistryApi(self._config)
            for o in regapi.all_offsets:
                if "_HBASE_BLOCK" in self._config.TYPE:
                    line = "[{2}_HBASE_BLOCK TimeStamp]{0} {1}{0} ".format(
                        "" if body else "|",
                        regapi.all_offsets[o],
                        self._config.MACHINE,
                    )
                    h = obj.Object("_HHIVE", o, addr_space)
                    yield self.getoutput(line,
                                         h.BaseBlock.TimeStamp,
                                         body=body)

                if ("_CMHIVE" in self._config.TYPE and version[0] == 6 and
                        addr_space.profile.metadata.get('build', 0) >= 7601):
                    line = (
                        line) = "[{2}_CMHIVE LastWriteTime]{0} {1}{0} ".format(
                            "" if body else "|",
                            regapi.all_offsets[o],
                            self._config.MACHINE,
                        )
                    cmhive = obj.Object("_CMHIVE", o, addr_space)
                    yield self.getoutput(line, cmhive.LastWriteTime, body=body)

        if "Registry" in self._config.TYPE:
            regapi.reset_current()
            regdata = regapi.reg_get_all_keys(self._config.HIVE,
                                              self._config.USER,
                                              reg=True,
                                              rawtime=True)

            for lwtime, reg, item in regdata:
                item = item.replace("|", "%7c")
                line = "[{3}REGISTRY]{0} {2}{0} Registry: {1}".format(
                    "" if body else "|", reg, item, self._config.MACHINE)

                yield self.getoutput(line, lwtime, body=body)

        if "Timer" in self._config.TYPE:
            volmagic = obj.VolMagic(addr_space)
            KUSER_SHARED_DATA = obj.Object(
                "_KUSER_SHARED_DATA",
                offset=volmagic.KUSER_SHARED_DATA.v(),
                vm=addr_space,
            )
            interrupt = (KUSER_SHARED_DATA.InterruptTime.High1Time <<
                         32) | KUSER_SHARED_DATA.InterruptTime.LowPart
            now = KUSER_SHARED_DATA.SystemTime.as_windows_timestamp()
            data = timers.Timers(self._config).calculate()
            for timer, module in data:
                signaled = "-"
                if timer.Header.SignalState.v():
                    signaled = "Yes"

                module_name = "UNKNOWN"
                if module:
                    module_name = str(module.BaseDllName or '')

                try:
                    # human readable time taken from http://computer.forensikblog.de/en/2011/10/timers-and-times.html
                    bufferas = addrspace.BufferAddressSpace(
                        self._config,
                        data=struct.pack(
                            '<Q', timer.DueTime.QuadPart - interrupt + now),
                    )
                    due_time = obj.Object("WinTimeStamp",
                                          is_utc=True,
                                          offset=0,
                                          vm=bufferas)
                except TypeError:
                    due_time = 0

                line = "[{6}TIMER]{0} {1}{0} Signaled: {2}/Routine: 0x{3:x}/Period(ms): {4}/Offset: 0x{5:x}".format(
                    "" if body else "|",
                    module_name,
                    signaled,
                    timer.Dpc.DeferredRoutine,
                    timer.Period,
                    timer.obj_offset,
                    self._config.MACHINE,
                )

                yield self.getoutput(line, due_time, body=body)