2012-05-05 62 views
7

Tôi nghĩ câu hỏi của tôi có vẻ hơi lạ, nhưng ở đây nó đi; Tôi đang cố gắng để tạo ra một chương trình tự động trong C + + (chủ yếu là cho niềm vui của nó, nhưng cũng vì một lý do có lập trình) và nó không phải là khó khăn như nó có thể âm thanh. Để thực hiện việc này, bạn phải sử dụng assembly trong thời gian chạy như sau:Một mẫu hợp lệ trong assembly cho đối số variadic

byte * buffer = new byte[5]; 
*buffer = '0xE9'; // Code for 'jmp' 
*(uint*)(buffer + 1) = 'address destination'; // Address to jump to 

Điều này dễ hơn rất nhiều, vì tôi chỉ nhắm vào một nền tảng và trình biên dịch; GCC với Linux 32 bit (và cũng chỉ có một quy ước gọi điện, cdecl). Vì vậy, tôi đang cố gắng để tạo ra một chức năng lắp ráp năng động để chuyển hướng cuộc gọi từ gây nên, vì vậy tôi có thể sử dụng phương pháp lớp như callbacks (ngay cả với thư viện API C (với cdecl của khóa học)). Tôi chỉ cần điều này để hỗ trợ con trỏ và các kiểu bản địa (char, int, short etc ...).

ANYTHING MyRedirect(ANY AMOUNT ARGUMENTS) 
{ 
    return MyClassFunc('this', ANY AMOUNT ARGUMENTS); 
} 

Chức năng ở trên, là chức năng tôi muốn tạo trong hội đồng tinh khiết (trong bộ nhớ với C++). Vì hàm này rất đơn giản, ASM của nó cũng đơn giản (tùy thuộc vào các đối số).

55      push %ebp 
89 e5     mov %esp,%ebp 
83 ec 04    sub $0x4,%esp 
8b 45 08    mov 0x8(%ebp),%eax 
89 04 24    mov %eax,(%esp) 
e8 00 00 00 00   call <address> 
c9      leave 
c3      ret 

Vì vậy, trong chương trình của tôi, tôi đã tạo trình tạo mẫu ASM (vì tôi không biết ASM đặc biệt tốt, tôi tìm kiếm mẫu). Hàm này có thể tạo mã assembly (tính theo byte, cho trường hợp chính xác ở trên, tức là một hàm chuyển hướng và trả về) bằng cách xác định số lượng đối số mà hàm cần. Đây là một đoạn trích từ mã C++ của tôi.

std::vector<byte> detourFunc(10 + stackSize, 0x90); // Base is 10 bytes + argument size 

// This becomes 'push %ebp; move %esp, %ebp' 
detourFunc.push_back(0x55);  // push %ebp 
detourFunc.push_back(0x89);  // mov 
detourFunc.push_back(0xE5);  // %esp, %ebp 

// Check for arguments 
if(stackSize != 0) 
{ 
    detourFunc.push_back(0x83);  // sub 
    detourFunc.push_back(0xEC);  // %esp 
    detourFunc.push_back(stackSize); // stack size required 

    // If there are arguments, we want to push them 
    // in the opposite direction (cdecl convention) 
    for(int i = (argumentCount - 1); i >= 0; i--) 
    { 
     // This is what I'm trying to implement 
     // ... 
    } 

    // Check if we need to add 'this' 
    if(m_callbackClassPtr) 
    { 

    } 
} 

// This is our call operator 
detourFunc.push_back(0xE8);  // call 

// All nop, this will be replaced by an address 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 

if(stackSize == 0) 
{ 
    // In case of no arguments, just 'pop' 
    detourFunc.push_back(0x5D); // pop %ebp 
} 

else 
{ 
    // Use 'leave' if we have arguments 
    detourFunc.push_back(0xC9); // leave  
} 

// Return function 
detourFunc.push_back(0xC3);  // ret 

Nếu tôi chỉ định không như các stackSize đây sẽ là kết quả:

55      push %ebp 
89 e5     mov %esp,%ebp 
e8 90 90 90 90   call <address> 
5d      pop %ebp 
c3      ret 

Như bạn có thể thấy, đây là hoàn toàn hợp lệ 32-bit ASM, và sẽ đóng vai trò là 'MyRedirect' nếu nó không có đối số và không cần một con trỏ 'this'. Vấn đề là, tôi muốn thực hiện phần mà nó tạo ra mã ASM, tùy thuộc vào số lượng đối số tôi chỉ định rằng chức năng 'chuyển hướng' sẽ nhận được. Tôi đã thực hiện thành công điều này trong chương trình C++ nhỏ của tôi (đã phá vỡ khuôn mẫu).

#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char * argv[]) 
{ 
    int val = atoi(argv[1]); 

    printf("\tpush %%ebp\n"); 
    printf("\tmov %%esp,%%ebp\n"); 

    if(val == 0) 
    { 
     printf("\tcall <address>\n"); 
     printf("\tpop %%ebp\n"); 
    } 

    else 
    { 
     printf("\tsub $0x%x,%%esp\n", val * sizeof(int)); 

     for(int i = val; i > 0; i--) 
     { 
      printf("\tmov 0x%x(%%ebp),%%eax\n", i * sizeof(int) + sizeof(int)); 
      printf("\tmov %%eax,0x%x(%%esp)\n", i * sizeof(int) - sizeof(int)); 
     } 

     printf("\tcall <address>\n"); 
     printf("\tleave\n"); 
    } 

    printf("\tret\n"); 
    return 0; 
} 

Chức năng này in chính xác cùng mẫu với mã ASM được tạo bởi 'objdump'. Vì vậy, câu hỏi của tôi là; điều này có hợp lệ trong mọi trường hợp nếu tôi chỉ chỉ muốn có chức năng chuyển hướng như chức năng ở trên, bất kể đối số, nếu nó chỉ nằm dưới Linux 32bit, hoặc có bất kỳ cạm bẫy nào tôi cần biết không? Ví dụ; ASM được tạo ra có khác biệt với 'quần short' hoặc 'ký tự' hay công việc này (tôi chỉ thử nghiệm với số nguyên), và nếu tôi gọi hàm trả về 'void' (điều đó ảnh hưởng đến ASM) thì sao?

tôi có thể giải thích tất cả mọi thứ một chút mờ, vì vậy hãy hỏi thay vì bất kỳ sự hiểu lầm :)

LƯU Ý: Tôi không muốn biết lựa chọn thay thế, tôi thưởng thức thực hiện hiện tại của tôi và nghĩ rằng đó là một cái rất thú vị, tôi sẽ đánh giá cao sự giúp đỡ của bạn về chủ đề này.

EDIT: Trong trường hợp quan tâm, đây là một số bãi cho C++ trên mã: link

+6

Một sự thay thế rất tốt để mã hóa hướng dẫn bằng tay là thư viện [asmjit] (http://code.google.com/p/asmjit/) (trái với tên, đây không phải là trình biên dịch JIT, chỉ là một cái gì đó Trình biên dịch JIT có thể sử dụng). –

+0

@afishwhoswimsaround Thư viện đó trông thật tuyệt vời. Theo nghĩa đen. Điều đó có vẻ là _exactly_ những gì tôi cần. Câu hỏi nhanh; Tôi có thể tạo ra một chức năng tự động với thư viện này (với malloc hoặc một cái gì đó) vì vậy tôi có thể 'chuyển hướng' (với lắp ráp 'jmp') các chức năng khác ('móc' chúng) cho các chức năng được tạo ra? –

+1

Có, hãy xem các ví dụ http://code.google.com/p/asmjit/wiki/Examples –

Trả lời

1

Như Dan gợi ý, bạn cần phải đánh dấu các bộ nhớ như thực thi. I wrote some code you can use. (Nó hoạt động trên GNU/Linux và Windows.) Nếu bạn định không bao giờ hỗ trợ ARM, x86-64 hoặc các nền tảng khác, thì tôi không thấy bất kỳ sự cố nào đối với mã của bạn (với phần thực thi được thêm vào) và có vẻ như nó nên "luôn hoạt động". (Giả sử mọi thứ khác đang hoạt động đúng cách.)

#include <sys/mman.h> 

... 

n = <size of code buffer>; 
p = mmap(0, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); 

'cá' khuyên bạn nên sử dụng asmjit. Tôi phải đồng ý với điều đó; nó di động hơn phương pháp của bạn. Tuy nhiên, bạn nói rằng bạn không quan tâm đến các lựa chọn thay thế.

Bạn có thể quan tâm đến điều gì đó có tên "Thunking" (loại). Về cơ bản, nó cố gắng thực hiện "thay thế một cuộc gọi lại bằng C++." Điều này thực sự khá hữu ích, nhưng không thực sự là một thiết kế tốt cho các ứng dụng của bạn.

Hy vọng điều đó sẽ hữu ích.

Các vấn đề liên quan