def get_clients(self): buf = (ctypes.c_uint64 * 16)(*([0] * 16)) _len = wintypes.DWORD(ctypes.sizeof(buf)) ret = virtualMIDIGetProcesses(self._id, buf, ctypes.byref(_len)) if ret != 0: return [ buf[i] for i in range(0, _len.value // ctypes.sizeof(ctypes.c_uint64)) ] else: raise DriverError(ctypes.GetLastError(), "couldn't get client PIDs")
def process__get_all_pids(): process_buff = (wintypes.DWORD * 2048)() process_size = wintypes.DWORD() res = windll.psapi.EnumProcesses(process_buff, c_sizeof(process_buff), byref(process_size)) if res != 0: raise WinError() count = process_size / c_sizeof(wintypes.DWORD) ret = [] for i in range(count): ret.append(process_buff[i]) return ret
def protect_process_memory( process_handle: int, address: int, size: int, flags: Protection = READ | WRITE | EXECUTE, ) -> int: old_protect = wintypes.DWORD(0) _virtual_protect(process_handle, address, size, PROTECTION_FLAGS[flags], ctypes.byref(old_protect)) return old_protect.value
def WSARecvFrom(hsock, count, sockaddr, overlapped, flags=0): """generic recvfrom (must pass an initialized sockaddr object)""" data = ctypes.create_string_buffer(count) buf = WSABUF(count, data) sockaddr_len = ctypes.c_int(ctypes.sizeof(sockaddr)) dw_flags = wintypes.DWORD(flags) sentcount = wintypes.DWORD(0) if isinstance(hsock, PyHANDLE): hsock = hsock.handle if isinstance(overlapped, PyOVERLAPPED): overlapped = _get_inner_overlapped(overlapped) rc = _WSARecvFrom(hsock, ctypes.byref(buf), 1, ctypes.byref(sentcount), ctypes.byref(dw_flags), ctypes.byref(sockaddr), ctypes.byref(sockaddr_len), ctypes.byref(overlapped), 0) error = ctypes.GetLastError() if rc != 0 and error != win32file.WSA_IO_PENDING: raise ctypes.WinError(error) return data, dw_flags
def _socket_addr_to_str(socket_addr): addr_str_len = wintypes.DWORD(256) addr_str = ctypes.create_unicode_buffer(256) ret_val = ws2_32.WSAAddressToStringW(socket_addr.lpSockaddr, socket_addr.iSockaddrLength, None, addr_str, ctypes.byref(addr_str_len)) if ret_val: raise exception.CloudbaseInitException( "WSAAddressToStringW failed: %s" % ws2_32.WSAGetLastError()) return addr_str.value
def enum_proc(hWnd, lParam): #if user32.IsWindowVisible(hWnd): pid = wintypes.DWORD() tid = user32.GetWindowThreadProcessId(hWnd, ctypes.byref(pid)) length = user32.GetWindowTextLengthW(hWnd) + 1 title = ctypes.create_unicode_buffer(length) user32.GetWindowTextW(hWnd, title, length) if title.value.startswith(self.name): results.append((WindowInfo(pid.value, title.value), hWnd)) return True
def read_option(self, handle, option): """ Reads information about the internet connection, which may be a string or struct :param handle: The handle to query for the info :param option: The (int) option to get :return: A string, or one of the InternetCertificateInfo or InternetProxyInfo structs """ option_buffer_size = 8192 try_again = True while try_again: try_again = False to_read_was_read = wintypes.DWORD(option_buffer_size) option_buffer = ctypes.create_string_buffer(option_buffer_size) ref = ctypes.byref(option_buffer) success = wininet.InternetQueryOptionA( handle, option, ref, ctypes.byref(to_read_was_read)) if not success: if ctypes.GetLastError() != self.ERROR_INSUFFICIENT_BUFFER: raise NonHttpError(self.extract_error()) # The error was a buffer that was too small, so try again option_buffer_size = to_read_was_read.value try_again = True continue if option == self.INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: length = min(len(option_buffer), ctypes.sizeof(InternetCertificateInfo)) cert_info = InternetCertificateInfo() ctypes.memmove(ctypes.addressof(cert_info), option_buffer, length) return cert_info elif option == self.INTERNET_OPTION_PROXY: length = min(len(option_buffer), ctypes.sizeof(InternetProxyInfo)) proxy_info = InternetProxyInfo() ctypes.memmove(ctypes.addressof(proxy_info), option_buffer, length) return proxy_info else: option = b'' if to_read_was_read.value > 0: option += option_buffer.raw[:to_read_was_read.value] return option.decode('cp1252').rstrip("\x00")
def get_geometry(self): if not self._geom: geom = Win32_DiskGeometry() bytes_returned = wintypes.DWORD() ret_val = kernel32.DeviceIoControl( self._handle, self.IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, ctypes.byref(geom), ctypes.sizeof(geom), ctypes.byref(bytes_returned), 0) if not ret_val: raise Exception("Cannot get disk geometry") self._geom = geom return self._geom
def LookupAccountSid(sid, machine=None): prototype = ctypes.WINFUNCTYPE( wintypes.BOOL, # return value wintypes.LPCWSTR, PSID, wintypes.LPCWSTR, wintypes.LPDWORD, wintypes.LPCWSTR, wintypes.LPDWORD, wintypes.LPDWORD) paramflags = ((_In_, 'lpSystemName'), (_In_, 'lpSid'), (_Out_, 'lpName', ctypes.create_unicode_buffer(UNLEN)), (_In_, 'cchName', ctypes.byref(wintypes.DWORD(UNLEN))), (_Out_, 'lpReferencedDomainName', ctypes.create_unicode_buffer(UNLEN)), (_In_, 'cchReferencedDomainName', ctypes.byref(wintypes.DWORD(UNLEN))), (_Out_, 'peUse')) _LookupAccountSid = prototype(('LookupAccountSidW', advapi32), paramflags) _LookupAccountSid.errcheck = errcheckBOOL lpname, lprefdn, peuse = _LookupAccountSid(machine, sid) return (lpname.value, lprefdn.value, peuse)
def _get_layout(self): layout = Win32_DRIVE_LAYOUT_INFORMATION_EX() bytes_returned = wintypes.DWORD() ret_val = kernel32.DeviceIoControl( self._handle, winioctlcon.IOCTL_DISK_GET_DRIVE_LAYOUT_EX, 0, 0, ctypes.byref(layout), ctypes.sizeof(layout), ctypes.byref(bytes_returned), 0) if not ret_val: raise exception.WindowsCloudbaseInitException( "Cannot get disk layout: %r") return layout
def _inject_dll(process_id: int, path: Union[str, Path]) -> int: path = Path(path).resolve() if not path.exists(): raise FileNotFoundError(f"Given DLL path does not exist: {path}.") process_handle = open_process(process_id) path_bytes = str(path).encode(ENCODING) path_size = len(path_bytes) + 1 # increment to account for null terminator # allocate memory required to put our DLL path parameter_address = allocate_memory(process_handle, 0, path_size) # write DLL path string into allocated space write_process_memory(process_handle, parameter_address, path_bytes) if _is_wow_64_process( process_handle ): # if we are injecting into 32-bit process from 64-bit # get base address to kernel32.dll module of the process module_base = get_base_address(process_id, _kernel32_name) # look up offset of LoadLibraryA in PE header of WoW64 kernel32.dll load_library_offset = _kernel32_symbols.get(_load_library_name, 0) # if function is not present, raise an error if not load_library_offset: raise LookupError( f"Can not find {_load_library_name} in WoW64 kernel32.dll module." ) # otherwise, get an actual address load_library = module_base + load_library_offset else: # otherwise, get address normally load_library = _get_module_proc_address("kernel32.dll", "LoadLibraryA") thread_id = wintypes.DWORD(0) # create remote thread, with start routine of LoadLibraryA(DLLPath) thread_handle = _create_remote_thread(process_handle, None, 0, load_library, parameter_address, 0, ctypes.byref(thread_id)) # wait for the handle _wait_for_single_object(thread_handle, INFINITE) # free memory used to allocate DLL path free_memory(process_handle, parameter_address, path_size) # close process and thread handles _close_handle(process_handle) _close_handle(thread_handle) return thread_id.value
def enable(): hOut = windll.kernel32.GetStdHandle(VirtualTerminalSequences.STD_OUTPUT_HANDLE) if hOut == VirtualTerminalSequences.INVALID_HANDLE_VALUE: return False dwMode = wintypes.DWORD() if windll.kernel32.GetConsoleMode(hOut, byref(dwMode)) == 0: return False dwMode.value |= VirtualTerminalSequences.ENABLE_VIRTUAL_TERMINAL_PROCESSING # dwMode.value |= ENABLE_LVB_GRID_WORLDWIDE if windll.kernel32.SetConsoleMode(hOut, dwMode) == 0: return False return True
def get_console_mode(stream=sys.stdin): file_handle = msvcrt.get_osfhandle(stream.fileno()) getConsoleMode = windll.kernel32.GetConsoleMode getConsoleMode.argtypes, getConsoleMode.restype = ([ wintypes.HANDLE, wintypes.LPDWORD ], wintypes.BOOL) mode = wintypes.DWORD(0) if getConsoleMode(file_handle, byref(mode)): return mode.value else: err = ctypes.get_last_error() raise ctypes.WinError(err)
def read_packet(self): buf = ctypes.create_string_buffer(self.descriptor.report_size_in + 1) num_read = wintypes.DWORD() ret = kernel32.ReadFile(self.handle, buf, len(buf), ctypes.byref(num_read), None) if not ret: raise ctypes.WinError() if num_read.value != self.descriptor.report_size_in + 1: raise OSError("Failed to read full length report from device.") return buf.raw[1:] # Strip report ID
def waitThenAdd(self, xres, yres, numImages, buf, eventToWait, newEvent): win32event.WaitForSingleObject(eventToWait.value, 10000) dwSize = wintypes.DWORD(2 * xres * yres) status = DWORD() for i in range(numImages - 1): hEvent = wintypes.HANDLE(1 + i) err = self.f_abe(self.hcam, hEvent, WORD(4), DWORD(0), DWORD(0), DWORD(0), addressof(buf[i]), dwSize, byref(status)) err = self.f_abe(self.hcam, newEvent, WORD(4), DWORD(0), DWORD(0), DWORD(0), addressof(buf[numImages - 1]), dwSize, byref(status))
def get_physical_monitors_from_HMONITOR(hmonitor: wintypes.HMONITOR): num = wintypes.DWORD() windll.Dxva2.GetNumberOfPhysicalMonitorsFromHMONITOR(hmonitor, byref(num)) class PHYSICAL_MONITOER(Structure): _fields_ = [("hPhysicalMonitor", wintypes.HANDLE), ("szPhysicalMonitorDescription", wintypes.WCHAR * 128)] phy_monitors_arr = (PHYSICAL_MONITOER * num.value)() windll.Dxva2.GetPhysicalMonitorsFromHMONITOR(hmonitor, num, phy_monitors_arr) return list(phy_monitors_arr)
def CalConvert(self, counts, scans): """Performs the calibration of one or more scans according to the previously called CalSetup function.""" err = daq.daqCalConvert( self.handle, ct.pointer(counts), wt.DWORD(scans), ) if err != 0: raise DaqError(err)
def enable_vt_mode(filehandle=None): """ Enables virtual terminal processing mode for the given console or stdout """ if filehandle is None: filehandle = msvcrt.get_osfhandle(sys.__stdout__.fileno()) current_mode = wintypes.DWORD() KERNEL32.GetConsoleMode(filehandle, ctypes.byref(current_mode)) new_mode = 0x0004 | current_mode.value KERNEL32.SetConsoleMode(filehandle, new_mode)
def execute_process_as_user(self, token, args, wait=True, new_console=False): """Executes processes as an user. :param token: Represents the user logon session token, resulted from running the 'create_user_logon_session' method. :param args: The arguments with which the process will be runned with. :param wait: Specifies if it's needed to wait for the process handler to finish up running all the operations on the process object. :param new_console: Specifies whether the process should run under a new console or not. :return: The exit code value resulted from the running process. :rtype: int """ LOG.debug("Executing process as user, command line: %s", args) proc_info = Win32_PROCESS_INFORMATION() startup_info = Win32_STARTUPINFO_W() startup_info.cb = ctypes.sizeof(Win32_STARTUPINFO_W) startup_info.lpDesktop = "" flags = self.CREATE_NEW_CONSOLE if new_console else 0 cmdline = ctypes.create_unicode_buffer(subprocess.list2cmdline(args)) try: ret_val = advapi32.CreateProcessAsUserW(token, None, cmdline, None, None, False, flags, None, None, ctypes.byref(startup_info), ctypes.byref(proc_info)) if not ret_val: raise exception.WindowsCloudbaseInitException( "CreateProcessAsUserW failed: %r") if wait and proc_info.hProcess: kernel32.WaitForSingleObject(proc_info.hProcess, self.INFINITE) exit_code = wintypes.DWORD() if not kernel32.GetExitCodeProcess(proc_info.hProcess, ctypes.byref(exit_code)): raise exception.WindowsCloudbaseInitException( "GetExitCodeProcess failed: %r") return exit_code.value finally: if proc_info.hProcess: kernel32.CloseHandle(proc_info.hProcess) if proc_info.hThread: kernel32.CloseHandle(proc_info.hThread)
def CvtLinearSetupConvert(self, nscan, readingsPos, nReadings, signal1, voltage1, signal2, voltage2, avg, scans): """Both sets up the linear conversion process and converts the ADC readings into floating point numbers.""" counts = self.dataBuf fValues = (ct.c_float * self.dBufSz)() nValues = self.dBufSz err = daq.daqCvtLinearSetupConvert( wt.DWORD(nscan), wt.DWORD(readingsPos), wt.DWORD(nReadings), ct.c_float(signal1), ct.c_float(voltage1), ct.c_float(signal2), ct.c_float(voltage2), wt.DWORD(avg), ct.pointer(counts), wt.DWORD(scans), ct.pointer(fValues), wt.DWORD(nValues), ) if err != 0: raise DaqError(err) return fValues
def makeRequest(cls): """ Issue a WININET request based on the class parameters. :return: None """ hInternet = wi.InternetOpenW(cls.user_agent, wi.INTERNET_OPEN_TYPE_DIRECT, None, None, 0) if hInternet is None: raise ct.WinError() hSession = wi.InternetConnectW(hInternet, cls.url, cls.port, None, None, wi.INTERNET_SERVICE_HTTP, 0, 0) if hSession is None: raise ct.WinError() hRequest = wi.HttpOpenRequestW(hSession, cls.verb, '', None, None, None, 0, 0) if hRequest is None: raise ct.WinError() request_sent = wi.HttpSendRequestW(hRequest, None, 0, None, 0) if request_sent == 0: raise ct.WinError() # Setup the necessary parameters to read the server's response buff_size = wt.DWORD(cls.size) buf = (ct.c_char * buff_size.value)() keep_reading = 1 bytes_read = wt.DWORD(-1) response_str = str() while keep_reading == 1 and bytes_read.value != 0: # Read the entire response. keep_reading = wi.InternetReadFile(hRequest, buf, buff_size, ct.byref(bytes_read)) response_str += str(buf.value) return response_str
def _ABSetPos(edge, appbarWindow): barData = APPBARDATA() barData.cbSize = wintypes.DWORD(sizeof(barData)) barData.hWnd = appbarWindow.GetHandle() barData.uEdge = edge deskW = wx.SystemSettings_GetMetric(wx.SYS_SCREEN_X) deskH = wx.SystemSettings_GetMetric(wx.SYS_SCREEN_Y) winW, winH = appbarWindow.Size if barData.uEdge == ABEdge.Left or barData.uEdge == ABEdge.Right: barData.rc.top = 0 barData.rc.bottom = deskH if barData.uEdge == ABEdge.Left: barData.rc.left = 0 barData.rc.right = winW else: barData.rc.right = deskW barData.rc.left = deskW - winW else: barData.rc.left = 0 barData.rc.right = deskW if barData.uEdge == ABEdge.Top: barData.rc.top = 0 barData.rc.bottom = winH else: barData.rc.bottom = deskH barData.rc.top = deskH - winH shell32.SHAppBarMessage(ABMsg.ABM_QUERYPOS, PAPPBARDATA(barData)) # http://msdn.microsoft.com/en-us/library/bb776821.aspx if barData.uEdge == ABEdge.Left: barData.rc.right = barData.rc.left + winW elif barData.uEdge == ABEdge.Right: barData.rc.left = barData.rc.right - winW elif barData.uEdge == ABEdge.Top: barData.rc.bottom = barData.rc.top + winH elif barData.uEdge == ABEdge.Bottom: barData.rc.top = barData.rc.bottom - winH shell32.SHAppBarMessage(ABMsg.ABM_SETPOS, PAPPBARDATA(barData)) def _resize(): appbarWindow.SetPosition((barData.rc.left, barData.rc.top)) appbarWindow.SetSize((barData.rc.right - barData.rc.left, barData.rc.bottom - barData.rc.top)) # This is done async, because windows will send a resize # after a new appbar is added. if we size right away, the # windows resize comes last and overrides us. wx.CallAfter(_resize)
def inject_process_CreateRemoteThread(self): print(""" [*] ======================================================= [*] Find a process to inject shellcode into using process [*] listing, then VirtualAllocEx(), WriteProcessMemory(), [*] CreateRemoteThread() [*] =======================================================""") pid = self.select_pid() ph = self.kernel32.OpenProcess(self.PROCESS_SOME_ACCESS, False, pid) print('[*] Process handle is: 0x{:06X}'.format(ph)) if ph == 0: return memptr = self.VirtualAllocEx( ph, 0, len(self.shellcode), self.MEM_COMMIT_RESERVE, self.PAGE_READWRITE ) print('[*] VirtualAllocEx() memory at: 0x{:08X}'.format(memptr)) if memptr == 0: return nbytes = ctypes.c_int(0) result = self.WriteProcessMemory( ph, memptr, self.shellcode, len(self.shellcode), ctypes.byref(nbytes) ) print('[+] Bytes written = {}'.format(nbytes.value)) if result == 0: print("[-] WriteProcessMemory() Failed - Error Code: {}".format( self.kernel32.GetLastError() )) return old_protection = ctypes.pointer(wt.DWORD()) result = self.VirtualProtectEx( ph, memptr, len(self.shellcode), self.PAGE_READ_EXECUTE, old_protection ) if result == 0: print("[-] VirtualProtectEx() Failed - Error Code: {}".format( self.kernel32.GetLastError() )) return th = self.CreateRemoteThread(ph, None, 0, memptr, None, 0, None) if th == 0: print("[-] CreateRemoteThread() Failed - Error Code: {}".format( self.kernel32.GetLastError() )) return self.VirtualFreeEx(ph, memptr, 0, 0xC000) self.CloseHandle(ph)
def setconsole(bRestore): if os.name == "nt": if not bRestore: os.system(" ") #VT100 in Windows import ctypes from ctypes import wintypes global h #prevent garbage collection def on_exit(event): print((SET_BKGND_COLOR(0) + SET_FORE_COLOR(7) + SET_CURSOR_FMT + CLEAR_LINE + SHOW_CURSOR) % (height, 0)) setconsole(True, False) return 0 _kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) _HandlerRoutine = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD) _kernel32.SetConsoleCtrlHandler.argtypes = (_HandlerRoutine, wintypes.BOOL) h = _HandlerRoutine(on_exit) _kernel32.SetConsoleCtrlHandler(h, True) import subprocess hStdin = _kernel32.GetStdHandle(subprocess.STD_INPUT_HANDLE) ENABLE_LINE_INPUT = 0x0002 ENABLE_ECHO_INPUT = 0x0004 mode = wintypes.DWORD() _kernel32.GetConsoleMode(hStdin, ctypes.byref(mode)) _kernel32.SetConsoleMode(hStdin, mode.value & (~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)) if not bRestore else mode.value | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT) else: def termination_handler(signum, frame): global height print((SET_BKGND_COLOR(0) + SET_FORE_COLOR(7) + SET_CURSOR_FMT + CLEAR_LINE + SHOW_CURSOR) % (height, 0)) setconsole(True, False) sys.exit() fd = sys.stdin.fileno() import termios attr = [0,0,0,0] if not os.isatty(fd) else termios.tcgetattr(fd)[:] import fcntl CurFl = fcntl.fcntl(fd, fcntl.F_GETFL, 0) if not bRestore: import signal signal.signal(signal.SIGFPE, termination_handler) signal.signal(signal.SIGILL, termination_handler) signal.signal(signal.SIGSEGV, termination_handler) signal.signal(signal.SIGBUS, termination_handler) signal.signal(signal.SIGABRT, termination_handler) signal.signal(signal.SIGTRAP, termination_handler) signal.signal(signal.SIGSYS, termination_handler) signal.signal(signal.SIGINT, termination_handler) signal.signal(signal.SIGTERM, termination_handler) signal.signal(signal.SIGQUIT, termination_handler) signal.signal(signal.SIGHUP, termination_handler) attr[3] &= ~(termios.ICANON | termios.ECHO) #if nonblock and os.isatty(fd): fcntl.fcntl(fd, fcntl.F_SETFL, CurFl | os.O_NONBLOCK) # STDIN_FILENO=0, set to nonblocking reads else: attr[3] |= termios.ICANON | termios.ECHO #if (CurFl & os.O_NONBLOCK): fcntl.fcntl(fd, fcntl.F_SETFL, CurFl & ~os.O_NONBLOCK) if os.isatty(fd): termios.tcsetattr(fd, termios.TCSANOW, attr)
def _set_conout_mode(new_mode, mask=0xFFFFFFFF): # don't assume StandardOutput is a console. # open CONOUT$ instead fdout = os.open('CONOUT$', os.O_RDWR) try: hout = msvcrt.get_osfhandle(fdout) old_mode = wintypes.DWORD() kernel32.GetConsoleMode(hout, ctypes.byref(old_mode)) mode = (new_mode & mask) | (old_mode.value & ~mask) kernel32.SetConsoleMode(hout, mode) return old_mode.value finally: os.close(fdout)
def list_pids(): '''Return sorted list of process IDs.''' length = 4096 PID_SIZE = ctypes.sizeof(wintypes.DWORD) while True: pids = (wintypes.DWORD * length)() cb = ctypes.sizeof(pids) cbret = wintypes.DWORD() psapi.EnumProcesses(pids, cb, ctypes.byref(cbret)) if cbret.value < cb: length = cbret.value // PID_SIZE return sorted(pids[:length]) length *= 2
def show_privilege_information(): privileges = get_privilege_information() for i in range(privileges.PrivilegeCount): return_length = wintypes.DWORD(10240) language_id = wintypes.DWORD(128) display_name = create_unicode_buffer(return_length.value) privilege_name = create_unicode_buffer(return_length.value) enabled = bool(privileges.Privileges[i].Attributes & SE_PRIVILEGE_ENABLED) res = advapi32.LookupPrivilegeNameW(None, privileges.Privileges[i].Luid, privilege_name, return_length) if not res: continue privilege_name = str(privilege_name[:return_length.value]) res = advapi32.LookupPrivilegeDisplayNameW(None, privilege_name, display_name, return_length, language_id) if not res: continue display_name = str(display_name[:return_length.value]) print("{}({}) : {}".format(privilege_name, display_name, enabled))
def _get_capabilities(self): # this is slow length = wintypes.DWORD() if not ctypes.windll.dxva2.GetCapabilitiesStringLength( self._handle, ctypes.byref(length)): raise ctypes.WinError() capabilities_string = (ctypes.c_char * length.value)() if not ctypes.windll.dxva2.CapabilitiesRequestAndCapabilitiesReply( self._handle, capabilities_string, length): raise ctypes.WinError() self._capabilities_raw = capabilities_string.value.decode('ascii') self._capabilities = self.parse_capabilities_string( self._capabilities_raw)
def write_packet(self, packet): out = b"\0" + packet # Prepend report ID num_written = wintypes.DWORD() ret = kernel32.WriteFile( self.handle, out, len(out), ctypes.byref(num_written), None ) if not ret: raise ctypes.WinError() if num_written.value != len(out): raise OSError( "Failed to write complete packet. " + "Expected %d, but got %d" % (len(out), num_written.value) )
def pids(): """Returns a list of PIDs currently running on the system.""" length = 4096 PID_SIZE = ctypes.sizeof(wintypes.DWORD) while True: pids = (wintypes.DWORD * length)() cb = ctypes.sizeof(pids) cbret = wintypes.DWORD() psapi.EnumProcesses(pids, cb, ctypes.byref(cbret)) if cbret.value < cb: length = cbret.value // PID_SIZE return list(pids[:length]) length *= 2