احتياط تجربيڪار سي پروگرامر رت جا ڳوڙها ڦاٽي پوڻ جو خطرو! مان شايد اصطلاحن ۾ غلط به هجان، پر ڪنهن به تنقيد جو استقبال آهي. پوسٽ انهن لاءِ آهي جن کي سي پروگرامنگ جو تمام گهڻو خيال آهي ۽ اهي لينڪس جي اندرين کي ڏسڻ چاهيندا آهن.
تبصرن ۾ منهنجي پهرين مضمون ذڪر ڪيو ويو آهي SoftEther VPN، جيڪو ڪجهه "باقاعده" پروٽوڪول کي نقل ڪري سگهي ٿو، خاص طور تي HTTPS، ICMP ۽ جيتوڻيڪ DNS. مان تصور ڪري سگهان ٿو ته انهن مان صرف پهريون ڪم ڪري رهيو آهي، ڇاڪاڻ ته مان HTTP(S) سان تمام گهڻو واقف آهيان، ۽ مون کي ICMP ۽ DNS تي سرنگنگ سکڻو پيو.
ها، 2020 ۾ مون سکيو ته توهان ICMP پيڪٽس ۾ هڪ صوابديدي پيل لوڊ داخل ڪري سگهو ٿا. پر بهتر دير کان ڪڏهن به نه! ۽ جيئن ته ان بابت ڪجهه ڪري سگهجي ٿو، پوء اهو ڪرڻ جي ضرورت آهي. جيئن ته منهنجي روزاني زندگي ۾ آئون اڪثر ڪري ڪمانڊ لائن استعمال ڪندو آهيان، بشمول SSH ذريعي، هڪ ICMP شيل جو خيال پهريون ڀيرو منهنجي ذهن ۾ آيو. ۽ هڪ مڪمل بيلشيلڊ بنگو گڏ ڪرڻ لاءِ، مون ان کي لکڻ جو فيصلو ڪيو لينڪس ماڊل جي طور تي هڪ ٻوليءَ ۾ جنهن جو مون کي صرف هڪ اندازو آهي. اهڙو شيل پروسيس جي لسٽ ۾ نظر نه ايندو، توهان ان کي ڪرنل ۾ لوڊ ڪري سگهو ٿا ۽ اهو فائل سسٽم تي نه هوندو، توهان کي ٻڌڻ واري بندرگاهن جي فهرست ۾ ڪجهه به مشڪوڪ نظر نه ايندو. ان جي صلاحيتن جي لحاظ کان، هي هڪ مڪمل روٽ ڪٽ آهي، پر مون کي اميد آهي ته ان کي بهتر بڻائي ۽ ان کي آخري ريسٽورٽ جي شيل طور استعمال ڪيو وڃي جڏهن لوڊ اوسط تمام گهڻي آهي SSH ذريعي لاگ ان ٿيڻ ۽ گهٽ ۾ گهٽ عمل ڪرڻ. echo i > /proc/sysrq-triggerريبوٽ ڪرڻ کان سواءِ رسائي بحال ڪرڻ لاءِ.
مون کي لڳي رهيو هو ته ڪلائنٽ جي حصي لاءِ مون کي اٽڪل 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:، اسان کي ان جي ضرورت پوندي بي ترتيب پيل لوڊ سان پيڪيجز کي خارج ڪرڻ لاءِ.
ڪرنل کي پيڪيجز ٺاهڻ لاءِ استحقاق جي ضرورت آهي، تنهنڪري اسڪرپٽ کي سپر يوزر طور هلائڻو پوندو. لڳائڻ جي اجازت ڏيڻ نه وساريو ۽ پاڻ کي اسڪيپي انسٽال ڪريو. ديبين نالي هڪ پيڪيج آهي 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)
ديبين ورچوئل مشين ٺاهڻ لاءِ توهان کي گهٽ ۾ گهٽ ضرورت پوندي make и linux-headers-amd64، باقي انحصار جي صورت ۾ ايندا. مان مضمون ۾ پورو ڪوڊ مهيا نه ڪندس؛ توھان ان کي ڪلون ڪري سگھوٿا Github.
ٿلهو سيٽ اپ
شروع ڪرڻ سان، اسان کي ماڊل لوڊ ڪرڻ ۽ ان کي لوڊ ڪرڻ لاءِ ٻن ڪمن جي ضرورت آھي. unloading لاء فنڪشن ضروري نه آهي، پر پوء rmmod اهو ڪم نه ڪندو؛ ماڊل صرف ان لوڊ ڪيو ويندو جڏهن بند ڪيو ويندو.
مون کي اضافي هيڊر فائلون شامل ڪرڻيون هيون، هن ڀيري IP ۽ ICMP هيڊرز کي ترتيب ڏيڻ لاءِ.
مون وڌ ۾ وڌ لڪير جي ڊيگهه مقرر ڪئي: #define MAX_CMD_LEN 1976. بلڪل ائين ڇو؟ ڇو ته مرتب ڪندڙ ان بابت شڪايت ڪري ٿو! انهن مون کي اڳ ۾ ئي صلاح ڏني آهي ته مون کي اسٽيڪ ۽ هيپ کي سمجهڻ جي ضرورت آهي، هڪ ڏينهن مان اهو ضرور ڪندس ۽ شايد ڪوڊ کي به درست ڪندس. مون فوري طور تي لڪير مقرر ڪيو جنهن ۾ حڪم هوندو: char cmd_string[MAX_CMD_LEN];. اهو سڀني ڪمن ۾ ظاهر ٿيڻ گهرجي؛ مان هن بابت وڌيڪ تفصيل سان پيراگراف 9 ۾ ڳالهائيندس.
هاڻي اسان کي شروع ڪرڻ جي ضرورت آهي (struct work_struct my_work;) ساخت ۽ ان کي ٻئي فنڪشن سان ڳنڍيو (DECLARE_WORK(my_work, work_handler);). مان اهو به ڳالهائيندس ته اهو نائين پيراگراف ۾ ڇو ضروري آهي.
هاڻي مان هڪ فنڪشن جو اعلان ڪريان ٿو، جيڪو هڪ ٿلهو هوندو. قسم ۽ قبول ٿيل دلائل نيٽ فلٽر طرفان ترتيب ڏنل آهن، اسان کي صرف دلچسپي آهي skb. هي هڪ ساکٽ بفر آهي، هڪ بنيادي ڊيٽا جو ڍانچو جنهن ۾ هڪ پيڪٽ بابت سڀ موجود معلومات شامل آهي.
ڪم ڪرڻ لاءِ فنڪشن لاءِ، توھان کي ضرورت پوندي ٻن اڏاوتن ۽ گھڻن متغيرن، جن ۾ ٻه اِيٽرٽر شامل آھن.
اسان منطق سان شروع ڪري سگهون ٿا. ماڊل کي ڪم ڪرڻ لاءِ، ICMP ايڪو کان سواءِ ڪنهن به پيڪيٽ جي ضرورت نه آهي، تنهنڪري اسان بلٽ ان فنڪشن استعمال ڪندي بفر کي پارس ڪريون ٿا ۽ سڀني غير ICMP ۽ غير ايڪو پيڪيٽ کي ٻاهر ڪڍون ٿا. واپسي NF_ACCEPT مطلب ته پيڪيج جي قبوليت، پر توهان واپسي ذريعي پيڪيجز کي به ڇڏي سگھو ٿا NF_DROP.
مون جانچ نه ڪئي آهي ته IP هيڊرن کي چيڪ ڪرڻ کان سواءِ ڇا ٿيندو. سي جي منهنجي گهٽ ۾ گهٽ ڄاڻ مون کي ٻڌائي ٿي ته اضافي چيڪن جي بغير، ڪجهه خوفناڪ ٿيڻ جو پابند آهي. مون کي خوشي ٿي ويندي جيڪڏهن توهان مون کي هن کان روڪيو!
هاڻي ته پيڪيج صحيح قسم جو آهي جيڪو توهان کي گهربل آهي، توهان ڊيٽا ڪڍي سگهو ٿا. بغير ڪنهن تعمير ٿيل فنڪشن جي، توهان کي پهريان هڪ پوائنٽر حاصل ڪرڻو پوندو پيلي لوڊ جي شروعات ڏانهن. اهو هڪ جڳهه تي ڪيو ويو آهي، توهان کي پوائنٽر کي ICMP هيڊر جي شروعات ڏانهن وٺي وڃڻ جي ضرورت آهي ۽ ان کي هن هيڊر جي سائيز ڏانهن منتقل ڪرڻ جي ضرورت آهي. هر شي ساخت کي استعمال ڪري ٿو icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
هيڊر جي پڇاڙيءَ ۾ پيل لوڊ جي پڇاڙيءَ سان ملڻ گهرجي skbتنهن ڪري اسان ان کي حاصل ڪريون ٿا ايٽمي ذريعن سان لاڳاپيل ساخت مان: tail = skb_tail_pointer(skb);.
هڪ دفعو توهان وٽ شروع ۽ آخر ڏانهن اشارو آهي، توهان ڊيٽا کي هڪ تار ۾ نقل ڪري سگهو ٿا cmd_string، ان کي چيڪ ڪريو اڳي جي موجودگي لاءِ run: ۽، يا ته پيڪيج کي رد ڪريو جيڪڏھن اھو غائب آھي، يا ھن اڳياڙي کي ختم ڪندي، لڪير کي ٻيهر لکو.
اھو اھو آھي، ھاڻي توھان ٻئي ھينڊلر کي سڏي سگھو ٿا: schedule_work(&my_work);. جيئن ته اهو ممڪن نه ٿيندو ته اهڙي ڪال تي هڪ پيٽرولر کي منتقل ڪرڻ لاء، حڪم سان لائن گلوبل هجڻ گهرجي. schedule_work() پاس ٿيل ڍانچي سان لاڳاپيل فنڪشن کي ٽاسڪ شيڊولر جي عام قطار ۾ رکي ٿو ۽ مڪمل ڪيو ويندو، توهان کي حڪم جي مڪمل ٿيڻ جو انتظار نه ڪرڻ جي اجازت ڏيندو. اهو ضروري آهي ڇو ته ٿلهو تمام تيز هجڻ گهرجي. ٻي صورت ۾، توهان جي پسند اها آهي ته ڪجھ به شروع نه ٿيندو يا توهان کي ڪنيل خوفناڪ حاصل ٿيندو. دير آهي موت وانگر!
اهو ئي آهي، توهان هڪ لاڳاپيل واپسي سان پيڪيج قبول ڪري سگهو ٿا.
يوزر اسپيس ۾ هڪ پروگرام سڏڻ
هي فنڪشن سڀ کان وڌيڪ سمجھڻ وارو آهي. ان ۾ نالو ڏنو ويو DECLARE_WORK()، قسم ۽ قبول ٿيل دليل دلچسپ نه آهن. اسان لڪير کي حڪم سان وٺي وٺو ۽ ان کي مڪمل طور تي شيل ڏانهن منتقل ڪيو. هن کي پارس ڪرڻ سان معاملو ڪرڻ ڏيو، بائنري ڳولڻ ۽ ٻيو سڀ ڪجهه.
دليلن کي ترتيب ڏيو تارن جي صف ۾ argv[]. مان سمجهان ٿو ته هرڪو ڄاڻي ٿو ته پروگرام اصل ۾ هن طريقي سان عمل ڪيا ويا آهن، ۽ نه ته خالن سان مسلسل قطار جي طور تي.
ماحولياتي متغير مقرر ڪريو. مون صرف PATH داخل ڪيو گهٽ ۾ گهٽ رستن جي سيٽ سان، اميد آهي ته اهي سڀئي اڳ ۾ ئي گڏيل آهن /bin с /usr/bin и /sbin с /usr/sbin. ٻيا رستا گهٽ ۾ گهٽ عملي طور تي اهم آهن.
ٿي ويو، اچو ته اهو ڪريون! ڪرنل فنڪشن call_usermodehelper() داخلا قبول ڪري ٿي. بائنري ڏانهن رستو، دليلن جي صف، ماحول جي متغيرن جي صف. هتي مان اهو به سمجهان ٿو ته هرڪو هڪ الڳ دليل جي طور تي executable فائل ڏانهن رستو پاس ڪرڻ جي معني سمجهي ٿو، پر توهان پڇي سگهو ٿا. آخري دليل بيان ڪري ٿو ته ڇا پروسيس مڪمل ٿيڻ جو انتظار ڪرڻ (UMH_WAIT_PROC)، عمل جي شروعات (UMH_WAIT_EXEC) يا بلڪل انتظار نه ڪريو (UMH_NO_WAIT). ڪجھ وڌيڪ آھي؟ UMH_KILLABLE، مون ان ۾ نه ڏٺو.
اسيمبلي
ڪنيل ماڊلز جي اسيمبلي کي ڪنيل ميڪ فريم ورڪ ذريعي ڪيو ويندو آهي. سڏيو ويو make هڪ خاص ڊاريڪٽري اندر ڪنييل ورزن سان ڳنڍيل آهي (هتي بيان ڪيو ويو آهي: KERNELDIR:=/lib/modules/$(shell uname -r)/build)، ۽ ماڊل جو مقام variable ڏانهن منتقل ڪيو ويو آهي M دليلن ۾. icmpshell.ko ۽ صاف مقصد هي فريم ورڪ مڪمل طور تي استعمال ڪن ٿا. IN 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 ۽ اهو تاريخ تي مشتمل آهي درخواست موڪلي وئي، جنهن جو مطلب آهي ته توهان سڀ ڪجهه صحيح ڪيو ۽ مون سڀ ڪجهه صحيح ڪيو.
ٿڪل
ايٽمي ترقي سان منهنجو پهريون تجربو منهنجي توقع کان گهڻو آسان هو. جيتوڻيڪ سي ۾ ترقي ڪرڻ جي تجربي کان سواءِ، ڪمپيلر اشارن ۽ گوگل جي نتيجن تي توجهه ڏيڻ، مان هڪ ڪم ڪندڙ ماڊل لکڻ جي قابل ٿيس ۽ هڪ ڪرنل هيڪر وانگر محسوس ڪري رهيو هوس، ۽ ساڳئي وقت هڪ اسڪرپٽ ٻار. ان کان علاوه، مان ڪينل نيوبيز چينل ڏانهن ويو، جتي مون کي استعمال ڪرڻ لاء چيو ويو schedule_work() سڏڻ بدران call_usermodehelper() پاڻ کي ٿلهو اندر ۽ کيس شرمسار ڪيو، صحيح طور تي هڪ اسڪيم تي شڪ ڪيو. ڪوڊ جون هڪ سؤ لائينون مون کي منهنجي مفت وقت ۾ ترقي جي هڪ هفتي بابت خرچ ڪري ٿي. هڪ ڪامياب تجربو جيڪو سسٽم جي ترقي جي زبردست پيچيدگي بابت منهنجي ذاتي افسانه کي تباهه ڪري ڇڏيو.
جيڪڏهن ڪو ماڻهو Github تي ڪوڊ جو جائزو وٺڻ تي متفق آهي، مان شڪرگذار ٿيندس. مون کي پڪ آهي ته مون ڪيتريون ئي بيوقوف غلطيون ڪيون آهن، خاص طور تي جڏهن تارن سان ڪم ڪندي.