วิธีดูแอสเซมบลีที่อยู่เบื้องหลังโค้ดโดยใช้ Visual C ++


117

ฉันกำลังอ่านคำถามอื่นเกี่ยวกับประสิทธิภาพของโค้ดสองบรรทัดและ OP บอกว่าเขาดูการประกอบด้านหลังโค้ดและทั้งสองบรรทัดก็เหมือนกันในการประกอบ นอกเหนือจากการย่อยแล้วฉันจะดูรหัสแอสเซมบลีที่สร้างขึ้นเมื่อคอมไพล์โปรแกรมได้อย่างไร

ฉันใช้ Visual C ++ ของ Microsoft แต่ฉันต้องการทราบด้วยว่าเป็นไปได้หรือไม่ที่จะดูแอสเซมบลีที่อยู่เบื้องหลังโค้ดที่เขียนด้วย Visual Basic

ดังนั้นฉันจะดูรหัสแอสเซมบลีที่อยู่เบื้องหลังโปรแกรมที่เขียนด้วยภาษาระดับสูงกว่าเช่น C ++ และ Visual Basic ได้อย่างไร


เรียกว่ารายการประกอบใน msvc ตามที่คนอื่นพูดถึงไปแล้ว ฉันสร้างปลั๊กอินง่ายๆที่เพิ่มรายการลงในเมนูบริบทของตัวแก้ไขเพื่อทำให้ขั้นตอนที่น่าเบื่อเหล่านั้นเป็นไปโดยอัตโนมัติ: marketplace.visualstudio.com/items?itemName=Trass3r.DevUtils
Trass3r

คำตอบ:


149

มีหลายวิธี:

  1. โดยปกติคุณสามารถดูรหัสแอสเซมบลีขณะที่ดีบัก C ++ ในวิชวลสตูดิโอ (และคราสด้วย) สำหรับสิ่งนี้ใน Visual Studio ให้ใส่เบรกพอยต์ในโค้ดที่เป็นปัญหาและเมื่อดีบักเกอร์ถึงขีดสุดให้คลิกและค้นหา "ไปที่แอสเซมบลี" (หรือกด CTRL + ALT + D)

  2. แนวทางที่สองคือการสร้างรายชื่อแอสเซมบลีในขณะที่รวบรวม สำหรับสิ่งนี้ไปที่การตั้งค่าโปรเจ็กต์ -> C / C ++ -> ไฟล์เอาต์พุต -> ตำแหน่งรายการ ASM และกรอกชื่อไฟล์ เลือก "Assembly Output" เป็น "Assembly With Source Code" ด้วย

  3. คอมไพล์โปรแกรมและใช้ดีบักเกอร์ของ บริษัท อื่น คุณสามารถใช้ OllyDbg หรือ WinDbg สำหรับสิ่งนี้ นอกจากนี้คุณสามารถใช้ IDA (ตัวแยกชิ้นส่วนแบบโต้ตอบ) แต่นี่เป็นวิธีที่ไม่ยอมใครง่ายๆในการทำ


5
โปรดทราบว่าแนวทาง # 2 ใช้ไม่ได้เมื่อคอมไพล์ไลบรารีแบบสแตติกที่เปิดใช้งานการปรับให้เหมาะสมทั้งโปรแกรม (ใน VS2010 เป็นอย่างน้อย) ซึ่งสมเหตุสมผล - คอมไพเลอร์ยังไม่ได้สร้างโค้ดสุดท้าย
dhaffey

3
เรียกว่า "Goto Disassembly" ใน Visual Studio 2017
Matthias

ด้วยแนวทาง # 2 ฉันจะดูชุดประกอบได้อย่างไร
user1507435

ควรเห็นไฟล์. asm ใน dir ดีบักถ้าคุณใช้ตำแหน่งเริ่มต้น
user3015682

28

หมายเหตุเพิ่มเติม: มีความแตกต่างอย่างมากระหว่างเอาต์พุตแอสเซมเบลอร์ Debug และ Release one อันแรกเป็นการดีที่จะเรียนรู้ว่าคอมไพเลอร์สร้างโค้ดแอสเซมเบลอร์จาก C ++ ได้อย่างไร สิ่งที่สองเป็นสิ่งที่ดีในการเรียนรู้ว่าคอมไพเลอร์ปรับแต่งโครงสร้าง C ++ ต่างๆอย่างไร ในกรณีนี้การแปลง C ++ - to-asm บางอย่างจะไม่ชัดเจน


ฉันสังเกตเห็นว่าเมื่อถอดแยกส่วนปฏิบัติการแก้จุดบกพร่องดูเหมือนว่าจะคลายรหัสในขณะที่กำลังทำงานอยู่สิ่งนี้จะไม่เกิดขึ้นในเวอร์ชันรีลีส นอกจากนี้เมื่อเปิดทั้งสองอย่างด้วย PEiD เพียงแค่เวอร์ชัน Debug จะแสดง "Microsoft Visual C ++ 8.0 [Debug]"
jyz

9
นี่เป็นเรื่องจริงอย่างแน่นอน แต่มันไม่ตอบคำถามเลย.
imallett

25

ระบุสวิตช์ / FA สำหรับ cl compiler ขึ้นอยู่กับค่าของสวิตช์มีเพียงรหัสแอสเซมบลีหรือโค้ดระดับสูงและโค้ดแอสเซมบลีเท่านั้น ชื่อไฟล์ได้รับนามสกุลไฟล์. asm ค่าที่รองรับมีดังนี้


  • / รหัส FA Assembly; .asm
  • / FAc เครื่องและรหัสการประกอบ; .cod
  • / FAs แหล่งที่มาและรหัสการประกอบ; .asm
  • / FAcs Machine แหล่งที่มาและรหัสการประกอบ; .cod

10

วิธีที่ง่ายที่สุดคือการยิงดีบักและตรวจสอบหน้าต่างถอดชิ้นส่วน


8

คำตอบรุ่นก่อนหน้านี้ ("แฮ็ก" สำหรับ rextester.com) ส่วนใหญ่ซ้ำซ้อนในขณะนี้ที่http://gcc.godbolt.org/มีCL 19 RCสำหรับ ARM, x86 และ x86-64 (กำหนดเป้าหมายตามรูปแบบการโทรของ Windows ซึ่งแตกต่างจาก gcc, clang และ icc ในไซต์นั้น)

Godbolt compiler explorer ได้รับการออกแบบมาสำหรับการจัดรูปแบบเอาต์พุต asm ของคอมไพเลอร์อย่างสวยงามโดยลบ "สัญญาณรบกวน" ของคำสั่งดังนั้นฉันขอแนะนำให้ใช้มันเพื่อดู asm สำหรับฟังก์ชันง่ายๆที่รับ args และคืนค่า (ดังนั้นจะไม่เป็น ปรับให้เหมาะสมที่สุด)

ในระยะหนึ่ง CL มีอยู่ในhttp://gcc.beta.godbolt.org/แต่ไม่ใช่ไซต์หลัก แต่ตอนนี้มีอยู่ในทั้งสองไซต์


ในการรับเอาต์พุต MSVC asm จากhttp://rextester.com/l/cpp_online_compiler_visualคอมไพลเลอร์ออนไลน์: เพิ่ม/FAsในตัวเลือกบรรทัดคำสั่ง ให้โปรแกรมของคุณค้นหาเส้นทางของตัวเองและหาเส้นทางไปยัง.asmและถ่ายโอนข้อมูล หรือเรียกใช้ disassembler บน.exe.

เช่นhttp://rextester.com/OKI40941

#include <string>
#include <boost/filesystem.hpp>
#include <Windows.h>

using namespace std;

static string my_exe(void){
    char buf[MAX_PATH];
    DWORD tmp = GetModuleFileNameA( NULL, // self
                                  buf, MAX_PATH);
    return buf;
}

int main() {
    string dircmd = "dir ";
    boost::filesystem::path p( my_exe() );
    //boost::filesystem::path dir = p.parent_path();

    // transform c:\foo\bar\1234\a.exe 
    // into      c:\foo\bar\1234\1234.asm
    p.remove_filename();
    system ( (dircmd + p.string()).c_str() );

    auto subdir = p.end();      // pointing at one-past the end
    subdir--;                   // pointing at the last directory name
    p /= *subdir;               // append the last dir name as a filename
    p.replace_extension(".asm");
    system ( (string("type ") + p.string()).c_str() );
//    std::cout << "Hello, world!\n";
}

... code of functions you want to see the asm for goes here ...

typeเป็นเวอร์ชัน DOS ของcat. ฉันไม่ต้องการรวมโค้ดเพิ่มเติมที่จะทำให้ค้นหาฟังก์ชันที่ฉันต้องการดู asm ได้ยากขึ้น (แม้ว่าจะใช้ std :: string และ boost run สวนทางกับเป้าหมายเหล่านั้น! การปรับแต่งสตริงรูปแบบ C บางอย่างที่ทำให้เกิดสมมติฐานเพิ่มเติมเกี่ยวกับสตริงที่กำลังประมวลผล (และละเว้นความปลอดภัย / การจัดสรรที่มีความยาวสูงสุดโดยใช้บัฟเฟอร์ขนาดใหญ่) จากผลของGetModuleFileNameAwould น้อยกว่ารหัสเครื่องทั้งหมด)

IDK ทำไม แต่cout << p.string() << endlแสดงเฉพาะชื่อฐาน (เช่นชื่อไฟล์โดยไม่มีไดเร็กทอรี) แม้ว่าการพิมพ์ความยาวจะแสดงว่าไม่ใช่แค่ชื่อเปล่า (Chromium48 บน Ubuntu 15.10) อาจมีการประมวลผลแบ็กสแลชหลบหนีในบางจุดcoutหรือระหว่าง stdout ของโปรแกรมกับเว็บเบราว์เซอร์


@MichaelPetch: โอ้ปรากฎว่าเป็นสิ่งที่ฉันได้ลอง .c_str()พิมพ์สิ่งที่ดูเหมือนตัวชี้ หากคุณไปที่ลิงก์คุณจะเห็นรหัสเพื่อ hexdump a std::string(ปิดใช้งานด้วย#if 0) ปรากฎว่าสตริงใช้ได้ แต่coutไม่ได้เข้าเว็บเบราว์เซอร์ ไม่มีอักขระใด ๆ ที่ไม่ใช่ ascii มีเพียงแบ็กสแลช
Peter Cordes

ฉันอาจจะขาดอะไรไป แต่เมื่อคุณsubdir--; p /= *subdir;ไม่ได้ลดpเป็นแค่ชื่อไฟล์? หรือบางทีฉันอาจเข้าใจผิดว่าคุณพยายามจะพิมพ์อะไร
Michael Petch

ผมคิดว่าผมค่อนข้างไม่เข้าใจsubdir--ตามด้วยp /= *subdirเมื่อsubdirเดิมp.end()
ไมเคิลเพชร

@MichaelPetch: ความคิดเห็นที่อัปเดต ฉันต้องการรับส่วนประกอบไดเร็กทอรีสุดท้ายของพา ธ เพื่อใช้เป็นชื่อไฟล์ มันไม่ทำงาน แต่เอาฉันเป็นเวลานานที่จะทำงานออกเพราะผมคิดว่าเป็นเพียงการกลับมาGetModuleFileNameA a.exeมันไม่ได้จนกว่าฉันจะหกเหลี่ยมมันและพิมพ์ความยาวที่ฉันรู้ว่ามันใช้งานได้และฉันสามารถให้โปรแกรมจัดการเส้นทางได้ฉันก็พิมพ์เส้นทางไม่ได้
Peter Cordes

1
ใช่ดูเหมือนว่าจะเป็นส่วน\\r( \rเมื่อคอมไพเลอร์ส่งออก) ของชื่อไฟล์ที่แปลได้ไม่ดีเมื่อแสดงผลสำหรับเว็บเบราว์เซอร์ ใช้p.generic_string()งานได้ แต่แบ็กสแลชเป็นเครื่องหมายทับ
Michael Petch

5

ใน Visual C ++ ตัวเลือกโครงการภายใต้ไฟล์เอาต์พุตฉันเชื่อว่ามีตัวเลือกสำหรับการส่งออกรายการ ASM ด้วยซอร์สโค้ด ดังนั้นคุณจะเห็นซอร์สโค้ด C / C ++ และ ASM ที่เป็นผลลัพธ์ทั้งหมดในไฟล์เดียวกัน



1

.NET Reflector ของ Red Gateเป็นเครื่องมือที่ยอดเยี่ยมมากที่ช่วยฉันได้มากกว่าสองสามครั้ง ข้อดีของยูทิลิตี้นี้นอกเหนือจากการแสดง MSIL ให้คุณอย่างง่ายดายคือคุณสามารถวิเคราะห์ DLL ของบุคคลที่สามจำนวนมากและให้ตัวสะท้อนแสงดูแลแปลง MSIL เป็น C # และ VB

ฉันไม่ได้สัญญาว่ารหัสจะชัดเจนเท่ากับแหล่งที่มา แต่คุณไม่ควรมีปัญหาในการติดตามมากนัก


2
หมายเหตุ: ใช้ได้เฉพาะกับแอสเซมบลีที่ได้รับการจัดการเท่านั้นที่จะไม่ถอดชิ้นส่วนในแอสเซมเบลอร์ asm
ฌอนจ

จุดดีฉันอ่านว่า "มีโค้ดสองบรรทัดเหมือนกันในแอสเซมบลี" แทนที่จะเป็น "โค้ดสองบรรทัดเหมือนกันในแอสเซมบลี"
Dave L

จะใช้ได้เฉพาะกับแอพ dotnet ไม่ใช่ Visual C ++ linker หรือคอมไพเลอร์
มูฮัมหมัดอาลี

1

หากคุณกำลังพูดถึงการดีบักเพื่อดูรหัสประกอบวิธีที่ง่ายที่สุดคือ Debug-> Windows-> Disassembly (หรือ Alt-8) สิ่งนี้จะช่วยให้คุณก้าวเข้าสู่ฟังก์ชันที่เรียกว่าและอยู่ใน Disassembly

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.