/proc is a virtual file system that is created each time a system boots and is dissolved during shutdown.
The directory /proc contains (among other things) one subdirectory for each process running on the system, which is named after the process ID (PID).

One such directory is /proc/<PID>/net
This directory provides a comprehensive look at various networking parameters and statistics. Each directory and virtual file within this directory describes aspects of the system's network configuration.

For example:
/proc/<PID>/net/tcp — Contains detailed TCP socket information.

A sample file looks like

/proc/net

 

DECODING THE VALUES

The values are divided in different columns like local_address, rem_address [remote address], st [status code], etc

Values in each column is stored in Hexadeciaml notation in 'Little Endian' format

This means

0100007F should be read as
7F 00 00 01 
which gets decoded to 127.0.0.1

and the value of port number 8124 gets decoded to 33060 

This is also shown in below image

The below script created in Ruby decodes the fields and print in a tabular format. All it needs is process ID as the parameter. The script also displays the command line used to execute the process and the Apparmor status of process

 

The output from the script is given below

https://i.ibb.co/SNy6DLX/ruby-script.png
 

 

The code

#!/bin/env ruby

=begin
A DECODER FOR /PROC/<PID>/NET/TCP or UDP FILE
DISPLAYS NETWORK STATS AND INFORMATION OF PROCESS
COMMAND LINE AND APPARMOR STATUS OF PROCESS
REGARDS: ALPHA THREAT IT SOLUTIONS

USAGE : ruby proc_net.rb <PROCESS ID> 
 process ID can be obtained via ps -A command
=end

local_ip=[]; local_port=[]; remote_address=[]; remote_port=[]; state=[]
final_local=["\nLocal_IP : PORT\t\t\t\b REMOTE_IP : REMOTE_PORT\t\tSTATE\n"]

filename="/proc/#{ARGV[0]}/net/tcp"

begin

a=File.readlines(filename).each do |x|

if x.match?(/[A-Z 0-9]{8}:[A-Z -9]{4}/)

y = x.split(":")
local_ip << y[1].sub!(" ",'')
local_port << y[2].split(" ")[0]
remote_address << y[2].split(" ")[1]
remote_port << y[3].split(" ")[0]

if y[4].split(" ")[1] == '00'
state << "ERROR_STATUS"
elsif y[4].split(" ")[1] == '01'
state << "TCP_ESTABLISHED"
elsif y[4].split(" ")[1] == '02'
state << "TCP_SYN_SENT"
elsif y[4].split(" ")[1] == '03'
state << "TCP_SYN_RECV"
elsif y[4].split(" ")[1] == '04'
state << "TCP_FIN_WAIT1"
elsif y[4].split(" ")[1] == '05'
state << "TCP_FIN_WAIT2"
elsif y[4].split(" ")[1] == '06'
state << "TCP_TIME_WAIT"
elsif y[4].split(" ")[1] == '07'
state << "TCP_CLOSE"
elsif y[4].split(" ")[1] == '00'
state << "TCP_CLOSE_WAIT"
elsif y[4].split(" ")[1] == '09'
state << "TCP_LAST_ACK"
elsif y[4].split(" ")[1] == '0A'
state << "TCP_LISTEN"
elsif y[4].split(" ")[1] == '0B'
state << "TCP_CLOSING"
else
state << "UNKNOWN"

#if end
end

end

rescue Exception
next
#exception end
end
#do end
end

def print_ip(x)
return  x.scan(/../).reverse.map{|x| x.to_i(16)}.join('.')
end

def print_port(y)
return y.to_i(16)
end

for length in 0..local_ip.count-1

#sets spacing for printing columns
size=print_ip(local_ip[length]).to_s + ' : ' + print_port(local_port[length]).to_s
size=size.length.to_i
fs=23-size
size=print_ip(remote_address[length]).to_s + ' : ' + print_port(remote_port[length]).to_s
size=size.length.to_i
ns=23-size

#create a final array with all data
final_local << print_ip(local_ip[length]).to_s + ' : ' + print_port(local_port[length]).to_s + " "*fs + "\t\t" +  print_ip(remote_address[length]).to_s + ' : ' + print_port(remote_port[length]).to_s + " "*ns + "\t\t" + state[length].to_s
end

puts final_local
puts "\nProcess Command Line: " + File.read("/proc/#{ARGV[0]}/cmdline") + "\n\n" if File.exists?("/proc/#{ARGV[0]}/cmdline")
puts "AppArmour status: "  + File.read("/proc/#{ARGV[0]}/attr/apparmor/current") + "\n" if File.exists?("/proc/#{ARGV[0]}/attr/apparmor/current")