การป้อนข้อมูลผู้ใช้ระบายสีทำได้ยากเพราะในครึ่งกรณีจะมีเอาต์พุตจากไดรเวอร์เทอร์มินัล (พร้อมเสียงสะท้อนในเครื่อง) ดังนั้นในกรณีนั้นไม่มีแอปพลิเคชันที่ทำงานอยู่ในเทอร์มินัลนั้นอาจทราบเมื่อผู้ใช้พิมพ์ข้อความและเปลี่ยนสีเอาต์พุต . เฉพาะไดรเวอร์เทอร์มินัลหลอก (ในเคอร์เนล) เท่านั้นที่รู้ (เทอร์มินัลอีมูเลเตอร์ (เช่น xterm) ส่งอักขระบางตัวเมื่อกดปุ่มและไดรเวอร์เทอร์มินัลอาจส่งอักขระบางตัวสำหรับ echo แต่ xterm ไม่สามารถรู้ได้ว่า echo โลคัลหรือจากแอ็พพลิเคชันที่ส่งออกไปยังฝั่งทาสของเทอร์มินัลหลอก)
และจากนั้นก็มีโหมดอื่น ๆ ที่ไดรเวอร์ของเทอร์มินัลบอกว่าไม่ให้สะท้อนเสียง แต่แอพพลิเคชั่นในครั้งนี้ให้ผลลัพธ์บางอย่าง แอปพลิเคชัน (เช่นผู้ที่ใช้ readline เช่น gdb, bash ... ) อาจส่งไปที่ stdout หรือ stderr ซึ่งจะยากที่จะแยกความแตกต่างจากสิ่งที่มันเอาท์พุทเพื่อสิ่งอื่นที่ไม่ใช่การสะท้อนกลับอินพุตของผู้ใช้
จากนั้นเพื่อแยกความแตกต่าง stdout ของแอปพลิเคชันจาก stderr ของมันมีหลายวิธี
หลายคนเกี่ยวข้องกับการเปลี่ยนเส้นทางคำสั่ง stdout และ stderr ไปป์และไปป์เหล่านั้นอ่านโดยแอปพลิเคชันเพื่อสี มีสองปัญหาคือ:
- เมื่อ stdout ไม่ใช่เทอร์มินัลอีกต่อไป (เช่นไปป์แทน) แอปพลิเคชันจำนวนมากมักจะปรับพฤติกรรมของพวกเขาเพื่อเริ่มบัฟเฟอร์เอาต์พุตของพวกเขาซึ่งหมายความว่าเอาต์พุตจะถูกแสดงในกลุ่มก้อนขนาดใหญ่
- แม้ว่าจะเป็นกระบวนการเดียวกับที่ประมวลผลสองไพพ์ แต่ก็ไม่มีการรับประกันว่าลำดับที่ข้อความที่เขียนโดยแอปพลิเคชันบน stdout และ stderr จะถูกเก็บรักษาไว้เนื่องจากกระบวนการอ่านไม่สามารถรู้ได้ (ถ้ามีบางอย่างที่ต้องอ่านจากทั้งคู่) ไม่ว่าจะเริ่มอ่านจากไปป์ "stdout" หรือไปป์ "stderr"
อีกวิธีหนึ่งคือการปรับเปลี่ยนแอปพลิเคชันเพื่อให้สีของ stdout และ stdin มันมักจะเป็นไปไม่ได้หรือเป็นจริงที่จะทำ
จากนั้นเคล็ดลับ (สำหรับแอปพลิเคชันที่เชื่อมโยงแบบไดนามิก) สามารถขโมยได้ (ใช้$LD_PRELOAD
ในคำตอบของ sickill ) ฟังก์ชั่นการส่งออกที่เรียกใช้โดยแอปพลิเคชันเพื่อส่งออกบางสิ่งบางอย่างและใส่รหัสในรหัสที่กำหนดสีพื้นหน้า บน stderr หรือ stdout อย่างไรก็ตามนั่นหมายถึงการหักหลังทุกฟังก์ชั่นที่เป็นไปได้จาก C library และwrite(2)
ไลบรารี่อื่น ๆ ที่เรียก syscall โดยตรงโดยแอพพลิเคชั่นที่อาจจะเขียนอะไรลงบน stdout หรือ stderr (printf, put, perror ... ) และแม้แต่ตอนนั้น ที่อาจปรับเปลี่ยนพฤติกรรมของมัน
อีกวิธีหนึ่งที่อาจจะใช้เทคนิค ptrace เป็นstrace
หรือgdb
ทำเพื่อตัวเราเองขอเวลาทุกwrite(2)
สายระบบที่เรียกว่าและการตั้งค่าสีที่ส่งออกขึ้นอยู่กับว่าwrite(2)
อยู่ในไฟล์อธิบาย 1 หรือ 2
อย่างไรก็ตามนั่นเป็นเรื่องใหญ่ที่ต้องทำ
เคล็ดลับที่ฉันเพิ่งเล่นด้วยคือการจี้strace
ตัวเอง (ซึ่งเป็นการทำงานที่สกปรกของ hooking ตัวเองก่อนการเรียกระบบทุกครั้ง) โดยใช้ LD_PRELOAD เพื่อบอกให้เปลี่ยนสีเอาต์พุตตามว่ามันตรวจพบ a write(2)
fd 1 หรือ 2
จากการดูstrace
ซอร์สโค้ดเราจะเห็นว่าผลลัพธ์ทั้งหมดนั้นทำผ่านvfprintf
ฟังก์ชั่น สิ่งที่เราต้องทำคือการขโมยฟังก์ชั่นนั้น
เสื้อคลุม LD_PRELOAD จะมีลักษณะดังนี้:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
int vfprintf(FILE *outf, const char *fmt, va_list ap)
{
static int (*orig_vfprintf) (FILE*, const char *, va_list) = 0;
static int c = 0;
va_list ap_orig;
va_copy(ap_orig, ap);
if (!orig_vfprintf) {
orig_vfprintf = (int (*) (FILE*, const char *, va_list))
dlsym (RTLD_NEXT, "vfprintf");
}
if (strcmp(fmt, "%ld, ") == 0) {
int fd = va_arg(ap, long);
switch (fd) {
case 2:
write(2, "\e[31m", 5);
c = 1;
break;
case 1:
write(2, "\e[32m", 5);
c = 1;
break;
}
} else if (strcmp(fmt, ") ") == 0) {
if (c) write(2, "\e[m", 3);
c = 0;
}
return orig_vfprintf(outf, fmt, ap_orig);
}
จากนั้นเรารวบรวมด้วย:
cc -Wall -fpic -shared -o wrap.so wrap.c -ldl
และใช้เป็น:
LD_PRELOAD=/path/to/wrap.so strace -qfo /dev/null -e write -s 0 env -u LD_PRELOAD some-cmd
คุณจะสังเกตว่าถ้าคุณแทนที่some-cmd
ด้วยbash
bash prompt และสิ่งที่คุณพิมพ์จะปรากฏเป็นสีแดง (stderr) ในขณะที่zsh
มันปรากฏเป็นสีดำ (เพราะ zsh dups stderr ไปยัง fd ใหม่เพื่อแสดงพร้อมท์และ echo)
ดูเหมือนว่าจะใช้งานได้ดีอย่างน่าประหลาดใจแม้กับแอปพลิเคชันที่คุณคาดหวังไม่ได้ (เช่นเดียวกับที่ใช้สี)
โหมดการระบายสีจะถูกแสดงผลบนstrace
stderr ของซึ่งจะถือว่าเป็นขั้ว หากแอปพลิเคชั่นเปลี่ยนเส้นทาง stdout หรือ stderr ของเราที่ถูกแย่งชิง strace ของเราจะทำการเขียนลำดับการหลีกเลี่ยงสีบนเทอร์มินัล
โซลูชันนั้นมีข้อ จำกัด :
- สิ่งเหล่านี้มีอยู่ใน
strace
: ปัญหาด้านประสิทธิภาพคุณไม่สามารถเรียกใช้คำสั่ง PTRACE อื่น ๆ เช่นstrace
หรือgdb
ในนั้นหรือปัญหา setuid / setgid
- มันเป็นสีตาม
write
s บน stdout / stderr ของแต่ละกระบวนการ ดังนั้นสำหรับตัวอย่างเช่นในsh -c 'echo error >&2'
, error
จะเป็นสีเขียวเพราะecho
ผลบนของ stdout (ซึ่งดวลจุดโทษเปลี่ยนเส้นทางไปยัง stderr ดวลจุดโทษ แต่ strace ทั้งหมดเห็นเป็นwrite(1, "error\n", 6)
) และในsh -c 'seq 1000000 | wc'
นั้นseq
ทำอะไรมากมายหรือwrite
s ไปยังstdout ของมันดังนั้นเสื้อคลุมจะจบลงด้วยการส่งลำดับ escape (ที่มองไม่เห็น) จำนวนมากไปยังเทอร์มินัล