PVS-Studio analyzer ဖြင့် rdesktop နှင့် xrdp ကို စစ်ဆေးနေသည်

PVS-Studio ခွဲခြမ်းစိတ်ဖြာမှုကို အသုံးပြု၍ rdesktop နှင့် xrdp ကိုစစ်ဆေးခြင်း။
ဤသည်မှာ RDP ပရိုတိုကောနှင့် လုပ်ဆောင်ရန်အတွက် ဖွင့်ထားသော ပရိုဂရမ်များကို စစ်ဆေးခြင်းအကြောင်း ဆောင်းပါးတွဲတစ်ခုတွင် ဒုတိယပြန်လည်သုံးသပ်ခြင်းဖြစ်သည်။ ၎င်းတွင်၊ ကျွန်ုပ်တို့သည် rdesktop client နှင့် xrdp server ကိုစဉ်းစားပါမည်။

အမှားအယွင်းများကို ဖော်ထုတ်ရာတွင် အသုံးပြုသည့် ကိရိယာဖြစ်ခဲ့ပါသည်။ PVS-Studio မှ၎င်းသည် C၊ C++၊ C# နှင့် Java ပလက်ဖောင်းများပေါ်တွင် ရရှိနိုင်သော static code analyzer တစ်ခုဖြစ်သည်။ Windows, Linux и macOS.

ဆောင်းပါးသည် ကျွန်ုပ်အတွက် စိတ်ဝင်စားဖွယ်ကောင်းသော အမှားများကိုသာ တင်ဆက်ပါသည်။ သို့သော် ပရောဂျက်များသည် သေးငယ်သောကြောင့် အမှားအယွင်းအနည်းငယ်ရှိခဲ့သည်။

ပွောဆို. FreeRDP ပရောဂျက်သုံးသပ်ချက်အကြောင်း ယခင်ဆောင်းပါးကို တွေ့နိုင်သည်။ ဒီမှာ.

ဒက်စ်တော့

ဒက်စ်တော့ — свободная реализация клиента RDP для UNIX-based систем. Его также можно использовать и под Windows, если собирать проект под Cygwin. Лицензирован под GPLv3.

ဤကလိုင်းယင့်သည် အလွန်ရေပန်းစားသည် — ၎င်းသည် ReactOS တွင် မူရင်းဖြစ်ပြီး၊ ၎င်းအတွက် ပြင်ပဂရပ်ဖစ်ဆိုင်ရာ ရှေ့စွန်းများကို တွေ့ရှိနိုင်သည်။ သို့သော်၊ ၎င်းသည်အတော်လေးဟောင်းနေပြီဖြစ်သည်- ပထမအကြိမ်ထုတ်ဝေမှုသည်ဧပြီလ 4၊ 2001 - ရေးသားချိန်တွင်၎င်းသည် 17 နှစ်ဖြစ်သည်။

စောစောကပြောခဲ့သလိုပဲ ပရောဂျက်က သေးသေးလေးပါ။ ၎င်းတွင် ကုဒ်လိုင်းပေါင်း 30 ခန့်ပါဝင်ပြီး ၎င်းသည် ၎င်း၏အသက်အရွယ်အရ အနည်းငယ်ထူးဆန်းသည်။ နှိုင်းယှဉ်ရန်အတွက် FreeRDP တွင် လိုင်း ၃၂၀,ဝဝဝ ပါရှိသည်။ ဤသည်မှာ Cloc ပရိုဂရမ်၏ ရလဒ်ဖြစ်သည်။

PVS-Studio ခွဲခြမ်းစိတ်ဖြာမှုကို အသုံးပြု၍ rdesktop နှင့် xrdp ကိုစစ်ဆေးခြင်း။

ဆက်သွယ်၍မရသောကုဒ်

V779 မရရှိနိုင်သောကုဒ်ကို တွေ့ရှိခဲ့သည်။ error ရှိနေတာ ဖြစ်နိုင်တယ်။ rdesktop.c 1502

int
main(int argc, char *argv[])
{
  ....
  return handle_disconnect_reason(deactivated, ext_disc_reason);

  if (g_redirect_username)
    xfree(g_redirect_username);

  xfree(g_username);
}

လုပ်ဆောင်ချက်တွင် အမှားသည် ကျွန်ုပ်တို့နှင့် ကိုက်ညီပါသည်။ အဓိက: အော်ပရေတာရဲ့နောက်မှာ ပါလာတဲ့ ကုဒ်ကို ကျွန်တော်တို့ မြင်ပါတယ်။ ပြန်လာ - ဤအပိုင်းသည် မှတ်ဉာဏ်ရှင်းလင်းခြင်းကို လုပ်ဆောင်သည်။ သို့ရာတွင်၊ အမှားသည် ခြိမ်းခြောက်မှုတစ်ခုမဟုတ်ပေ- ပရိုဂရမ်ကို ရပ်စဲပြီးနောက် ခွဲဝေသတ်မှတ်ထားသော မှတ်ဉာဏ်အားလုံးကို လည်ပတ်မှုစနစ်က ရှင်းလင်းသွားမည်ဖြစ်သည်။

ကိုင်တွယ်ရာတွင် အမှားအယွင်းမရှိပါ။

V557 Array သည် underrun ဖြစ်နိုင်သည်။ 'n' အညွှန်းကိန်းတန်ဖိုးသည် -1 သို့ရောက်ရှိနိုင်သည်။ rdesktop.c ၁၈၇၂

RD_BOOL
subprocess(char *const argv[], str_handle_lines_t linehandler, void *data)
{
  int n = 1;
  char output[256];
  ....
  while (n > 0)
  {
    n = read(fd[0], output, 255);
    output[n] = ' '; // <=
    str_handle_lines(output, &rest, linehandler, data);
  }
  ....
}

ဤကိစ္စတွင် ကုဒ်အပိုင်းအစသည် ဖိုင်ပြီးဆုံးသည်အထိ ကြားခံထဲသို့ ဖိုင်ကိုဖတ်သည်။ သို့သော် ဤနေရာတွင် ကိုင်တွယ်ရာတွင် အမှားအယွင်းမရှိပါ- တစ်ခုခု မှားနေပါက၊ ထို့နောက် ဖတ် -1 ပြန်လာမည်ဖြစ်ပြီး၊ ထို့နောက် array သည် အကန့်အသတ်မရှိ ဖြစ်နေလိမ့်မည်။ output ကို.

char အမျိုးအစားတွင် EOF ကိုအသုံးပြုခြင်း။

V739 EOF ကို 'char' အမျိုးအစား၏ တန်ဖိုးတစ်ခုနှင့် မနှိုင်းယှဉ်သင့်ပါ။ '(c = fgetc(fp))' သည် 'int' အမျိုးအစား ဖြစ်သင့်သည်။ ctrl.c 500


int
ctrl_send_command(const char *cmd, const char *arg)
{
  char result[CTRL_RESULT_SIZE], c, *escaped;
  ....
  while ((c = fgetc(fp)) != EOF && index < CTRL_RESULT_SIZE && c != 'n')
  {
    result[index] = c;
    index++;
  }
  ....
}

ဤနေရာတွင် ဖိုင်၏အဆုံးသို့ရောက်ရှိခြင်း၏ မှားယွင်းစွာကိုင်တွယ်မှုကို ကျွန်ုပ်တို့တွေ့မြင်ရသည်- if fgetc ကုဒ်သည် 0xFF ဟူသော စာလုံးကို ပြန်ပေးသည်၊ ထို့နောက် ၎င်းကို ဖိုင်၏အဆုံးအဖြစ် ရိပ်မိလိမ့်မည် (EOF).

EOF ပုံမှန်အားဖြင့် -1 အဖြစ် သတ်မှတ်သည်။ ဥပမာအားဖြင့်၊ CP1251 ကုဒ်နံပါတ်တွင်၊ ရုရှားအက္ခရာ၏နောက်ဆုံးအက္ခရာတွင် ကုဒ်နံပါတ် -0 နှင့် ကိုက်ညီသော ကုဒ်နံပါတ်-1 ပါရှိသည်။ char. သင်္ကေတ 0xFF ကဲ့သို့ဖြစ်နိုင်သည်။ EOF (-၁) ဖိုင်၏အဆုံးအဖြစ် သဘောထားသည်။ ထိုသို့သောအမှားများကိုရှောင်ရှားရန် function ကို၏ရလဒ် fgetc variable အမျိုးအစားထဲမှာ သိမ်းဆည်းထားသင့်ပါတယ်။ int.

စာလုံးပေါင်း

တစ်ပိုင်းတစ်စ ၁

V547 'write_time' ဆိုသည့် စကားရပ်သည် အမြဲတမ်း လွဲမှားနေပါသည်။ disk.c 805

RD_NTSTATUS
disk_set_information(....)
{
  time_t write_time, change_time, access_time, mod_time;
  ....
  if (write_time || change_time)
    mod_time = MIN(write_time, change_time);
  else
    mod_time = write_time ? write_time : change_time; // <=
  ....
}

ဤကုဒ်ရေးသူသည် မှားသွားနိုင်သည်။ || и && အခြေအနေ၌။ ဖြစ်နိုင်ချေရှိသော တန်ဖိုးအမျိုးမျိုးကို သုံးသပ်ကြည့်ကြပါစို့ ရေးရန်အချိန် и အပြောင်းအလဲ_အချိန်:

  • ကိန်းရှင်နှစ်ခုလုံးသည် 0 ဖြစ်သည်- ဤကိစ္စတွင် ကျွန်ုပ်တို့သည် ဌာနခွဲသို့သွားပါမည်။ အခြားသူ: ပြောင်းလဲနိုင်သည်။ mod_time နောက်ဆက်တွဲအခြေအနေ မည်သို့ပင်ရှိစေကာမူ 0 နှင့် အမြဲတန်းတူညီနေပါမည်။
  • ကိန်းရှင်များထဲမှ တစ်ခုသည် 0 နှင့် ညီသည်- mod_time 0 နှင့် ညီလိမ့်မည် (အခြား variable တွင် အနုတ်လက္ခဏာမဟုတ်သောတန်ဖိုး) ရှိသောကြောင့်ဖြစ်သည်။ MIN ရွေးစရာနှစ်ခုထဲက အသေးကို ရွေးလိမ့်မယ်။
  • ကိန်းရှင်နှစ်ခုလုံးသည် 0 နှင့် မညီပါ- အနိမ့်ဆုံးတန်ဖိုးကို ရွေးပါ။

အခြေအနေနဲ့ အစားထိုးတဲ့အခါ write_time && change_time အပြုအမူသည် မှန်လိမ့်မည်-

  • ကိန်းရှင်တစ်ခု သို့မဟုတ် နှစ်ခုစလုံးသည် 0 နှင့် မညီပါ- သုညမဟုတ်သော တန်ဖိုးကို ရွေးပါ။
  • ကိန်းရှင်နှစ်ခုလုံးသည် 0 နှင့် မညီပါ- အနိမ့်ဆုံးတန်ဖိုးကို ရွေးပါ။

တစ်ပိုင်းတစ်စ ၁

V547 စကားအသုံးအနှုန်းက အမြဲမှန်တယ်။ ဤနေရာတွင် '&&' အော်ပရေတာကို သုံးသင့်သည်။ disk.c ၁၄၁၉

static RD_NTSTATUS
disk_device_control(RD_NTHANDLE handle, uint32 request, STREAM in,
      STREAM out)
{
  ....
  if (((request >> 16) != 20) || ((request >> 16) != 9))
    return RD_STATUS_INVALID_PARAMETER;
  ....
}

အော်ပရေတာများသည် ဤနေရာတွင်လည်း ရောထွေးနေပုံရသည်။ || и &&သို့မဟုတ် == и !=: ကိန်းရှင်တစ်ခုတွင် တန်ဖိုး 20 နှင့် 9 ကို တစ်ချိန်တည်းတွင် မရနိုင်ပါ။

အကန့်အသတ်မရှိ စာကြောင်းကူးခြင်း။

V512 'sprintf' လုပ်ဆောင်ချက်ကို ခေါ်ဆိုခြင်းသည် ကြားခံ 'fullpath' ကို ပြည့်လျှံသွားစေသည်။ disk.c 1257

RD_NTSTATUS
disk_query_directory(....)
{
  ....
  char *dirname, fullpath[PATH_MAX];
  ....
  /* Get information for directory entry */
  sprintf(fullpath, "%s/%s", dirname, pdirent->d_name);
  ....
}

လုပ်ဆောင်ချက်တစ်ခုလုံးကို သင်စစ်ဆေးသောအခါ၊ ဤကုဒ်သည် ပြဿနာမဖြစ်စေကြောင်း ရှင်းပါသည်။ သို့ရာတွင်၊ ၎င်းတို့သည် အနာဂတ်တွင် ဖြစ်ပေါ်လာနိုင်သည်- သတိလက်လွတ် ပြောင်းလဲမှုတစ်ခု၊ ကျွန်ုပ်တို့သည် ကြားခံလွှမ်းမိုးမှုကို ရရှိလိမ့်မည်- sprintf မည်သည့်အရာမှ ကန့်သတ်မထားသောကြောင့် လမ်းကြောင်းများကို ပေါင်းစပ်လိုက်သောအခါတွင် ကျွန်ုပ်တို့သည် array နယ်နိမိတ်များကို ကျော်လွန်သွားနိုင်သည်။ ဤခေါ်ဆိုမှုကို သတိပြုမိရန် အကြံပြုအပ်ပါသည်။ snprintf(fullpath၊ PATH_MAX၊….).

မလိုတော့တဲ့ အခြေအနေ

V560 conditional expression ၏ အစိတ်အပိုင်းတစ်ခုသည် အမြဲမှန်သည်- add > 0. scard.c 507

static void
inRepos(STREAM in, unsigned int read)
{
  SERVER_DWORD add = 4 - read % 4;
  if (add < 4 && add > 0)
  {
    ....
  }
}

ကြည့်ရှုစစ်ဆေးခြင်း ထည့် > 0 ဤနေရာတွင် အမှတ်မရှိပါ- ကိန်းရှင်သည် သုညထက် အမြဲကြီးနေမည်ဖြစ်သောကြောင့်၊ % 4 ကိုဖတ်ပါ။ အပိုင်း၏အကြွင်းကို ပြန်ပေးမည်၊ ၎င်းသည် 4 နှင့် ဘယ်သောအခါမျှ ညီမျှမည်မဟုတ်ပါ။

xrdp

xrdp — open source RDP ဆာဗာကို အကောင်အထည်ဖော်ခြင်း။ ပရောဂျက်ကို အပိုင်း ၂ ပိုင်းခွဲထားသည်။

  • xrdp သည် ပရိုတိုကောကို အကောင်အထည်ဖော်မှုတစ်ခုဖြစ်သည်။ Apache 2.0 လိုင်စင်အောက်တွင် ဖြန့်ဝေထားသည်။
  • xorgxrdp — xrdp ဖြင့် အသုံးပြုရန်အတွက် Xorg ဒရိုက်ဗာများ အစုံ။ လိုင်စင် — X11 (MIT ကဲ့သို့၊ သို့သော် ကြော်ငြာတွင် အသုံးပြုခြင်းကို တားမြစ်ထားသည်)

ပရောဂျက်ဖွံ့ဖြိုးတိုးတက်မှုသည် rdesktop နှင့် FreeRDP ၏ရလဒ်များအပေါ်အခြေခံသည်။ အစပိုင်းတွင်၊ ဂရပ်ဖစ်နှင့်အလုပ်လုပ်ရန်၊ သီးခြား VNC ဆာဗာ သို့မဟုတ် RDP ပံ့ပိုးမှု- X11rdp ပါသော အထူး X11 ဆာဗာကို အသုံးပြုရန် လိုအပ်သော်လည်း xorgxrdp ထွန်းကားလာသည်နှင့်အမျှ ၎င်းတို့အတွက် လိုအပ်ချက်များ ပျောက်ကွယ်သွားခဲ့သည်။

ဤဆောင်းပါး၌ကျွန်ုပ်တို့သည် xorgxrdp ကိုမထိပါ။

xrdp ပရောဂျက်သည် ယခင်ကဲ့သို့ပင် သေးငယ်ပြီး လိုင်းပေါင်း 80 ခန့် ပါဝင်ပါသည်။

PVS-Studio ခွဲခြမ်းစိတ်ဖြာမှုကို အသုံးပြု၍ rdesktop နှင့် xrdp ကိုစစ်ဆေးခြင်း။

အမှားပိုများသည်။

V525 ကုဒ်တွင် အလားတူလုပ်ကွက်များ စုစည်းမှု ပါရှိသည်။ 87၊ 88၊ 89 လိုင်းများတွင် 'r'၊ 'g'၊ 'r' တို့ကို စစ်ဆေးပါ။ rfxencode_rgb_to_yuv.c 87

static int
rfx_encode_format_rgb(const char *rgb_data, int width, int height,
                      int stride_bytes, int pixel_format,
                      uint8 *r_buf, uint8 *g_buf, uint8 *b_buf)
{
  ....
  switch (pixel_format)
  {
    case RFX_FORMAT_BGRA:
      ....
      while (x < 64)
      {
          *lr_buf++ = r;
          *lg_buf++ = g;
          *lb_buf++ = r; // <=
          x++;
      }
      ....
  }
  ....
}

ဤကုဒ်ကို RemoteFX အတွက် jpeg2000 codec ကို အသုံးပြုသည့် librfxcodec စာကြည့်တိုက်မှ ထုတ်ယူထားပါသည်။ ဤတွင်၊ ဂရပ်ဖစ်ဒေတာချန်နယ်များ ရောထွေးနေပုံရသည် - "အပြာရောင်" အရောင်အစား "အနီရောင်" ဟုရေးထားသည်။ ကော်ပီကူးထည့်ခြင်း၏ ရလဒ်အနေဖြင့် ထိုသို့သော အမှားအယွင်းများစွာ ပေါ်လာပါသည်။

အလားတူပြဿနာသည်လည်း အလားတူလုပ်ဆောင်မှုတစ်ခုတွင် ပေါ်လာသည်။ rfx_encode_format_argbခွဲခြမ်းစိတ်ဖြာသူမှလည်း ကျွန်ုပ်တို့အား ပြောပြထားသည်-

V525 ကုဒ်တွင် အလားတူလုပ်ကွက်များ စုစည်းမှု ပါရှိသည်။ 260၊ 261၊ 262၊ 263 လိုင်းများတွင် 'a'၊ 'r', 'g', 'r' တို့ကို စစ်ဆေးပါ။ rfxencode_rgb_to_yuv.c 260

while (x < 64)
{
    *la_buf++ = a;
    *lr_buf++ = r;
    *lg_buf++ = g;
    *lb_buf++ = r;
    x++;
}

Array ကြေငြာချက်

V557 Array overrun ဖြစ်နိုင်သည်။ 'i — 8' အညွှန်းကိန်းတန်ဖိုးသည် 129 သို့ရောက်ရှိနိုင်သည်။ genkeymap.c 142

// evdev-map.c
int xfree86_to_evdev[137-8+1] = {
  ....
};

// genkeymap.c
extern int xfree86_to_evdev[137-8];

int main(int argc, char **argv)
{
  ....
  for (i = 8; i <= 137; i++) /* Keycodes */
  {
    if (is_evdev)
        e.keycode = xfree86_to_evdev[i-8];
    ....
  }
  ....
}

ဤဖိုင်နှစ်ခုရှိ array ၏ ကြေငြာချက်နှင့် အဓိပ္ပါယ်ဖွင့်ဆိုချက်သည် သဟဇာတမဖြစ်ပါ - အရွယ်အစား 1 ဖြင့် ကွဲပြားပါသည်။ သို့သော်၊ အမှားအယွင်းမရှိပါ - မှန်ကန်သောအရွယ်အစားကို evdev-map.c ဖိုင်တွင် သတ်မှတ်ထားသောကြောင့် အကန့်အသတ်မရှိသော အမှားအယွင်းမရှိပါ။ ဒီတော့ ဒါက အလွယ်တကူ ပြင်လို့ရတဲ့ bug တစ်ခုပါပဲ။

မှားယွင်းသော နှိုင်းယှဉ်မှု

V560 အခြေအနေပေးဖော်ပြချက်၏ အစိတ်အပိုင်းတစ်ခုသည် အမြဲတမ်း မှားယွင်းနေသည်- (cap_len < 0)။ xrdp_caps.c ၆၁၆

// common/parse.h
#if defined(B_ENDIAN) || defined(NEED_ALIGN)
#define in_uint16_le(s, v) do 
....
#else
#define in_uint16_le(s, v) do 
{ 
    (v) = *((unsigned short*)((s)->p)); 
    (s)->p += 2; 
} while (0)
#endif

int
xrdp_caps_process_confirm_active(struct xrdp_rdp *self, struct stream *s)
{
  int cap_len;
  ....
  in_uint16_le(s, cap_len);
  ....
  if ((cap_len < 0) || (cap_len > 1024 * 1024))
  {
    ....
  }
  ....
}

လုပ်ဆောင်ချက်သည် အမျိုးအစား၏ ကွဲပြားမှုကို ဖတ်သည်။ လက်မှတ်မထိုးထားသော အတို အမျိုးအစား၏ပြောင်းလဲမှုတစ်ခုသို့ int. ကျွန်ုပ်တို့သည် လက်မှတ်မထိုးထားသော ကိန်းရှင်ကို ဖတ်ရှုပြီး ရလဒ်ကို ပိုမိုကြီးမားသော ကိန်းရှင်သို့ သတ်မှတ်ပေးသောကြောင့် ဤနေရာတွင် စစ်ဆေးရန်မလိုအပ်ပါ။

မလိုအပ်သောစစ်ဆေးမှုများ

V560 အခြေအနေဆိုင်ရာဖော်ပြချက်၏ အစိတ်အပိုင်းတစ်ခုသည် အမြဲတမ်းအမှန်ဖြစ်သည်- (bpp != 16)။ libxrdp.c 704

int EXPORT_CC
libxrdp_send_pointer(struct xrdp_session *session, int cache_idx,
                     char *data, char *mask, int x, int y, int bpp)
{
  ....
  if ((bpp == 15) && (bpp != 16) && (bpp != 24) && (bpp != 32))
  {
      g_writeln("libxrdp_send_pointer: error");
      return 1;
  }
  ....
}

ကျွန်ုပ်တို့တွင် အစပိုင်းတွင် နှိုင်းယှဉ်မှုတစ်ခုရှိနေပြီဖြစ်သောကြောင့် မညီမျှမှုစစ်ဆေးမှုများသည် ဤနေရာတွင် အဓိပ္ပာယ်မရှိပေ။ ဒါဟာ typo တစ်ခုဖြစ်ပြီး developer က အော်ပရေတာကို အသုံးပြုဖို့ ရည်ရွယ်ထားတာ ဖြစ်နိုင်တယ်။ || မမှန်ကန်သော အကြောင်းပြချက်များကို စစ်ထုတ်ရန်။

ကောက်ချက်

စစ်ဆေးစဉ်အတွင်း ကြီးကြီးမားမား အမှားအယွင်းများ မတွေ့ခဲ့ရသော်လည်း ချို့ယွင်းချက်များစွာ တွေ့ရှိခဲ့သည်။ သို့သော်လည်း ဤပရောဂျက်များကို အရွယ်အစားသေးငယ်သော်လည်း စနစ်များစွာတွင် အသုံးပြုပါသည်။ ပရောဂျက်ငယ်တစ်ခုတွင် အမှားအယွင်းများစွာရှိရန်မလိုအပ်ပါ၊ ထို့ကြောင့် ခွဲခြမ်းစိတ်ဖြာသူ၏စွမ်းဆောင်ရည်ကို ပရောဂျက်ငယ်များတွင်သာ အကဲဖြတ်ခြင်းမပြုသင့်ပါ။ ဒီအကြောင်းကို ဆောင်းပါးမှာ ဆက်လက်ဖတ်ရှုနိုင်ပါတယ်"ခံစားချက်များကို ကိန်းဂဏန်းများဖြင့် အတည်ပြုခဲ့သည်။"။

PVS-Studio ၏ အစမ်းဗားရှင်းကို ကျွန်ုပ်တို့ထံမှ ဒေါင်းလုဒ်လုပ်နိုင်ပါသည်။ က်ဘ်ဆိုက်.

PVS-Studio ခွဲခြမ်းစိတ်ဖြာမှုကို အသုံးပြု၍ rdesktop နှင့် xrdp ကိုစစ်ဆေးခြင်း။

ဤဆောင်းပါးကို အင်္ဂလိပ်စကားပြောပရိသတ်နှင့် မျှဝေလိုပါက၊ ဘာသာပြန်လင့်ခ်- Sergey Larin ကို အသုံးပြုပါ။ PVS-Studio ဖြင့် rdesktop နှင့် xrdp ကို စစ်ဆေးနေပါသည်။

source: www.habr.com

DDoS ကာကွယ်ရေး၊ VPS VDS ဆာဗာများပါသည့် ဆိုက်များအတွက် ယုံကြည်စိတ်ချရသော hosting ကို ဝယ်ယူပါ။ 🔥 DDoS ကာကွယ်မှု၊ VPS VDS ဆာဗာများပါရှိသော ယုံကြည်စိတ်ချရသော ဝဘ်ဆိုက် hosting ကို ဝယ်ယူပါ | ProHoster