เทคนิคการใช้ typedef สร้างชนิดข้อมูลแบบ function

4 replies [Last post]
Edkung
Edkung's picture
User offline. Last seen 7 hours 13 min ago. Offline
Joined: 11/29/2009

          typedef เป็นคำสั่งที่ใช้สำหรับ define ชนิดของข้อมูล โดยปกติแล้วจะใช้สร้างชนิดข้อมูลแบบต่างๆ แต่ถ้าเอามานำเสนอมันคงธรรมดาไป วันนี้ผมจะนำเสนอวิธีที่สนุกสนานกว่า เพื่อสร้างชนิดของข้อมูลเป็นแบบ function

          ก่อนอื่นคงต้องแสดงตัวอย่างการใช้ typedef

#include <stdio.h>

typedef char* p_char;

int main(int argc, char **argv)
{
          p_char sz_test = "111";
          printf("%s\n",sz_test);
          return 0;
}

           เห็นได้ว่า p_char จะการเป็น type ของข้อมูลโดยเป็น type แบบ char* สามารถใช้แทน char* ได้ทันที คราวนี้มาดูการใช้ typedef เพื่อสร้าง function type

#include <stdio.h>
#include <windows.h>

typedef char* p_char;

typedef int (__stdcall *MESSAGEBOX) (HWND,LPCSTR,LPCSTR,UINT);

int main(int argc, char **argv)
{
          p_char sz_test = "111";
          printf("%s\n",sz_test);

          MESSAGEBOX API_MSG;
          API_MSG = (MESSAGEBOX)GetProcAddress(LoadLibrary("user32.dll"),"MessageBoxA");

          API_MSG(0,"RCECODE.COM","MessageBoxA by Edkung",MB_OK);
          return 0;
}

          จะได้ว่า MESSAGEBOX เป็น function type ที่เราสร้างขึ้นมาโดย function นี้จะ return value เป็น int และมี parameter 4 ตัวคือ HWND, LPCSTR, LPSTR , UINT ตามลำดับ โดยมีการระบุ Calling Convertion ของ MESSAGEBOX เป็น __stdcall เนื่องจาก compiler จะระบุโดย default เป็น __cdecl แต่ user32.MessageBoxA() เป็น API Function ที่มี Calling Convertion เป็น __stdcall ดังนั้นจึงต้องกำหนดให้เหมือนกันจึงจะทำให้เมื่อ call API_MSG() เรียบร้อยแล้วจะได้มีการเคลียร์ stack ได้อย่างถูกต้อง

          ลองเอาประยุกต์ไปใช้กันดูครับรับรองว่าผู้อ่านจะเขียน code ได้ยืดหยุ่นขึ้นอีกเยอะเลย

nProtect
User offline. Last seen 2 weeks 6 days ago. Offline
Joined: 01/07/2010

ถ้าผมจะเขียนให้ DLL ของผม Call ฟังก์ชั่นถอดรหัสไฟล์ data ของโปรแกรมหนึ่งน่ะครับ (เข้ารหัสด้วย RSA 1024 bits + zlib ขี้เกียจเขียนโปรแกรมถอด ใช้ Inline Decode ดีกว่า)
เท่าที่ทราบ เป็น __stdcall ครับ โดยการ call จาก debugger เป็นแบบนี้
PUSH Param2 << โหมดการอ่านเขียน (ในโปรแกรมใช้ "rb")
PUSH Param1 << ชื่อไฟล์ที่ต้องการถอดรหัส
Call Function (Address 0x0044C68B)

สำหรับการ Return ค่าเนี่ย ผมงงมากเลยครับ ซึ่งตัวฟังก์ชั่นจะ Return ออกมาใน EAX ใช่มั้ยครับ?
สมมุต EAX (หลังจาก call เสร็จ) เป็น 02F68EC8 ซึ่งสิ่งที่ผมต้องการจากฟังก์ชั่นนี้คือ เนื้อหาของไฟล์ที่ถอดรหัสแล้ว
และขนาดของเนื้อหา ซึ่งจะส่งกลับออกมาดังนี้
EAX + 0x8 = Decoded Buffer (02F68EC8 + 0x8 = 02F68ED0)
EAX + 0xC = Decoded Buffer Size (02F68EC8 + 0xC = 02F68ED4)

ไม่ทราบว่าต้องเขียน typedef ยังไงครับ

Edkung
Edkung's picture
User offline. Last seen 7 hours 13 min ago. Offline
Joined: 11/29/2009

ดูแล้วแปลกๆ ครับ ปกติ return value ควรจะเก็บไว้ใน eax ครับ
ส่วนค่าใน eax ก็มักจะเป็น return value นั่นเองเว้นแต่กรณีที่ค่าที่จะ return มีขนาดมากกว่า 32 bit (ใหญ่กว่า eax) eax จะเก็บค่าเป็น pointer of value

กรณีที่ต้องการ output หลายๆ ค่าใน function เดียว แนะนำให้เขียน function แบบ pass by reference ดีกว่า ยกตัวอย่างเช่น

bool testfunction(int param1,int param2,char* out_data,int &out_cbData)
{
    out_data = "abcdefg";
    out_cbData = strlen(out_data);
    return true;
}

เขียน asm ควรจะได้

lea eax,out_cbData
push eax
push out_data
push param2
push param1
call testfunction

ปล. ผมอ่านคำถามยังไม่ค่อยเคลียร์เท่าไร ถ้าสงสัยตรงไหนช่วยถามอีกทีละกัน
 

nProtect
User offline. Last seen 2 weeks 6 days ago. Offline
Joined: 01/07/2010

เท่าที่ดู มัน Return Value เป็น struct น่ะครับ เขียนไว้แบบนี้

struct ReturnStruct
{
    DWORD Reversed1; //ที่ต้องทำเป็น Reversed เพราะส่วนนี้มันคืออะไรผมก็ไม่รู้
    DWORD Reversed2; //ก็เลยต้องเขียนไว้ เพื่อให้มันรับค่ามาได้อย่างถูกต้อง
    char *Buffer;
    DWORD BufferSize;
};

แต่ก็ขอบคุณสำหรับ asm นะครับ

Edkung
Edkung's picture
User offline. Last seen 7 hours 13 min ago. Offline
Joined: 11/29/2009

เมื่อมีการ return เป็น struct นั้นแปลว่าใน asm eax จะเก็บ address of struct หรือ pointer ของ struct เอาไว้ครับ เนื่องจาก struct มีขนาดใหญ่กว่า eax หรือ 32 bit นั่นเอง
ส่วน member ภายใน struct ที่เขียนว่า Reversed นั้น โดยปกติจะหมายถึง จองเอาไว้เฉยๆ ครับ เผื่อกรณีที่อยากจะเพิ่มเติมอะไรลงไป จะได้ไม่ต้องสร้าง struct ขึ้นมาใหม่