0x04- Stack Based Buffer Overflow

0x03 မွာ Memory Layout အေၾကာင္းကို ေလ့လာျပီးေနာက္ Functions ေတြနဲ႕ Stack ရဲ႕ အလုပ္လုပ္ပံုကို က်ေနာ္တို႕ေလလာၾကမွာျဖစ္ပါတယ္။ ထို႕ေနာက္ Buffer ကိုလည္း တစ္ခါတည္းေလ့လာသြားမွာျဖစ္ပါတယ္။ ေနာက္တစ္ခန္းအေနနဲ႕ ေရးရေလာက္ေအာင္မမ်ားတဲ့အတြက္ တစ္ခါတည္းေလ့လာသြားျခင္းျဖစ္ပါတယ္။

Buffer

Buffer ကို အရင္ေလ့လာပါမယ္။ ဒီ Buffer အေၾကာင္းကို Shellcoder’s Handbook Second Edition မွ ေကာက္နွုတ္ထားျခင္းျဖစ္ပါတယ္။

Buffer သည္ ကန္႕သတ္ထားေသာ Memory ကိုဆိုလိုျခင္းျဖစ္သည္။ C Program တြင္ပါဝင္ေသာ Array သည္ buffer မ်ားပင္ျဖစ္သည္။ ဥပမာဆိုရေသာ္ array[5] ဟုဆိုလွ်င္ array သည္ 5 ခန္းပါဝင္သည္။ ၅ ခန္းဟုသတ္မွတ္ထားေသာေၾကာင့္ ၄င္းသည္ buffer ျဖစ္သည္။ Shellcoder’s Handbook အဆိုအရ က်ေနာ္တို႕သည္ သတ္မွတ္ထားသည္႕ အရာမ်ားကို ေဖာက္ဖ်က္ၾကည့္ဖို႕တကယ္လိုအပ္ပါသည္။ ဆိုလိုသည္မွာ ၄င္း အခန္း၅ ခန္းရွိေသာ array တြင္ က်ေနာ္တို႕ ျပန္လည္ေခၚသံုးမည္ဆိုလွ်င္ 0 မွာ 4 ထိ သာ ေခၚသံုးလို႕ရမည္ျဖစ္ေသာ္လည္း ၆ ခန္းေျမာက္ value ကို လွမ္းေခၚသည့္အခါ မည္ကဲ့သုိ႕ျဖစ္မည္နည္း ?

#include <stdio.h>
#include <string.h>

int main()
{
int array[5]={1,2,3,4,5};
printf("%d\n",array[0]);
printf("%d\n",array[1]);
printf("%d\n",array[2]);
printf("%d\n",array[3]);
printf("%d\n",array[4]); // end of array
printf("%d\n",array[5]);

}

ဒါေလးကို run ၾကည့္မယ္။

C program မွာ built-in protection မရွိတာေၾကာင့္ buffer ရဲ႕ အဆံုးမွာ value တစ္ခုရေနတာကိုေတြ႕ရပါလိမ့္မယ္။ ဥပမာတြင္ buffer ၏ အဆံုးကို က်ေနာ္တို႕ ဖတ္လို႕ရေနတာကိုေတြ႕ရမွာျဖစ္ပါတယ္။

The Stack 

ဒီလိုဆိုရင္ Stack အေၾကာင္းကို ေသခ်ာေလ့လာၾကည့္ၾကတာေပါ့ ။ အေရွ႕ခန္းေတြမွာလဲ ေဖာ္ျပခဲ့ပါတယ္။ က်ေနာ္တုိ႕ အခန္း  ၁ တုန္းကေျပာခဲ့တာတစ္ခုရွိတယ္ ။ stack ထဲကို data သြားထားမယ္ဆိုရင္ push instruction  ကိုသံုးတယ္လို႕ဆိုပါတယ္။ ေနာက္တစ္ခုက Stack သည္ Last In First Out (LIFO) Data Structure ျဖစ္တယ္လို႕လဲဆိုခဲ့တယ္။ ဥပမာအေနနဲ႕ value ၂ ခုကို stack သို႕ push လုပ္မယ္ဆိုပါစို႕

push value 1
push value 2

value 1 ကို အရင္ဆံုး သြားထားမယ္ ျပီးရင္ေတာ့ value 2 ေပါ့။ ဒါဆိုရင္ stack မွာ ဘယ္လိုျဖစ္မလဲ ? Stack Pointer ကေရာ

-Lower Address
value 2       - > ESP point to this address
value 1
-Higer Address

Lower Address ကို တျဖည္းျဖည္းခ်င္းဆင္းသြားမွာျဖစ္ပါတယ္။ ပံုမွာတက္သြားတယ္လို႕ ထင္ေနမယ္ေနာ္ ။ အခန္း ၃ ကိုျပန္ဖတ္ေပးပါ။ ဘာေၾကာင့္ ဒီလိုေဖာ္ျပထားတယ္ဆိုတာပါပါတယ္။

Stack ကေနျပန္ထုတ္မယ္ဆိုရင္ေတာ့ pop instruction ကို သံုးေပးရပါတယ္။

pop eax
pop ebx

ပထမဆံုး stack ရဲ႕ ထိပ္ဆံုးမွာရွိေနတဲ့ value ကို အရင္ဆံုးထုတ္တယ္ ။ esp က eax ကို ညႊန္ထားတဲ့အတြက္ eax ထဲကို value 2 ကဝင္သြားမွာျဖစ္ပါတယ္။ ဘာေၾကာင့္ value2 လဲဆိုရင္ LIFO လို႕ေျပာခဲ့ပါတယ္။ ေနာက္တစ္ခုကိုေတာ့ ebx ထဲသို႕ pop လုပ္မယ္လို႕ဆိုပါတယ္။ ဒီလို value ေတြကို ျပန္ယူတဲ့အခါမွာ stack က ေျပာင္းလဲသြားျခင္းမရွိပါဘူး။ ေျပာင္းလဲသြားတာက esp pointer ပဲျဖစ္ပါတယ္။

-Lower Address
value 2       
value 1
-Higer Address - > ESP point to this address

Functions and the Stack

အေရွ႕ခန္းမွာတုန္းက Stack ထဲမွာ Functions ေတြနဲ႕ local variables ေတြ ကို သိမ္းတဲ့ပံုစံကို ေျပာထားျပီးျဖစ္ေပမဲ့ တစ္ခုထက္ပိုတဲ့ Function ေတြ ကိုဘယ္လိုအလုပ္လုပ္တယ္ဆိုတာမေျပာရေသးပါဘူး။ ဒီအခန္းမွာေရးဖို႕တမင္ခ်န္ထားခဲ့ျခင္းျဖစ္ပါတယ္။ ဒီလိုဆို တစ္ခုထက္ပိုတဲ့ function ေတြပါတဲ့ program တစ္ခု ဥပမာအေနနဲ႕လိုမွာျဖစ္ပါတယ္။

void function(int a,int b)
{
int array[5];
}

int main() 
{
function(1,2);
printf("Return Address Point");
}

၄င္း program ကို Shellcoder’s Handbook မွ ေကာက္နွုတ္ထားျခင္းျဖစ္ပါသည္။ Program သည္ RET ကိုေကာင္းေကာင္းနားလည္ေစရန္ရည္ရြယ္ထားပါသည္။ RET ဆိုတာကေတာ့ EIP value ပါဝင္တဲ့ Next Instruction Pointer လို႕ အေရွ႕ခန္းမွာထဲကေျပာခဲ့ပါတယ္။ ခုေတာ့ နားလည္ေအာင္လုပ္ၾကတာေပါ့။ Progam ကို run တဲ့အခ်ိန္မွာ ပထမဆံုး အလုပ္လုပ္မွာက Main Function ျဖစ္တယ္ ။ ဒီလိုဆို stack မွာ main function ကို သြားထားလိုက္ျပီေပါ့။ function တစ္ခုစတိုင္း ဘယ္လိုအလုပ္လုပ္တယ္ဆိုတာ အခန္း ၃ တြင္ေဖာ္ျပျပီးျဖစ္ပါသည္။ ထို႕ေနာက္ function ဟု အမည္ေပးထားတဲ့ function ကို လွမ္းေခၚမွာျဖစ္ပါတယ္။ ဒီေနရာမွာတစ္ခုသိထားရမွာက function ကို အရင္မေခၚခင္ argument ကို stack ဆီအရင္ပို႕တယ္ဆိုတာပါပဲ။ ဘယ္လိသိနိုင္မလဲ ။ main function ကို disassemble လုပ္ၾကည့္ရင္သိနိုင္ပါတယ္။

gdb -q function

(gdb) disas main
Dump of assembler code for function main:
   0x08048425 <+0>:	push   %ebp
   0x08048426 <+1>:	mov    %esp,%ebp
   0x08048428 <+3>:	and    $0xfffffff0,%esp
   0x0804842b <+6>:	sub    $0x10,%esp  // main function စစခ်င္း stack မွာ ေနရာယူသြားျခင္းျဖစ္ပါတယ္။
   0x0804842e <+9>:	movl   $0x2,0x4(%esp) // ဒုတိယ arg ကို esp-4 မွာ ထားတယ္
   0x08048436 <+17>:	movl   $0x1,(%esp)  // ပထမ arg ကို esp မွာ ထားတယ္
   0x0804843d <+24>:	call   0x804841d <function> // ထို႕ေနာက္ function ကို လွမ္းေခၚတယ္ 
   0x08048442 <+29>:	movl   $0x80484e0,(%esp) 
   0x08048449 <+36>:	call   0x80482f0 <printf@plt> // function ဆီ ကေနျပန္လာရင္ print ထုတ္မယ္
   0x0804844e <+41>:	leave  
   0x0804844f <+42>:	ret    
End of assembler dump.

တစ္ဆင့္ခ်င္းရွင္းထားတယ္ေနာ္။ ဒီလိုဆို function ဆီကို argument နဲ႕အတူ သြားေတာ့မွာေပါ့။ function function ကို disassemble လုပ္ၾကည့္မယ္

(gdb) disas function
Dump of assembler code for function function:
   0x0804841d <+0>:	push   %ebp
   0x0804841e <+1>:	mov    %esp,%ebp
   0x08048420 <+3>:	sub    $0x20,%esp
   0x08048423 <+6>:	leave  
   0x08048424 <+7>:	ret    
End of assembler dump.

function မွာကက်ေနာ္တို႕ ဘာမွလုပ္မထားခဲ့ပါဘူး။ array[5] လို႕ပဲေၾကညာခဲ့တာျဖစ္ပါတယ္။ ဒါေၾကာင့္ stack မွာ ေနရာယူတာေလးပဲကြာမယ္။ array 5 ခုစာပါေအာင္ sub $0x20,%esp ဆိုျပီး ယူသြားတာကိုေတာ့ သတိထားမိပါမယ္။ array 5 ခုစာ , 5*4 bytes ယူရမွာျဖစ္ေပမဲ့ 0x20 ဆိုေတာ့ program က 30 bytes စာ ယူသြားပါတယ္။

ဒီေနရာမွာ function ရဲ႕ instruction ေတြအလုပ္လုပ္ျပီးျပီဆိုလွ်င္ main မွာ က်န္ေနေသးတဲ့ print function ဆီကို ျပန္သြားဖို႕က်န္ေနေသးတယ္ဆိုတာ သိထားပါတယ္။ ဒီေတာ့ function အလုပ္လုပ္ျပီးရင္ သြားမယ့္ next instruction pointer ဟာ 0x08048442 ျဖစ္မယ္လို႕ က်ေနာ္တုိ႕ ခန္႕မွန္းလို႕ရပါတယ္။ ဟုတ္မဟုတ္ gdb နဲ႕ break point ေလးေတြထားျပီး ၾကည့္ၾကည့္မယ္။

function မွာ ပဲ break point ထားလိုက္မယ္။ main က function ကိုသြားမယ္ဆိုတာ က်ေနာ္တို႕သိျပီးျဖစ္ပါတယ္။

(gdb) break *function   // function မွာ breakpoint ထားလိုက္တယ္
Breakpoint 1 at 0x804841d
(gdb) r      // run လိုက္ေတာ့ရပ္သြားမယ္ 
Starting program: /home/lunam00n/Desktop/function 

Breakpoint 1, 0x0804841d in function ()
(gdb) i r // ရပ္ေနတုန္း registers ေတြကိုၾကည့္ၾကည့္မယ္
eax            0x1	1
ecx            0x5d5dd49d	1566430365
edx            0xffffcfe4	-12316
ebx            0xf7fbc000	-134496256
esp            0xffffcf9c	0xffffcf9c
ebp            0xffffcfb8	0xffffcfb8
esi            0x0	0
edi            0x0	0
eip            0x804841d	0x804841d <function>
eflags         0x286	[ PF SF IF ]
cs             0x23	35
ss             0x2b	43
ds             0x2b	43
es             0x2b	43
fs             0x0	0
gs             0x63	99

ဘာမွမထူးျခားေသးပါဘူး function ရဲ႕ instruction ေတြကိုတစ္ဆင့္ခ်င္းလုပ္သြားေစခ်င္ေတာ့ ni နဲ႕ တစ္လိုင္းခ်င္းစီ ဆင္းလိုက္မယ္။ ဘယ္ထိလဲဆိုရင္ RET ကိုေရာက္တဲ့ထိ က်ေနာ္တို႕ run ေပးလိုက္မယ္။

(gdb) ni
0x08048442 in main ()
(gdb) i r
eax            0x1	1
ecx            0x5d5dd49d	1566430365
edx            0xffffcfe4	-12316
ebx            0xf7fbc000	-134496256
esp            0xffffcfa0	0xffffcfa0
ebp            0xffffcfb8	0xffffcfb8
esi            0x0	0
edi            0x0	0
eip            0x8048442	0x8048442 <main+29>
eflags         0x286	[ PF SF IF ]
cs             0x23	35
ss             0x2b	43
ds             0x2b	43
es             0x2b	43
fs             0x0	0
gs             0x63	99

EIP မွာ main ရဲ႕  က်န္ေနတဲ့ဆီကို သြားဖို႕ Memory Address ကိုေတြ႕ရမွာျဖစ္ပါတယ္။ သြားခိုင္းလိုက္ရင္ေတာ့ သြားျပီေပါ့။ ဒီေနရာမွာ က်ေနာ္တို႕ သိထားရမွာေလးတစ္ခုရွိလာပါတယ္။ RET ကို ဘယ္လိုပါလာတာလဲေပါ့။

Function တစ္ခုလွမ္းေခၚတယ္ဆိုရင္ Stack မွာျဖစ္ေနတဲ့ ပံုစံကိုေအာက္မွာၾကည့္ပါ။

-Lower Address
[Array]
EBP
RET
-Higher Address

အခုအဆင့္မ်ားသည္က်ေနာ္တို႕ function ေတြ stack ေတြ RET ေတြ ဘယ္လိုအလုပ္လုပ္တယ္ဆိုတာကိုသိေအာင္ေလ့လာျခင္းသာျဖစ္ပါတယ္။ User Input သာမရွိရင္ေတာ့ Buffer Overflow လဲမျဖစ္ပါဘူး။ ဒါဆိုရင္ ဘယ္လိုမ်ိဳးမွ Buffer Overflow ျဖစ္တာလဲ?

Buffer Overflow Vulnerability

Buffer တစ္ခုေတာ့လိုမယ္ ေနာက္ေတာ့ User Input တစ္ခုလဲလိုပါဦးမယ္။ ေနာက္တစ္ခုက အဲဒီ User Input ကို buffer ထဲသို႕ copy လုပ္တဲ့ function တစ္ခုလဲလိုပါတယ္။ အဲ့ခ်ိန္က်မွာ User က Buffer Overflow လုပ္တယ္ဆိုတာျဖစ္လာမွာပါ။ ဒီေတာ့ Shellcoder Handbook က example ေလးနဲ႕ပဲစမ္းမယ္။ ဒီေနရာမွာ Shellcoder Handbook က ဟာေတြခ်ည္းပဲလားလို႕ေမးစရာရွိပါတယ္။ ဟုတ္တယ္ ဒီအခန္းကို က်ေနာ္ဖတ္တဲ့ထဲမွာ အဲ့ကဟာကိုအၾကိုက္ဆံုးပါပဲ။ က်ေနာ္တို႕ကို နားလည္ေစတယ္လို႕ယူဆလို႕ျဖစ္ပါတယ္။

void return_input(void)
{
char array[30];
gets(array);
printf("%s\n",array);
}

int main()
{
return_input();
return 0;
}

porgram သည္ user ရိုက္ထဲ႕လိုက္တဲ့ character ေတြကို buffer ထဲကိုဒီတိုင္းထဲ့လိုက္တာေတြ႕ရပါမယ္။

compile လုပ္ျပီး run ၾကည့္မယ္။ overflow လို႕နာမည္ေပးလိုက္မယ္။

lunam00n@lunam00n-lol:~/Desktop$ ./overflow 
AAAAAAAAAA
AAAAAAAAAA

A ၁၀ လံုးရိုက္ထည့္လိုက္တယ္ အဲ့ array ကို ျပန္ျပီးျပတယ္ array က 30 ထားခဲ့တာ ၁၀ လံုးထည့္ေတာ့ ဘာမွမျဖစ္ဘူး အဆင္ေျပတယ္။ Characters ၄၀ ရိုက္ၾကည့္ပါ။

lunam00n@lunam00n-lol:~/Desktop$ ./overflow 
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
* stack smashing detected *: ./overflow terminated
Aborted (core dumped)

Stack Smashing detected တဲ့ ။ ခုကေလ့လာမွာဆိုေတာ့ Detector ကို ပိတ္ေပးလိုက္မယ္  မဟုတ္ရင္ ဘယ္လိုလုပ္သိတာ့မလဲ။

gcc -m32 =g -fno-stack-protector -z execstack -o overflow overflow.c

ျပန္ run မယ္

lunam00n@lunam00n-lol:~/Desktop$ ./overflow 
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
Segmentation fault (core dumped)

ဒီတစ္ခါေတာ့ Segmentation Fault error တက္ပါျပီ။

ထံုးစံအတိုင္းပဲ gdb နဲ႕ Debuggin လုပ္ျပီး နားလည္ေအာင္လုပ္ရမွာေပါ့။ ဒါေၾကာင့္ main function နဲ႕ return_input ၂ ခုလံုးကို အရင္ဆံုး disassemble လုပ္လိုက္မယ္။

(gdb) disas main
Dump of assembler code for function main:
   0x0804846b <+0>:	push   %ebp
   0x0804846c <+1>:	mov    %esp,%ebp
   0x0804846e <+3>:	and    $0xfffffff0,%esp
   0x08048471 <+6>:	call   0x804844d <return_input>   // return_input ကို လွမ္းေခၚတဲ့ instruction
   0x08048476 <+11>:	mov    $0x0,%eax // return 0 ေၾကာင့္ eax ကို 0 ထားမဲ့ instruction
   0x0804847b <+16>:	leave  
   0x0804847c <+17>:	ret    
End of assembler dump.
(gdb) disas return_input
Dump of assembler code for function return_input:
   0x0804844d <+0>:	push   %ebp
   0x0804844e <+1>:	mov    %esp,%ebp
   0x08048450 <+3>:	sub    $0x38,%esp
   0x08048453 <+6>:	lea    -0x26(%ebp),%eax  // Load Effective Address
   0x08048456 <+9>:	mov    %eax,(%esp)
   0x08048459 <+12>:	call   0x8048310 <gets@plt> // Call gets function
   0x0804845e <+17>:	lea    -0x26(%ebp),%eax
   0x08048461 <+20>:	mov    %eax,(%esp)
   0x08048464 <+23>:	call   0x8048320 <puts@plt> // Call Print Function
   0x08048469 <+28>:	leave  
   0x0804846a <+29>:	ret    
End of assembler dump.

မ run ၾကည့္ခင္မွာ က်ေနာ္တို႕ ထံုးစံအတိုင္း break point ေတြထားမယ္။ Break point ၂ ခုထားရပါမယ္။ အေၾကာင္းမွာ User Input မထည့္မီအခ်ိန္နွင့္ ထည့္ျပီးအခ်ိန္ေတြမွာ stack ကို ေသခ်ာၾကည့္နိုင္ေအာင္ျဖစ္ပါတယ္။ ဒါေၾကာင့္ gets ကို ေခၚတဲ့ေနရာရယ္ ret ရယ္မွာ breakpoint ထားလိုက္မယ္။

break *return_input+12
break *return_input+29

ဒီလို break point ၂ ခုထားျပီးျပီဆိုရင္ေတာ့ run ၾကည့္မယ္။ ျပီးရင္ေတာ့ stack ရဲ႕ top မွာရွိတဲ့ esp ကေနစျပီးေတာ့ ေနာက္ထပ္ အခု ၂ဝ စာၾကည့္လိုက္မယ္။ RET ကိုသိခ်င္တာျဖစ္ပါတယ္။ ESP -> EBP -> RET အဲ့လို အစဥ္လိုက္၇ွိတယ္လို႕ ဆိုတာကိုး

(gdb) run
Starting program: /home/lunam00n/Desktop/overflow 

Breakpoint 1, 0x08048459 in return_input () at overflow.c:7
7	gets(array);
(gdb) x/20x $esp
0xffffcf70:	0xffffcf82	0x09ca212c	0x00000001	0x080482d9
0xffffcf80:	0xffffd239	0x0000002f	0x0804a000	0x080484d2
0xffffcf90:	0x00000001	0xffffd054	0xffffd05c	0xf7e424ad
0xffffcfa0:	0xf7fbc3c4	0xf7ffd000	0xffffcfb8(EBP)	0x08048476 (RET)
0xffffcfb0:	0x08048480	0x00000000	0x00000000	0xf7e28af3

return_input ျပီးသြားရင္ main ကို ျပန္လာျပီး eax ထဲကို 0 ထားမယ့္ Instruction ရွိေသးတယ္လို႕ က်ေနာ္ေဖာ္ျခဲ့ပါတယ္။ 0x08048476 ဒီေနရာျဖစ္တယ္လို႕ဆိုပါတယ္။ ဒါေၾကာင့္ ဒီ Address ဟာ EIP ျဖစ္ျပီးေတာ့ သူ႕အေရွ႕က value က EBP လို႕သိနိုင္ပါတယ္။ အေပၚပံုေဘးေလးမွာေဖာ္ျပထားပါတယ္။

ဒီလိုဆို Program ကို ဆက္ run ၾကတာေပါ့ gets ေတာင္းမယ္ ျပီးရင္ RET မွာ ထပ္ျပီး break ထားတယ္။

(gdb) c
Continuing.
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEE
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEE

Breakpoint 2, 0x0804846a in return_input () at overflow.c:9
9	}

RET ကိုမေရာက္ေသးဘူ Program က ထြက္သြားရင္ registers ေတြကိုၾကည့္လို႕မရေတာ့မွာမို႕ ဒီမွာ ထပ္ Break ထားျခင္းျဖစ္ပါတယ္။

Stack ကုိျပန္ၾကည့္ၾကတာေပါ့။

(gdb) x/20x 0xffffcf70
0xffffcf70:	0xffffcf82	0x09ca212c	0x00000001	0x080482d9
0xffffcf80:	0x4141d239	0x41414141	0x41414141	0x42424242
0xffffcf90:	0x42424242	0x43434242	0x43434343	0x43434343
0xffffcfa0:	0x44444444	0x44444444	0x45454444(EBP)	0x45454545 (EIP)
0xffffcfb0:	0x45454545	0x00000000	0x00000000	0xf7e28af3

Array ထဲကေနတျဖည္းျဖည္း ဆင္းလာလိုက္တာေနာက္ဆံုး EBP ကိုလဲ overwirte တယ္ ျပီးေတာ့ E ေတြရယ္ Hex value ျဖစ္တဲ့ 45 ေတြက EIP value ကို Overwrite လုပ္သြားတာေတြ႕ရပါလိမ့္မယ္။ ဒီလိုဆို program က ဘယ္ကိုဆက္သြားမလဲ?

(gdb) ni
0x45454545 in ?? ()
Program received signal SIGSEGV, Segmentation fault.
0x45454545 in ?? ()

ဒီ Error ဘယ္အခ်ိန္မွာျပသလဲဆိုရင္ ခြင့္မျပဳထားတဲ့ memory address ကို သြားမယ္ဆိုတဲ့အခ်ိန္မွာ ၄င္း error ကိုျပတယ္လို႕ဆိုပါတယ္။ (ref) အကယ္လို႕ က်ေနာ္တို႕က Valid ျဖစ္တဲ့ Address ကို control လုပ္နိုင္မယ္ဆိုလွ်င္ေရာ ?

ေနာက္အခန္းမွာ Controlling EIP ဆိုတဲ့ ေခါင္းစဥ္နဲ႕ေရးသြားပါမယ္။