ICMP پر جوہری شیل

ICMP پر جوہری شیل

TL؛ ڈاکٹر: میں ایک کرنل ماڈیول لکھ رہا ہوں جو ICMP پے لوڈ سے کمانڈ پڑھے گا اور سرور پر ان پر عمل کرے گا چاہے آپ کا SSH کریش ہو جائے۔ سب سے زیادہ بے صبری کے لئے، تمام کوڈ ہے گاٹہوب.

احتیاط تجربہ کار سی پروگرامرز خون کے آنسو بہنے کا خطرہ رکھتے ہیں! میں اصطلاحات میں غلط بھی ہو سکتا ہوں، لیکن کوئی بھی تنقید خوش آئند ہے۔ اس پوسٹ کا مقصد ان لوگوں کے لیے ہے جو سی پروگرامنگ کے بارے میں بہت کم خیال رکھتے ہیں اور لینکس کے اندرونی حصے کو دیکھنا چاہتے ہیں۔

میرے پہلے تبصرے میں آرٹیکل SoftEther VPN کا ذکر کیا، جو کچھ "باقاعدہ" پروٹوکول کی نقل کر سکتا ہے، خاص طور پر HTTPS، ICMP اور یہاں تک کہ DNS۔ میں ان میں سے صرف پہلا کام کرنے کا تصور کر سکتا ہوں، کیونکہ میں HTTP(S) سے بہت واقف ہوں، اور مجھے ICMP اور DNS پر ٹنلنگ سیکھنی تھی۔

ICMP پر جوہری شیل

ہاں، 2020 میں میں نے سیکھا کہ آپ ICMP پیکٹوں میں صوابدیدی پے لوڈ ڈال سکتے ہیں۔ لیکن دیر سے بہتر کبھی نہیں! اور چونکہ اس کے بارے میں کچھ کیا جا سکتا ہے، اس لیے اسے کرنے کی ضرورت ہے۔ چونکہ اپنی روزمرہ کی زندگی میں میں اکثر کمانڈ لائن استعمال کرتا ہوں، بشمول SSH کے ذریعے، اس لیے سب سے پہلے میرے ذہن میں ICMP شیل کا خیال آیا۔ اور ایک مکمل بلشیلڈ بنگو کو جمع کرنے کے لیے، میں نے اسے ایک ایسی زبان میں لینکس ماڈیول کے طور پر لکھنے کا فیصلہ کیا جس کے بارے میں مجھے صرف اندازہ ہے۔ ایسا شیل عمل کی فہرست میں نظر نہیں آئے گا، آپ اسے کرنل میں لوڈ کر سکتے ہیں اور یہ فائل سسٹم پر نہیں ہو گا، آپ کو سننے والی بندرگاہوں کی فہرست میں کوئی مشکوک چیز نظر نہیں آئے گی۔ اس کی صلاحیتوں کے لحاظ سے، یہ ایک مکمل روٹ کٹ ہے، لیکن مجھے امید ہے کہ اس میں بہتری آئے گی اور اسے آخری حربے کے شیل کے طور پر استعمال کیا جائے گا جب SSH کے ذریعے لاگ ان کرنے اور اس پر عمل درآمد کرنے کے لیے لوڈ ایوریج بہت زیادہ ہو۔ echo i > /proc/sysrq-triggerریبوٹ کیے بغیر رسائی بحال کرنے کے لیے۔

ہم ایک ٹیکسٹ ایڈیٹر لیتے ہیں، Python اور C میں پروگرامنگ کی بنیادی مہارتیں، گوگل اور مجازی مشین جسے آپ کو چاقو کے نیچے رکھنے میں کوئی اعتراض نہیں ہے اگر سب کچھ ٹوٹ جائے (اختیاری - مقامی ورچوئل باکس/KVM/etc) اور چلیں!

کلائنٹ حصہ۔

مجھے ایسا لگ رہا تھا کہ کلائنٹ کے حصے کے لیے مجھے تقریباً 80 لائنوں پر مشتمل اسکرپٹ لکھنا پڑے گا، لیکن ایسے مہربان لوگ تھے جنہوں نے میرے لیے ایسا کیا۔ تمام کام. کوڈ غیر متوقع طور پر آسان نکلا، 10 اہم لائنوں میں فٹ ہوتا ہے:

import sys
from scapy.all import sr1, IP, ICMP

if len(sys.argv) < 3:
    print('Usage: {} IP "command"'.format(sys.argv[0]))
    exit(0)

p = sr1(IP(dst=sys.argv[1])/ICMP()/"run:{}".format(sys.argv[2]))
if p:
    p.show()

اسکرپٹ دو دلائل لیتا ہے، ایک پتہ اور ایک پے لوڈ۔ بھیجنے سے پہلے، پے لوڈ ایک کلید سے پہلے ہوتا ہے۔ run:، ہمیں بے ترتیب پے لوڈز والے پیکجوں کو خارج کرنے کے لیے اس کی ضرورت ہوگی۔

کرنل کو پیکجوں کو تیار کرنے کے لیے مراعات کی ضرورت ہوتی ہے، اس لیے اسکرپٹ کو بطور سپر یوزر چلانا ہوگا۔ پھانسی کی اجازت دینا اور اسکیپی خود انسٹال کرنا نہ بھولیں۔ Debian نامی ایک پیکیج ہے python3-scapy. اب آپ چیک کر سکتے ہیں کہ یہ سب کیسے کام کرتا ہے۔

کمانڈ کو چلانا اور آؤٹ پٹ کرنا
morq@laptop:~/icmpshell$ sudo ./send.py 45.11.26.232 "Hello, world!"
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 45
id = 17218
flags =
frag = 0
ttl = 58
proto = icmp
chksum = 0x3403
src = 45.11.26.232
dst = 192.168.0.240
options
###[ ICMP ]###
type = echo-reply
code = 0
chksum = 0xde03
id = 0x0
seq = 0x0
###[ Raw ]###
load = 'run:Hello, world!

سنیفر میں ایسا ہی لگتا ہے۔
morq@laptop:~/icmpshell$ sudo tshark -i wlp1s0 -O icmp -f "icmp and host 45.11.26.232"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'wlp1s0'
Frame 1: 59 bytes on wire (472 bits), 59 bytes captured (472 bits) on interface wlp1s0, id 0
Internet Protocol Version 4, Src: 192.168.0.240, Dst: 45.11.26.232
Internet Control Message Protocol
Type: 8 (Echo (ping) request)
Code: 0
Checksum: 0xd603 [correct] [Checksum Status: Good] Identifier (BE): 0 (0x0000)
Identifier (LE): 0 (0x0000)
Sequence number (BE): 0 (0x0000)
Sequence number (LE): 0 (0x0000)
Data (17 bytes)

0000 72 75 6e 3a 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 run:Hello, world
0010 21 !
Data: 72756e3a48656c6c6f2c20776f726c6421
[Length: 17]

Frame 2: 59 bytes on wire (472 bits), 59 bytes captured (472 bits) on interface wlp1s0, id 0
Internet Protocol Version 4, Src: 45.11.26.232, Dst: 192.168.0.240
Internet Control Message Protocol
Type: 0 (Echo (ping) reply)
Code: 0
Checksum: 0xde03 [correct] [Checksum Status: Good] Identifier (BE): 0 (0x0000)
Identifier (LE): 0 (0x0000)
Sequence number (BE): 0 (0x0000)
Sequence number (LE): 0 (0x0000)
[Request frame: 1] [Response time: 19.094 ms] Data (17 bytes)

0000 72 75 6e 3a 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 run:Hello, world
0010 21 !
Data: 72756e3a48656c6c6f2c20776f726c6421
[Length: 17]

^C2 packets captured

رسپانس پیکج میں پے لوڈ تبدیل نہیں ہوتا ہے۔

دانا ماڈیول

ڈیبین ورچوئل مشین بنانے کے لیے آپ کو کم از کم ضرورت ہوگی۔ make и linux-headers-amd64، باقی انحصار کی شکل میں آئے گا۔ میں مضمون میں پورا کوڈ فراہم نہیں کروں گا؛ آپ اسے Github پر کلون کرسکتے ہیں۔

ہک سیٹ اپ

شروع کرنے کے لیے، ہمیں ماڈیول کو لوڈ کرنے اور اسے اتارنے کے لیے دو فنکشنز کی ضرورت ہے۔ ان لوڈنگ کے لیے فنکشن کی ضرورت نہیں ہے، لیکن پھر rmmod یہ کام نہیں کرے گا؛ ماڈیول صرف اس وقت اتارا جائے گا جب اسے آف کیا جائے۔

#include <linux/module.h>
#include <linux/netfilter_ipv4.h>

static struct nf_hook_ops nfho;

static int __init startup(void)
{
  nfho.hook = icmp_cmd_executor;
  nfho.hooknum = NF_INET_PRE_ROUTING;
  nfho.pf = PF_INET;
  nfho.priority = NF_IP_PRI_FIRST;
  nf_register_net_hook(&init_net, &nfho);
  return 0;
}

static void __exit cleanup(void)
{
  nf_unregister_net_hook(&init_net, &nfho);
}

MODULE_LICENSE("GPL");
module_init(startup);
module_exit(cleanup);

یہاں کیا ہو رہا ہے:

  1. ماڈیول اور نیٹ فلٹر میں ہیرا پھیری کرنے کے لیے دو ہیڈر فائلیں کھینچی جاتی ہیں۔
  2. تمام آپریشنز نیٹ فلٹر سے ہوتے ہیں، آپ اس میں ہکس لگا سکتے ہیں۔ ایسا کرنے کے لیے، آپ کو اس ڈھانچے کا اعلان کرنے کی ضرورت ہے جس میں ہک کو ترتیب دیا جائے گا۔ سب سے اہم چیز اس فنکشن کی وضاحت کرنا ہے جو ہک کے طور پر انجام دیا جائے گا: nfho.hook = icmp_cmd_executor; میں بعد میں خود فنکشن میں پہنچ جاؤں گا۔
    پھر میں نے پیکیج کے لئے پروسیسنگ کا وقت مقرر کیا: NF_INET_PRE_ROUTING جب یہ پہلی بار کرنل میں ظاہر ہوتا ہے تو پیکیج پر کارروائی کرنے کی وضاحت کرتا ہے۔ استعمال کیا جا سکتا ہے NF_INET_POST_ROUTING پیکٹ پر کارروائی کرنے کے لیے جب یہ دانا سے باہر نکلتا ہے۔
    میں نے فلٹر کو IPv4 پر سیٹ کیا: nfho.pf = PF_INET;.
    میں اپنے ہک کو سب سے زیادہ ترجیح دیتا ہوں: nfho.priority = NF_IP_PRI_FIRST;
    اور میں ڈیٹا ڈھانچے کو اصل ہک کے طور پر رجسٹر کرتا ہوں: nf_register_net_hook(&init_net, &nfho);
  3. حتمی فنکشن ہک کو ہٹاتا ہے۔
  4. لائسنس میں واضح طور پر اشارہ کیا گیا ہے تاکہ مرتب کرنے والا شکایت نہ کرے۔
  5. افعال module_init() и module_exit() ماڈیول کو شروع کرنے اور ختم کرنے کے لیے دوسرے فنکشن سیٹ کریں۔

پے لوڈ کو بازیافت کیا جا رہا ہے۔

اب ہمیں پے لوڈ نکالنے کی ضرورت ہے، یہ سب سے مشکل کام نکلا۔ پے لوڈز کے ساتھ کام کرنے کے لیے کرنل میں بلٹ ان فنکشنز نہیں ہیں؛ آپ صرف اعلیٰ سطح کے پروٹوکول کے ہیڈرز کو پارس کر سکتے ہیں۔

#include <linux/ip.h>
#include <linux/icmp.h>

#define MAX_CMD_LEN 1976

char cmd_string[MAX_CMD_LEN];

struct work_struct my_work;

DECLARE_WORK(my_work, work_handler);

static unsigned int icmp_cmd_executor(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
  struct iphdr *iph;
  struct icmphdr *icmph;

  unsigned char *user_data;
  unsigned char *tail;
  unsigned char *i;
  int j = 0;

  iph = ip_hdr(skb);
  icmph = icmp_hdr(skb);

  if (iph->protocol != IPPROTO_ICMP) {
    return NF_ACCEPT;
  }
  if (icmph->type != ICMP_ECHO) {
    return NF_ACCEPT;
  }

  user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
  tail = skb_tail_pointer(skb);

  j = 0;
  for (i = user_data; i != tail; ++i) {
    char c = *(char *)i;

    cmd_string[j] = c;

    j++;

    if (c == '')
      break;

    if (j == MAX_CMD_LEN) {
      cmd_string[j] = '';
      break;
    }

  }

  if (strncmp(cmd_string, "run:", 4) != 0) {
    return NF_ACCEPT;
  } else {
    for (j = 0; j <= sizeof(cmd_string)/sizeof(cmd_string[0])-4; j++) {
      cmd_string[j] = cmd_string[j+4];
      if (cmd_string[j] == '')
	break;
    }
  }

  schedule_work(&my_work);

  return NF_ACCEPT;
}

کیا ہو رہا ہے:

  1. مجھے اضافی ہیڈر فائلوں کو شامل کرنا پڑا، اس بار آئی پی اور آئی سی ایم پی ہیڈرز کو جوڑنا۔
  2. میں نے زیادہ سے زیادہ لائن کی لمبائی مقرر کی: #define MAX_CMD_LEN 1976. بالکل ایسا کیوں؟ کیونکہ مرتب کرنے والے کو اس کی شکایت ہے! وہ مجھے پہلے ہی مشورہ دے چکے ہیں کہ مجھے اسٹیک اور ہیپ کو سمجھنے کی ضرورت ہے، کسی دن میں یہ ضرور کروں گا اور شاید کوڈ کو بھی درست کردوں گا۔ میں نے فوری طور پر لائن سیٹ کی جس میں کمانڈ ہو گی: char cmd_string[MAX_CMD_LEN];. یہ تمام افعال میں نظر آنا چاہیے؛ میں پیراگراف 9 میں اس کے بارے میں مزید تفصیل سے بات کروں گا۔
  3. اب ہمیں شروع کرنے کی ضرورت ہے (struct work_struct my_work;) ساخت اور اسے کسی اور فنکشن سے جوڑیں (DECLARE_WORK(my_work, work_handler);)۔ میں اس بارے میں بھی بات کروں گا کہ یہ نویں پیراگراف میں کیوں ضروری ہے۔
  4. اب میں ایک فنکشن کا اعلان کرتا ہوں، جو ایک ہک ہوگا۔ قسم اور قبول شدہ دلائل نیٹ فلٹر کے ذریعہ طے کیے جاتے ہیں، ہمیں صرف اس میں دلچسپی ہے۔ skb. یہ ایک ساکٹ بفر ہے، ایک بنیادی ڈیٹا ڈھانچہ جس میں پیکٹ کے بارے میں تمام دستیاب معلومات ہوتی ہیں۔
  5. فنکشن کے کام کرنے کے لیے، آپ کو دو ڈھانچے اور متعدد متغیرات کی ضرورت ہوگی، بشمول دو تکرار کرنے والے۔
      struct iphdr *iph;
      struct icmphdr *icmph;
    
      unsigned char *user_data;
      unsigned char *tail;
      unsigned char *i;
      int j = 0;
  6. ہم منطق کے ساتھ شروع کر سکتے ہیں. ماڈیول کے کام کرنے کے لیے، ICMP Echo کے علاوہ کسی پیکٹ کی ضرورت نہیں ہے، اس لیے ہم بلٹ ان فنکشنز کا استعمال کرتے ہوئے بفر کو پارس کرتے ہیں اور تمام غیر ICMP اور غیر Echo پیکٹوں کو باہر پھینک دیتے ہیں۔ واپسی NF_ACCEPT پیکیج کی منظوری کا مطلب ہے، لیکن آپ واپس آکر بھی پیکجوں کو چھوڑ سکتے ہیں۔ NF_DROP.
      iph = ip_hdr(skb);
      icmph = icmp_hdr(skb);
    
      if (iph->protocol != IPPROTO_ICMP) {
        return NF_ACCEPT;
      }
      if (icmph->type != ICMP_ECHO) {
        return NF_ACCEPT;
      }

    میں نے جانچ نہیں کی کہ آئی پی ہیڈرز کو چیک کیے بغیر کیا ہوگا۔ C کے بارے میں میرا کم سے کم علم مجھے بتاتا ہے کہ بغیر کسی اضافی جانچ کے، کچھ خوفناک ہونے والا ہے۔ اگر آپ مجھے اس سے باز رکھیں تو مجھے خوشی ہوگی!

  7. اب جب کہ پیکیج بالکل اسی قسم کا ہے جس کی آپ کو ضرورت ہے، آپ ڈیٹا نکال سکتے ہیں۔ بلٹ ان فنکشن کے بغیر، آپ کو پہلے پے لوڈ کے آغاز کے لیے ایک پوائنٹر حاصل کرنا ہوگا۔ یہ ایک جگہ پر کیا جاتا ہے، آپ کو پوائنٹر کو ICMP ہیڈر کے شروع میں لے جانا اور اسے اس ہیڈر کے سائز میں منتقل کرنا ہوگا۔ ہر چیز ساخت کا استعمال کرتی ہے۔ icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
    ہیڈر کا اختتام پے لوڈ کے آخر سے مماثل ہونا چاہیے۔ skbلہذا ہم اسے اسی ساخت سے جوہری ذرائع کا استعمال کرتے ہوئے حاصل کرتے ہیں: tail = skb_tail_pointer(skb);.

    ICMP پر جوہری شیل

    تصویر چوری ہو گئی تھی۔ اس وجہ سے، آپ ساکٹ بفر کے بارے میں مزید پڑھ سکتے ہیں۔

  8. ایک بار جب آپ کے پاس شروع اور اختتام کی طرف اشارہ ہو جائے تو، آپ ڈیٹا کو سٹرنگ میں کاپی کر سکتے ہیں۔ cmd_stringایک سابقہ ​​کی موجودگی کے لیے اسے چیک کریں۔ run: اور، یا تو پیکیج غائب ہونے کی صورت میں اسے ضائع کر دیں، یا اس سابقے کو ہٹا کر لائن کو دوبارہ لکھیں۔
  9. بس، اب آپ کسی دوسرے ہینڈلر کو کال کر سکتے ہیں: schedule_work(&my_work);. چونکہ ایسی کال میں پیرامیٹر کو منتقل کرنا ممکن نہیں ہوگا، اس لیے کمانڈ کے ساتھ لائن عالمی ہونی چاہیے۔ schedule_work() پاس شدہ ڈھانچے سے وابستہ فنکشن کو ٹاسک شیڈیولر کی عمومی قطار میں رکھ دے گا اور مکمل کر دے گا، جس سے آپ کمانڈ کے مکمل ہونے کا انتظار نہیں کریں گے۔ یہ ضروری ہے کیونکہ ہک بہت تیز ہونا چاہئے۔ بصورت دیگر، آپ کی پسند یہ ہے کہ کچھ بھی شروع نہیں ہوگا یا آپ کو کرنل گھبراہٹ ہوگی۔ تاخیر موت جیسی ہے!
  10. بس، آپ اسی واپسی کے ساتھ پیکیج کو قبول کر سکتے ہیں۔

یوزر اسپیس میں کسی پروگرام کو کال کرنا

یہ فنکشن سب سے زیادہ قابل فہم ہے۔ میں اس کا نام دیا گیا تھا۔ DECLARE_WORK()قسم اور قبول شدہ دلائل دلچسپ نہیں ہیں۔ ہم کمانڈ کے ساتھ لائن لیتے ہیں اور اسے مکمل طور پر شیل میں منتقل کرتے ہیں۔ اسے تجزیہ کرنے، بائنریز کی تلاش اور ہر چیز سے نمٹنے دیں۔

static void work_handler(struct work_struct * work)
{
  static char *argv[] = {"/bin/sh", "-c", cmd_string, NULL};
  static char *envp[] = {"PATH=/bin:/sbin", NULL};

  call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
}

  1. دلائل کو تاروں کی ایک صف پر سیٹ کریں۔ argv[]. میں فرض کروں گا کہ ہر کوئی جانتا ہے کہ پروگرام درحقیقت اس طرح چلائے جاتے ہیں، نہ کہ خالی جگہوں کے ساتھ مسلسل لائن کے طور پر۔
  2. ماحولیاتی متغیرات مرتب کریں۔ میں نے صرف کم از کم راستوں کے سیٹ کے ساتھ PATH داخل کیا، اس امید پر کہ وہ سب پہلے سے ہی اکٹھے ہو چکے ہیں /bin с /usr/bin и /sbin с /usr/sbin. دوسرے راستے شاذ و نادر ہی عملی طور پر اہمیت رکھتے ہیں۔
  3. ہو گیا، چلو کرتے ہیں! دانا فنکشن call_usermodehelper() داخلہ قبول کرتا ہے۔ بائنری کا راستہ، دلائل کی صف، ماحولیاتی متغیرات کی صف۔ یہاں میں یہ بھی فرض کرتا ہوں کہ ہر کوئی ایک الگ دلیل کے طور پر ایگزیکیوٹیبل فائل کا راستہ پاس کرنے کا مطلب سمجھتا ہے، لیکن آپ پوچھ سکتے ہیں۔ آخری دلیل یہ بتاتی ہے کہ آیا عمل مکمل ہونے کا انتظار کرنا ہے (UMH_WAIT_PROC) عمل شروع (UMH_WAIT_EXECیا بالکل بھی انتظار نہ کریں (UMH_NO_WAIT)۔ کچھ اور ہے؟ UMH_KILLABLE، میں نے اس پر غور نہیں کیا۔

اسمبلی

کرنل ماڈیولز کی اسمبلی کرنل میک فریم ورک کے ذریعے کی جاتی ہے۔ بلایا make کرنل ورژن سے منسلک ایک خصوصی ڈائریکٹری کے اندر (یہاں بیان کیا گیا ہے: KERNELDIR:=/lib/modules/$(shell uname -r)/build)، اور ماڈیول کا مقام متغیر کو دیا جاتا ہے۔ M دلائل میں. icmpshell.ko اور صاف اہداف اس فریم ورک کو مکمل طور پر استعمال کرتے ہیں۔ میں obj-m آبجیکٹ فائل کی نشاندہی کرتا ہے جسے ماڈیول میں تبدیل کیا جائے گا۔ نحو جو دوبارہ بناتا ہے۔ main.o в icmpshell.o (icmpshell-objs = main.o) مجھے زیادہ منطقی نہیں لگتا، لیکن ایسا ہی ہو۔

KERNELDIR:=/lib/modules/$(shell uname -r)/build

obj-m = icmpshell.o
icmpshell-objs = main.o

all: icmpshell.ko

icmpshell.ko: main.c
make -C $(KERNELDIR) M=$(PWD) modules

clean:
make -C $(KERNELDIR) M=$(PWD) clean

ہم جمع کرتے ہیں: make. لوڈ ہو رہا ہے: insmod icmpshell.ko. ہو گیا، آپ چیک کر سکتے ہیں: sudo ./send.py 45.11.26.232 "date > /tmp/test". اگر آپ کی مشین پر کوئی فائل ہے۔ /tmp/test اور اس میں درخواست بھیجی گئی تاریخ پر مشتمل ہے، جس کا مطلب ہے کہ آپ نے سب کچھ ٹھیک کیا اور میں نے سب کچھ ٹھیک کیا۔

حاصل يہ ہوا

جوہری ترقی کے ساتھ میرا پہلا تجربہ میری توقع سے کہیں زیادہ آسان تھا۔ یہاں تک کہ سی میں ترقی کرنے کے تجربے کے بغیر، کمپائلر اشارے اور گوگل کے نتائج پر توجہ مرکوز کرتے ہوئے، میں ایک ورکنگ ماڈیول لکھنے اور کرنل ہیکر کی طرح محسوس کرنے کے قابل تھا، اور ساتھ ہی ساتھ ایک اسکرپٹ کِڈی بھی۔ اس کے علاوہ، میں Kernel Newbies چینل پر گیا، جہاں مجھے استعمال کرنے کے لیے کہا گیا۔ schedule_work() کال کرنے کے بجائے call_usermodehelper() خود ہک کے اندر اور اسے شرمندہ کیا، بجا طور پر ایک گھوٹالے کا شبہ تھا۔ کوڈ کی ایک سو لائنوں میں میرے فارغ وقت میں تقریباً ایک ہفتہ ترقی ہوتی ہے۔ ایک کامیاب تجربہ جس نے نظام کی ترقی کی زبردست پیچیدگی کے بارے میں میرے ذاتی تصور کو تباہ کر دیا۔

اگر کوئی گیتوب پر کوڈ کا جائزہ لینے پر راضی ہوتا ہے تو میں شکر گزار ہوں گا۔ مجھے پورا یقین ہے کہ میں نے بہت سی احمقانہ غلطیاں کی ہیں، خاص طور پر جب ڈور کے ساتھ کام کرنا۔

ICMP پر جوہری شیل

ماخذ: www.habr.com

نیا تبصرہ شامل کریں