ตรวจสอบว่ากระบวนการเฉพาะเป็น 32- หรือ 64- บิต


14

ให้เคอร์เนล 2.6.x หรือใหม่กว่า Linux และ userland ที่มีอยู่ซึ่งสามารถใช้งานทั้ง ELF32 และ ELF64 ไบนารี (เช่นที่ผ่านมาฉันจะรู้ได้อย่างไรว่า CPU ของฉันรองรับระบบปฏิบัติการ 64 บิตภายใต้ Linux? ) ฉันจะทราบได้อย่างไรว่ากระบวนการที่กำหนด โดย PID) กำลังทำงานในโหมด 32- หรือ 64- บิต?

วิธีแก้ปัญหาไร้เดียงสาจะทำงาน:

file -L /proc/pid/exe | grep -o 'ELF ..-bit [LM]SB'

แต่ข้อมูลนั้นถูกเปิดเผยโดยตรง/procโดยไม่ต้องพึ่งพาlibmagicหรือไม่

คำตอบ:


21

หากคุณต้องการ จำกัด การตรวจจับเอลฟ์คุณสามารถอ่านส่วนหัวเอลฟ์ของ/proc/$PID/exeตัวคุณเอง มันค่อนข้างเล็กน้อย: ถ้าไบต์ที่ 5 ในไฟล์คือ 1 มันคือไบนารี 32 บิต ถ้าเป็น 2 ก็คือ 64- บิต สำหรับการเพิ่มสติตรวจ:

  1. ถ้า 5 ไบต์แรกคือ0x7f, "ELF", 1มันเป็นไบนารี ELF 32 บิต
  2. ถ้า 5 ไบต์แรกคือ0x7f, "ELF", 2: มันเป็นไบนารี 64 บิตของ ELF
  3. มิฉะนั้น: มันไม่สามารถสรุปได้

คุณสามารถใช้งานobjdumpได้ แต่นั่นจะทำให้การlibmagicพึ่งพาของคุณหายไปและแทนที่ด้วย alibelfหนึ่ง

อีกวิธีหนึ่ง : คุณสามารถแยกวิเคราะห์/proc/$PID/auxvไฟล์ ตามproc(5):

สิ่งนี้มีเนื้อหาของข้อมูลล่ามของ ELF ที่ส่งไปยังกระบวนการในเวลา exec รูปแบบคือหนึ่ง ID แบบยาวที่ไม่ได้ลงชื่อบวกค่าแบบยาวที่ไม่ได้ลงชื่อหนึ่งรายการสำหรับแต่ละรายการ รายการสุดท้ายมีสองศูนย์

ความหมายของคีย์อยู่ในunsigned long /usr/include/linux/auxvec.hคุณต้องการซึ่งเป็นAT_PLATFORM 0x00000fอย่าอ้างฉันในสิ่งนั้น แต่ดูเหมือนว่าค่าควรจะถูกตีความว่าเป็นchar *เพื่อให้ได้คำอธิบายสตริงของแพลตฟอร์ม

คุณอาจพบคำถาม StackOverflowมีประโยชน์

อีกวิธีหนึ่ง : คุณสามารถสั่งให้ไดนามิกman ldลิงค์เกอร์ ( ) เพื่อถ่ายโอนข้อมูลเกี่ยวกับปฏิบัติการ มันพิมพ์ออกไปยังเอาต์พุตมาตรฐานกับโครงสร้าง AUXV ที่ถอดรหัสแล้ว คำเตือน: นี่เป็นแฮ็ก แต่ก็ใช้ได้

LD_SHOW_AUXV=1 ldd /proc/$SOME_PID/exe | grep AT_PLATFORM | tail -1

สิ่งนี้จะแสดงสิ่งที่ชอบ:

AT_PLATFORM:     x86_64

ฉันลองมันในไบนารี 32 บิตและได้รับ i686แทน

วิธีการทำงาน: LD_SHOW_AUXV=1สั่งให้ Dynamic Linker ทำการดัมพ์โครงสร้าง AUXV ที่ถอดรหัสแล้วก่อนที่จะเรียกใช้ไฟล์ปฏิบัติการ ถ้าคุณจริงๆชอบที่จะทำให้ชีวิตของคุณที่น่าสนใจที่คุณต้องการที่จะหลีกเลี่ยงจริงทำงานกล่าวว่าปฏิบัติการ วิธีหนึ่งในการโหลดและเชื่อมโยงแบบไดนามิกโดยไม่ต้องเรียกใช้main()ฟังก์ชั่นของมันคือทำงานldd(1)บนมัน ข้อเสีย: LD_SHOW_AUXVเปิดใช้งานโดยเชลล์ดังนั้นคุณจะได้รับทิ้งโครงสร้าง AUXV สำหรับ: subshell lddและไบนารีเป้าหมายของคุณ ดังนั้นเราgrepสำหรับ AT_PLATFORM แต่ให้บรรทัดสุดท้ายเท่านั้น

การแยกวิเคราะห์ auxv : หากคุณแยกวิเคราะห์auxvโครงสร้างด้วยตัวคุณเอง (ไม่ต้องพึ่งพาตัวโหลดแบบไดนามิก) จากนั้นจะมีปริศนาเล็กน้อย: auxvโครงสร้างตามกฎของกระบวนการที่อธิบายไว้ดังนั้นsizeof(unsigned long)จะเป็น 4 สำหรับกระบวนการ 32 บิตและ 8 สำหรับ 64 กระบวนการบิต เราสามารถทำสิ่งนี้ให้กับเราได้ เพื่อให้สิ่งนี้ทำงานบนระบบ 32 บิตรหัสคีย์ทั้งหมดจะต้องเป็น0xffffffffหรือน้อยกว่า บนระบบ 64 บิต 32 บิตที่สำคัญที่สุดจะเป็นศูนย์ เครื่องของ Intel นั้นเป็น endian เล็ก ๆ น้อย ๆ ดังนั้น 32 บิตเหล่านี้จึงตามมาน้อยที่สุดในหน่วยความจำ

เช่นนี้สิ่งที่คุณต้องทำคือ:

1. Read 16 bytes from the `auxv` file.
2. Is this the end of the file?
3.     Then it's a 64-bit process.
4.     Done.
5. Is buf[4], buf[5], buf[6] or buf[7] non-zero?
6.     Then it's a 32-bit process.
7.     Done.
8. Go to 1.

การแยกไฟล์แผนที่ : สิ่งนี้ได้รับการแนะนำโดย Gilles แต่ก็ไม่ได้ผล นี่คือเวอร์ชั่นที่แก้ไขแล้ว มันอาศัยการอ่าน/proc/$PID/mapsไฟล์ หากไฟล์แสดงรายการที่อยู่ 64 บิตกระบวนการจะเป็น 64 บิต มิฉะนั้นจะเป็น 32 บิต ปัญหาอยู่ที่เคอร์เนลจะลดความซับซ้อนของการส่งออกโดยการปอกศูนย์นำจากที่อยู่ฐานสิบหกในกลุ่ม 4 ดังนั้นแฮ็คความยาวไม่สามารถทำงานได้ค่อนข้าง awkช่วยเหลือ:

if ! [ -e /proc/$pid/maps ]; then
    echo "No such process"
else
    case $(awk </proc/$pid/maps -- 'END { print substr($1, 0, 9); }') in
    *-) echo "32 bit process";;
    *[0-9A-Fa-f]) echo "64 bit process";;
    *) echo "Insufficient permissions.";;
    esac
 fi

สิ่งนี้ทำงานได้โดยการตรวจสอบที่อยู่เริ่มต้นของแผนที่หน่วยความจำสุดท้ายของกระบวนการ 12345678-deadbeefพวกเขากำลังที่ระบุไว้เช่น ดังนั้นถ้ากระบวนการเป็น 32- บิตที่อยู่นั้นจะเป็นเลขฐานสิบหกแปดหลักยาวและที่เก้าจะเป็นเครื่องหมายยัติภังค์ หากเป็นแบบ 64 บิตที่อยู่สูงสุดจะนานกว่านั้น อักขระที่เก้าจะเป็นเลขฐานสิบหก

ระวัง: ทั้งหมดยกเว้นวิธีแรกและวิธีสุดท้ายต้องการ Linux kernel 2.6.0 หรือใหม่กว่าเนื่องจากauxvไฟล์ไม่เคยมีมาก่อน


1
อืมฉันสงสัยว่าส่วนหัวของ ELF อยู่ใน/proc/[pid]/auxv: "ข้อมูลล่ามของ ELF ถูกส่งไปยังกระบวนการในเวลา exec รูปแบบคือ ID ยาวที่ไม่ได้ลงชื่อและบวกค่ายาวที่ไม่ได้ลงชื่อสำหรับแต่ละรายการ" ( man proc)
goldilocks

1
ส่วนหัวเองไม่ได้ ฉันเพิ่งhdแก้ไขไปหนึ่งตัวและมันก็ขาดเลขอาคม อาจมีข้อมูลที่เกี่ยวข้องบ้าง แต่ฉันคิดว่ามันอาจมีการเปลี่ยนแปลงบ่อยกว่า ELF header นอกจากนั้นยังได้รับการแนะนำใน 2.6.0 /proc/PID/exeดังนั้นจึงไม่ได้ค่อนข้างแพร่หลายเช่นเดียวกับ แต่มันก็ไม่ได้มีข้อมูลสถาปัตยกรรม ฉันจะอัปเดตคำตอบของฉัน
Alexios

auxv กลายเป็นเล่ห์เหลี่ยมมากกว่าที่ฉันคาดไว้ - sizeof(unsigned long)คือ 8 บน 64 บิตหรือ 4 ใน 32 บิตซึ่งหมายความว่าการตีความอย่างถูกต้องโดยตรงคุณต้องทราบว่ากระบวนการเป็น 64 บิตหรือ 32 บิตเพื่อเริ่มต้นด้วย!
เฟล็กโซ

คุณพูดถูก มันค่อนข้างน่ารำคาญ การแก้ปัญหาด่วน: หากไบต์ 16x + y (4≤y≤7) เป็นศูนย์ทั้งหมดในไฟล์คุณกำลังดูไฟล์ปฏิบัติการ 64- ​​บิต นี่คือ kludge: ฉันสมมติว่าเครื่อง endian เล็ก ๆ น้อย ๆ และauxvรหัสคีย์ทั้งหมดพอดีกับ 32 บิตunsigned longดังนั้นที่สำคัญที่สุด 32 บิตในกล่อง 64 บิตจะเป็นศูนย์
Alexios

6

/proc/$pid/mapsดูใน ช่วงที่อยู่นั้นมีที่อยู่มากกว่า 32 บิต (เลขฐานสิบหก 8 หลัก) หรือที่อยู่ 64 บิต (ตัวเลขฐานสิบหก 16 หลัก) สิ่งนี้ใช้ได้กับการปฏิบัติการทุกรูปแบบไม่ว่ารูปแบบใด คุณสามารถรับข้อมูลเกี่ยวกับกระบวนการที่ทำงานในฐานะผู้ใช้คนเดียวกัน (เว้นแต่คุณจะรูท)

if ! [ -e /proc/$pid/maps ]; then
  echo No such process
elif grep -q '^........[^-]' /proc/$pid/maps; then
  echo 64-bit
elif grep -q . /proc/$pid/maps; then
  echo 32-bit
else
  echo Insufficient permissions
fi

หากคุณไม่ได้รับอนุญาตให้เข้าถึงไฟล์นี้ฉันคิดว่าวิธีเดียวที่จะพยายามวิเคราะห์ไฟล์ที่เรียกใช้งานได้คือ (ในขณะที่คุณสามารถอ่านเสมอ/proc/$pid/statไม่มีเขตข้อมูลที่แสดงให้เห็นกระบวนการทำงานเป็นผู้ใช้ที่แตกต่างกันเผยให้เห็นขนาดบิตของกระบวนการ.) คุณสามารถทำให้การคาดเดาที่ดีของกระบวนการของปฏิบัติการด้วยps -o comm=และมองว่าขึ้นมาในPATH- แต่ระวังว่ากระบวนการ อาจจะได้รับการเปิดตัวกับที่แตกต่างกันหรืออาจจะเขียนใหม่ของมันPATH argv[0]จากนั้นคุณสามารถวิเคราะห์ปฏิบัติการ - ถ้าคุณยินดีที่จะถือว่าเป็นเอลฟ์, ดูที่ไบต์ที่ 5


ฉันทดสอบสูตรของคุณแล้วและมันล้มเหลว OpenSuSE 12.2, x86-64, เคอร์เนล 3.4.63-2.44- ค่าเริ่มต้น, / bin / bash บรรทัด / proc / $ pid / maps สำหรับไบนารีและฮีปแรกถูกเขียนในรูปแบบ 32 บิต แต่อื่น ๆ ทั้งหมดอยู่ในรูปแบบ 64 บิต มีแนวโน้มที่พวกเขาจะพิมพ์โดยใช้ "% 08x" แต่อย่างไรก็ตามสูตรนี้จะถูกปรับ
Netch

ฉันได้รับการผสมผสานของค่า 8, 12 และ 16-nybble ในทุกกล่องที่ฉันลองด้วย โดยไม่ต้องตรวจสอบแหล่งที่มาเดาของฉันคือเคอร์เนลปรับ padding เพื่อหลายต่ำสุดของ 16 บิตมากกว่าช่วงที่อยู่สำหรับแต่ละบรรทัดที่พิมพ์ดังนั้นคุณจะต้องค้นหาลำดับที่ยาวที่สุดของตัวอักษรฐานสิบแล้วตรวจสอบ
Alexios

แต่เนื่องจากvsyscallแผนที่อยู่เสมอสูงสุดคุณได้รับไปกับเพียงแค่เปลี่ยนheadไปtail- ซึ่งเศร้าจะไม่ทำงานเพราะ proc ไม่ใช้seek(2)ดังนั้นมันจะต้องเป็นสิ่งที่ไม่สวยงามเท่าเช่นawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios

@ เรียกแน่นอนฉันดูโง่ vsyscall และกองซ้อนและไม่สนใจการแมปของไฟล์ปฏิบัติการ ขอขอบคุณฉันได้รับการอัปเดตเพื่อค้นหาบรรทัดที่ไม่ใช่ 32 บิต น่าเสียดายที่มันน่าเกลียดมาก แต่นี่น่าเชื่อถือที่สุด (อย่างน้อยก็แน่ใจแล้วว่าใช้ x86 ฉันยังไม่ได้ตรวจสอบกับสถาปัตยกรรมคู่อื่น ๆ เช่น sparc และ arm)
Gilles 'หยุดความชั่วร้าย'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.