def on_message(self, msg): try: dl_msg_type = msg[1] if dl_msg_type == DataLoggerMessages.EXTRACT_GENERAL: self.uid = msg[2] self.n_columns = parse_uint32(msg[3:7]) self.n_entries = parse_uint32(msg[7:11]) self.schema_name = stringify(msg[11:]) elif dl_msg_type == DataLoggerMessages.EXTRACT_COLUMN: column_idx = msg[2] self.column_done[column_idx] = True self.column_type[column_idx] = msg[3] self.column_name[column_idx] = stringify(msg[4:]) elif dl_msg_type == DataLoggerMessages.EXTRACT_DATA: entry_idx = parse_uint32(msg[2:6]) self.entry_done[entry_idx] = True entry = [] offset = 6 for column_type in self.column_type: if column_type == ColumnType.FLOAT: entry.append(parse_float(msg[offset:(offset+4)])) offset += 4 if column_type == ColumnType.UINT32: entry.append(parse_uint32(msg[offset:(offset+4)])) offset += 4 self.entry[entry_idx] = tuple(entry) if (self.schema_name is not None and all(self.column_done) and all(self.entry_done)): self.done = True except Exception as e: print(e) self.failure = True
def parse_budget(self, raw_misc_data): """ Parses the budget data segment from MISC into budget data. Args: raw_misc_data (bytes): Raw segment from Misc """ # Ordinances ordinance_raw = raw_misc_data[0x0FA0:0x0FA0 + 4] self.ordinance_flags = int_to_bitstring(parse_uint32(ordinance_raw)) # bonds start_offset = 0x0610 bonds_len = 50 * 4 self.bonds = bytes_to_int32s(raw_misc_data[start_offset:start_offset + bonds_len]) # various sub-budgets sub_len = 27 * 4 for name, start_offset in self._sub_budget_indices.items(): chunk = raw_misc_data[start_offset:start_offset + sub_len] sub_budget = deepcopy(self._blank_budget) chunk_data = bytes_to_int32s(chunk) for idx, k in enumerate(self._blank_budget): sub_budget[k] = chunk_data[idx] self.budget_items[name] = sub_budget
def parse_data(raw_file): """ Parses the data file containing bitmaps. Works for Win95 version of SMALLMED.DAT, SPECIAL.DAT and LARGE.DAT Args: raw_file (bytes): Raw datafile to parse. Returns: A dictionary, where the key is the ID of the tile stored in that segment of the file, and the value being the raw bytes. """ large_metadata = {} large_data = {} large_metaentry = namedtuple("large_metadata", ["offset", "width", "height"]) large_entry = namedtuple("large_data", ["offset", "width", "height", "length", "data"]) entries = parse_uint16(raw_file[0:2]) index = raw_file[2:2 + entries * 10] # 10B per entry. # Generate the metadata on what's in the rest of the file from the header at the start. for idx in range(0, len(index), 10): item_id = parse_uint16(index[idx:idx + 2]) offset = parse_uint32(index[idx + 2:idx + 6]) height = parse_uint16(index[idx + 6:idx + 8]) width = parse_uint16(index[idx + 8:idx + 10]) # For duplicates in the data file, allow them, but give the second a -ve ID for later tracking. if item_id in large_metadata.keys(): item_id *= -1 large_metadata[item_id] = large_metaentry(offset, width, height) # Generate the lengths of each chunk. end = len(raw_file) offsets = [] for v in large_metadata.values(): offsets.extend([v.offset]) offsets.extend([end]) lengths = [ offsets[x] - offsets[x - 1] for x in range(1, len(large_metadata) + 1) ] # Combine the metadata together with the data. idx = 0 dupes = [x * -1 for x in large_metadata.keys() if x < 0] for k, v in large_metadata.items(): length = lengths[idx] idx += 1 data = raw_file[v.offset:v.offset + length] entry = large_entry(v.offset, v.width, v.height, length, data) # Now we need to perform some cleanup on the duplicated entries. # In this case, we want the second entries, which have a -'ve id, and if we've got the first, ignore them. if k < 0: k *= -1 elif k in dupes: continue large_data[k] = entry return large_data
def disconnect(self): '''Cleanly close the connection to the remote server.''' # Send our exit status msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'])) msg.append(generate_uint32(self._remote_channel_number)) msg.append(generate_string('exit-status')) msg.append(generate_byte(0)) # False msg.append(generate_uint32(0)) # Exit status = 0 self._ssh_transport_connection.send(''.join(msg)) # Then close the channel msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_CLOSE'])) msg.append(generate_uint32(self._remote_channel_number)) self._ssh_transport_connection.send(''.join(msg)) # Read back the remote side's exit status data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, request_type = parse_string(data, index) index, want_reply_byte = parse_byte(data, index) want_reply = want_reply_byte != 0 index, exit_status = parse_uint32(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'] assert recipient_channel == self._local_channel_number assert request_type == 'exit-status' assert not want_reply # Disconnect at the transport layer self._ssh_transport_connection.disconnect() return exit_status
def disconnect(self): '''Cleanly close the connection to the remote server.''' # Send our exit status msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'])) msg.append(generate_uint32(self._remote_channel_number)) msg.append(generate_string('exit-status')) msg.append(generate_byte(0)) # False msg.append(generate_uint32(0)) # Exit status = 0 self._ssh_transport_connection.send(''.join(msg)) # Then close the channel msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_CLOSE'])) msg.append(generate_uint32(self._remote_channel_number)) self._ssh_transport_connection.send(''.join(msg)) # Read back the remote side's exit status data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, request_type = parse_string(data, index) index, want_reply_byte = parse_byte(data, index) want_reply = want_reply_byte != 0 index, exit_status = parse_uint32(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'] assert recipient_channel == self._local_channel_number assert request_type == 'exit-status' assert not want_reply # Disconnect at the transport layer self._ssh_transport_connection.disconnect() return exit_status
def get_chunk_from_offset(input_data, offset): """ Parses an IFF chunk by reading the header and using the size to determine which bytes belong to it. An IFF chunk has an 8 byte header, of which the first 4 bytes is the type and the second 4 bytes is the size (exclusive of the header). Args: input_data (bytes): raw city information. offset (int): starting offset in input to start parsing at. Returns: A list containing the id of the chunk (a 4 byte ascii value), an int length of the chunk of finally bytes of the chunk data. """ location_index = offset chunk_id = input_data[location_index : location_index + 4].decode('ascii') # Maximum 32b/4B, so 2^32 in length. chunk_size = parse_uint32(input_data[location_index + 4 : location_index + 8]) chunk_data = input_data[location_index + 8 : location_index + 8 + chunk_size] return [chunk_id, chunk_size, chunk_data]
def read(self): '''Read data from the remote server. This data will be encrypted, and its authenticity guaranteed (both client-to-server and server-to-client). Returns (string): the data sent by the remote server. ''' data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, channel_data = parse_string(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_DATA'] assert recipient_channel == self._local_channel_number return channel_data
def read(self): '''Read data from the remote server. This data will be encrypted, and its authenticity guaranteed (both client-to-server and server-to-client). Returns (string): the data sent by the remote server. ''' data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, channel_data = parse_string(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_DATA'] assert recipient_channel == self._local_channel_number return channel_data
def read(self): '''Read a packet from the remote server. Assuming the initial connection has completed (i.e. #connect has been called, and returned), this data will be encrypted, and its authenticity guaranteed. Returns (string): the data sent by the remote server. ''' # Read the first <block_len> bytes of the packet, decrypt it if necessary, and parse out the # remaining packet length initial_packet = self._socket.recv(AES_BLOCK_LEN) if self._encryption_negotiated: initial_packet = self._aes_server_to_client.decrypt(initial_packet) _, packet_len = parse_uint32(initial_packet, 0) # Read the remaining bytes of the packet, decrypting if necessary, and checking the MAC remaining_msg = self._socket.recv(packet_len - (AES_BLOCK_LEN - 4)) if self._encryption_negotiated: remaining_msg = self._aes_server_to_client.decrypt(remaining_msg) # Read and verify the MAC received_mac = self._socket.recv(SHA1_LEN) calculated_mac = hmac.new( self._integrity_key_server_to_client, generate_uint32(self._packets_received_counter) + initial_packet + remaining_msg, hashlib.sha1 ).digest() assert received_mac == calculated_mac, \ 'MACs did not match: %s != %s' % (repr(received_mac), repr(calculated_mac)) print colors.cyan('MAC validated correctly!') # Pull the payload out of the message data = (initial_packet + remaining_msg)[4:] index, padding_len = parse_byte(data, 0) payload_len = packet_len - padding_len - index payload = data[index:payload_len + index] self._packets_received_counter += 1 print colors.green('< Received: %s' % repr(payload)) return payload
def read(self): '''Read a packet from the remote server. Assuming the initial connection has completed (i.e. #connect has been called, and returned), this data will be encrypted, and its authenticity guaranteed. Returns (string): the data sent by the remote server. ''' # Read the first <block_len> bytes of the packet, decrypt it if necessary, and parse out the # remaining packet length initial_packet = self._socket.recv(AES_BLOCK_LEN) if self._encryption_negotiated: initial_packet = self._aes_server_to_client.decrypt(initial_packet) _, packet_len = parse_uint32(initial_packet, 0) # Read the remaining bytes of the packet, decrypting if necessary, and checking the MAC remaining_msg = self._socket.recv(packet_len - (AES_BLOCK_LEN - 4)) if self._encryption_negotiated: remaining_msg = self._aes_server_to_client.decrypt(remaining_msg) # Read and verify the MAC received_mac = self._socket.recv(SHA1_LEN) calculated_mac = hmac.new( self._integrity_key_server_to_client, generate_uint32(self._packets_received_counter) + initial_packet + remaining_msg, hashlib.sha1).digest() assert received_mac == calculated_mac, \ 'MACs did not match: %s != %s' % (repr(received_mac), repr(calculated_mac)) print colors.cyan('MAC validated correctly!') # Pull the payload out of the message data = (initial_packet + remaining_msg)[4:] index, padding_len = parse_byte(data, 0) payload_len = packet_len - padding_len - index payload = data[index:payload_len + index] self._packets_received_counter += 1 print colors.green('< Received: %s' % repr(payload)) return payload
def parse_message(msg): parsed_message = [] i = 0 last_open_str = 0 def store_string_so_far(): if last_open_str != i: parsed_message.append(stringify(msg[last_open_str:i])) while i < len(msg): if stringify(msg[i:(i+2)]) == '%d': store_string_so_far() int_bytes = msg[(i+2):(i+4)] parsed_int = parse_int(int_bytes) parsed_message.append(parsed_int) last_open_str = i + 4 i = i + 4 elif stringify(msg[i:(i+2)]) == '%f': store_string_so_far() float_bytes = msg[(i+2):(i+6)] parsed_float = parse_float(float_bytes) parsed_message.append(parsed_float) last_open_str = i + 6 i = i + 6 elif stringify(msg[i:(i+2)]) == '%l': store_string_so_far() uint32_bytes = msg[(i+2):(i+6)] parsed_uint32 = parse_uint32(uint32_bytes) parsed_message.append(parsed_uint32) last_open_str = i + 6 i = i + 6 else: if i+1 == len(msg): i += 1 store_string_so_far() else: i += 1 return parsed_message
def handle(self, msg): if len(msg) == 0: print ('WARNING: Empty message') return msg_type = msg[0] if msg_type == ToComputer.DEBUG: # debug message subsystems = [ 'INFO', 'ERROR', 'CRON', ] if (0 > msg[1] or msg[1] >= len(subsystems)): print ("WARNING: Unknown debug category: %d.." % (msg[1],)) subsystem = 'UNKNOWN' print (stringify(msg[2:])) else: subsystem = subsystems[msg[1]] content = parse_message(msg[2:]) content = ''.join([str(m) for m in content]) content = '[%s] %s' % (subsystem, content) self.log(content) elif msg_type == ToComputer.GET_SETTINGS_REPLY: time_since_epoch_s = parse_uint32(msg[1:5]) date = datetime.fromtimestamp(time_since_epoch_s) box_uid = msg[5] box_node_type = chr(msg[6]) box_balance = parse_uint32(msg[7:11]) state_of_charge = parse_float(msg[11:15]) uncertainty_of_charge = parse_float(msg[15:19]) battery_capacity = parse_float(msg[19:23]) off_threshold = parse_float(msg[23:27]) red_threshold = parse_float(msg[27:31]) yellow_threshold = parse_float(msg[31:35]) balance_update_hours = parse_int(msg[35:37]) balance_update_minutes = parse_int(msg[37:39]) balance_update_ammount = parse_uint32(msg[39:43]) #self.log('Time on device is ' + str(date)) self.update_if_not_focused(self.ui_root.settings.box_time, str(date)) self.update_if_not_focused(self.ui_root.settings.box_uid, str(box_uid)) self.update_if_not_focused(self.ui_root.settings.box_node_type, str(box_node_type)) self.update_if_not_focused(self.ui_root.settings.box_balance, str(box_balance)) self.update_if_not_focused(self.ui_root.settings.state_of_charge, str(state_of_charge)) self.update_if_not_focused(self.ui_root.settings.uncertainty_of_charge, str(uncertainty_of_charge)) self.update_if_not_focused(self.ui_root.settings.battery_capacity, str(battery_capacity)) self.update_if_not_focused(self.ui_root.settings.off_threshold, str(off_threshold)[:6]) self.update_if_not_focused(self.ui_root.settings.red_threshold, str(red_threshold)[:6]) self.update_if_not_focused(self.ui_root.settings.yellow_threshold, str(yellow_threshold)[:6]) self.update_if_not_focused(self.ui_root.settings.balance_update_hours, str(balance_update_hours)) self.update_if_not_focused(self.ui_root.settings.balance_update_minutes, str(balance_update_minutes)) self.update_if_not_focused(self.ui_root.settings.balance_update_ammount, str(balance_update_ammount)) elif msg_type == ToComputer.DATA_LOGGER_REPLY: controller.get.data_logger.on_message(msg) else: print( 'WARNING: Uknown message type :', msg[0])
def parse_misc(self, misc_data): """ Parses the MISC section of the .sc2 file and populates the City object with its values. See .sc2 file spec docs for more, at: Args: misc_data (bytes): MISC segment of the raw data from the .sc2 file. """ # This is the offset of the section that's being parsed from MISC. parse_order = { '0x0000': 'FirstEntry', # nominally the same in every city. '0x0004': 'GameMode', '0x0008': 'Compass', # rotation '0x000c': 'baseYear', '0x0010': 'simCycle', '0x0014': 'TotalFunds', '0x0018': 'TotalBonds', '0x001c': 'GameLevel', '0x0020': 'CityStatus', '0x0024': 'CityValue', '0x0028': 'LandValue', '0x002c': 'CrimeCount', '0x0030': 'TrafficCount', '0x0034': 'Pollution', '0x0038': 'CityFame', '0x003c': 'Advertising', '0x0040': 'Garbage', '0x0044': 'WorkerPercent', '0x0048': 'WorkerHealth', '0x004c': 'WorkerEducate', '0x0050': 'NationalPop', '0x0054': 'NationalValue', '0x0058': 'NationalTax', '0x005c': 'NationalTrend', '0x0060': 'heat', '0x0064': 'wind', '0x0068': 'humid', '0x006c': 'weatherTrend', '0x0070': 'NewDisaster', '0x0074': 'oldResPop', '0x0078': 'Rewards', '0x007c': 'Population Graphs', '0x016c': 'Industry Graphs', '0x01f0': 'Tile Counts', '0x05f0': 'ZonePop|0', '0x05f4': 'ZonePop|1', '0x05f8': 'ZonePop|2', '0x05fc': 'ZonePop|3', '0x0600': 'ZonePop|4', '0x0604': 'ZonePop|5', '0x0608': 'ZonePop|6', '0x060c': 'ZonePop|7', '0x0610': 'Bonds', '0x06d8': 'Neighbours', '0x0718': 'Valve?|0', # reverse engineered from the game, may be a typo in original. '0x071c': 'Valve?|1', '0x0720': 'Valve?|2', '0x0724': 'Valve?|3', '0x0728': 'Valve?|4', '0x072c': 'Valve?|5', '0x0730': 'Valve?|6', '0x0734': 'Valve?|7', '0x0738': 'gas_power', '0x073c': 'nuclear_power', '0x0740': 'solar_power', '0x0744': 'wind_power', '0x0748': 'microwave_power', '0x074c': 'fusion_power', '0x0750': 'airport', '0x0754': 'highways', '0x0758': 'buses', '0x075c': 'subways', '0x0760': 'water_treatment', '0x0764': 'desalinisation', '0x0768': 'plymouth', '0x076c': 'forest', '0x0770': 'darco', '0x0774': 'launch', '0x0778': 'highway_2', '0x077c': 'Budget', '0x0e3c': 'YearEnd', '0x0e40': 'GlobalSeaLevel', '0x0e44': 'terCoast', '0x0e48': 'terRiver', '0x0e4c': 'Military', '0x0e50': 'Paper List', '0x0ec8': 'News List', '0x0fa0': 'Ordinances', '0x0fa4': 'unemployed', '0x0fa8': 'Military Count', '0x0fe8': 'SubwayCnt', '0x0fec': 'GameSpeed', '0x0ff0': 'AutoBudget', '0x0ff4': 'AutoGo', '0x0ff8': 'UserSoundOn', '0x0ffc': 'UserMusicOn', '0x1000': 'NoDisasters', '0x1004': 'PaperDeliver', '0x1008': 'PaperExtra', '0x100c': 'PaperChoice', '0x1010': 'unknown128', '0x1014': 'Zoom', '0x1018': 'CityCentX', '0x101c': 'CityCentY', '0x1020': 'GlobalArcoPop', '0x1024': 'ConnectTiles', '0x1028': 'TeamsActive', '0x102c': 'TotalPop', '0x1030': 'IndustryBonus', '0x1034': 'PolluteBonus', '0x1038': 'oldArrest', '0x103c': 'PoliceBonus', '0x1040': 'DisasterObject', '0x1044': 'CurrentDisaster', '0x1048': 'GoDisaster', '0x104c': 'SewerBonus', '0x1050': 'Extra', } handle_special = ['Population Graphs', 'Industry Graphs', 'Tile Counts', 'Bonds', 'Neighbours', 'Budget', 'Military Count', 'Paper List', 'News List', 'Extra', 'Ordinances'] + list( self.simulator_settings.keys()) + list(self.game_settings.keys()) + list(self.inventions.keys()) # Make sure the dict is sorted because following code requires the sorting. #sorted(parse_order.keys()) # Parse misc and generate city attributes. for k, v in parse_order.items(): offset = int(k, 16) if v not in handle_special: self.city_attributes[v] = parse_uint32(misc_data[offset : offset + 4]) elif v == 'Population Graphs': length = 240 self.population_graphs = self.misc_uninterleave_data(self._population_graph_names, offset, length, misc_data) elif v == 'Industry Graphs': length = 132 self.industry_graphs = self.misc_uninterleave_data(self._industry_graph_names, offset, length, misc_data) elif v == 'Tile Counts': for x in range(0, 256): self.building_count[x] = parse_int32(misc_data[offset: offset + 4]) offset += 4 elif v in ('Bonds', 'Ordinances'): # Handled along with the budget. continue elif v == 'Neighbours': neighbour_types = ['Name', 'Population', 'Value', 'Fame'] # Calculate their offsets. 64 = 4 neighbours at 4 x 4B entries each for idx, start_offset in enumerate(range(offset, offset + 64, 16)): # 16 = 4 entries x 4B per entry. neighbour = collections.OrderedDict() for x in range(start_offset, start_offset + 16, 4): type_key = neighbour_types[((x + 8) % 16) // 4] neighbour[type_key] = parse_int32(misc_data[x : x + 4]) self.neighbor_info[idx] = neighbour elif v == 'Budget': self.budget = Budget() self.budget.parse_budget(misc_data) elif v == 'Military Count': num_items = 16 for idx, x in enumerate(range(offset, offset + num_items * 4, 4)): key = "{}|{}".format(v, idx) self.city_attributes[key] = parse_int32(misc_data[x : x + 4]) elif v == 'Paper List': num_items = 6 * 5 for idx, x in enumerate(range(offset, offset + num_items * 4, 4)): key = "{}|{}".format(v, idx) self.city_attributes[key] = parse_int32(misc_data[x : x + 4]) elif v == 'News List': num_items = 9 * 6 for idx, x in enumerate(range(offset, offset + num_items * 4, 4)): key = "{}|{}".format(v, idx) self.city_attributes[key] = parse_int32(misc_data[x : x + 4]) elif v == 'Extra': for idx, x in enumerate(range(offset, 4800, 4)): key = "{}|{}".format(v, idx) self.city_attributes[key] = parse_int32(misc_data[x : x + 4]) elif v in list(self.simulator_settings.keys()): self.simulator_settings[v] = parse_int32(misc_data[offset : offset + 4]) elif v in list(self.game_settings.keys()): self.game_settings[v] = parse_int32(misc_data[offset : offset + 4]) elif v in list(self.inventions.keys()): self.inventions[v] = parse_int32(misc_data[offset : offset + 4]) else: # Fallthrough, this should never, ever, be hit. print("MISC is missing something!", k, v)
def _create_ssh_connection(self): # Read the global request that SSH sends us - this is trying to let us know all host keys, but # it's OpenSSH-specific, and we don't need it data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, request_name = parse_string(data, index) index, want_reply_byte = parse_byte(data, index) want_reply = want_reply_byte != 0 assert msg_type == SSH_MSG_NUMS['SSH_MSG_GLOBAL_REQUEST'] assert request_name == '*****@*****.**' assert not want_reply # Reply to let OpenSSH know that we don't know what they're talking about msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_REQUEST_FAILURE'])) self._ssh_transport_connection.send(''.join(msg)) # Actually get started with opening a channel for SSH communication window_size = 1048576 maximum_packet_size = 16384 # Request to open a session channel msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_OPEN'])) msg.append(generate_string('session')) msg.append(generate_uint32(self._local_channel_number)) msg.append(generate_uint32(window_size)) msg.append(generate_uint32(maximum_packet_size)) self._ssh_transport_connection.send(''.join(msg)) # Check that a channel was opened successfully data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, self._remote_channel_number = parse_uint32(data, index) index, initial_window_size = parse_uint32(data, index) index, maximum_packet_size = parse_uint32(data, index) print colors.cyan('Message type: %d' % msg_type) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_OPEN_CONFIRMATION'] assert recipient_channel == self._local_channel_number print colors.cyan('Remote channel number: %d' % self._remote_channel_number) print colors.cyan('Initial window size: %d' % initial_window_size) print colors.cyan('Maximum window size: %d' % maximum_packet_size) # Ask to turn that session channel into a shell msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'])) msg.append(generate_uint32(self._remote_channel_number)) msg.append(generate_string('shell')) msg.append(generate_byte(1)) # True, we do want a reply here self._ssh_transport_connection.send(''.join(msg)) # OpenSSH then asks to increase their window size, that's fine, do it data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, bytes_to_add = parse_uint32(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_WINDOW_ADJUST'] initial_window_size += bytes_to_add # Check that they tell us they've opened a channel successfully data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_SUCCESS'] assert recipient_channel == self._local_channel_number print colors.cyan('Successfully opened shell!')
def _create_ssh_connection(self): # Read the global request that SSH sends us - this is trying to let us know all host keys, but # it's OpenSSH-specific, and we don't need it data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, request_name = parse_string(data, index) index, want_reply_byte = parse_byte(data, index) want_reply = want_reply_byte != 0 assert msg_type == SSH_MSG_NUMS['SSH_MSG_GLOBAL_REQUEST'] assert request_name == '*****@*****.**' assert not want_reply # Reply to let OpenSSH know that we don't know what they're talking about msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_REQUEST_FAILURE'])) self._ssh_transport_connection.send(''.join(msg)) # Actually get started with opening a channel for SSH communication window_size = 1048576 maximum_packet_size = 16384 # Request to open a session channel msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_OPEN'])) msg.append(generate_string('session')) msg.append(generate_uint32(self._local_channel_number)) msg.append(generate_uint32(window_size)) msg.append(generate_uint32(maximum_packet_size)) self._ssh_transport_connection.send(''.join(msg)) # Check that a channel was opened successfully data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, self._remote_channel_number = parse_uint32(data, index) index, initial_window_size = parse_uint32(data, index) index, maximum_packet_size = parse_uint32(data, index) print colors.cyan('Message type: %d' % msg_type) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_OPEN_CONFIRMATION'] assert recipient_channel == self._local_channel_number print colors.cyan('Remote channel number: %d' % self._remote_channel_number) print colors.cyan('Initial window size: %d' % initial_window_size) print colors.cyan('Maximum window size: %d' % maximum_packet_size) # Ask to turn that session channel into a shell msg = [] msg.append(generate_byte(SSH_MSG_NUMS['SSH_MSG_CHANNEL_REQUEST'])) msg.append(generate_uint32(self._remote_channel_number)) msg.append(generate_string('shell')) msg.append(generate_byte(1)) # True, we do want a reply here self._ssh_transport_connection.send(''.join(msg)) # OpenSSH then asks to increase their window size, that's fine, do it data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) index, recipient_channel = parse_uint32(data, index) index, bytes_to_add = parse_uint32(data, index) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_WINDOW_ADJUST'] initial_window_size += bytes_to_add # Check that they tell us they've opened a channel successfully data = self._ssh_transport_connection.read() index, msg_type = parse_byte(data, 0) assert msg_type == SSH_MSG_NUMS['SSH_MSG_CHANNEL_SUCCESS'] assert recipient_channel == self._local_channel_number print colors.cyan('Successfully opened shell!')