#ifndef SERVER_AGENTSERVER_TIMECHECK_TICKSPEEDCHECKER_H_ #define SERVER_AGENTSERVER_TIMECHECK_TICKSPEEDCHECKER_H_ #pragma once #ifdef _NA_20100802_SPEED_HACK_DETECTION #include #include // ½ºÇǵåÇÙ °Ë»ç Ŭ·¡½º class TickSpeedChecker { public: TickSpeedChecker(); ~TickSpeedChecker(); // ÁÖ¾îÁø User °´Ã¼ ÂüÁ¶¿Í ÇÔ²² ÀÌ TickSpeedChecker °´Ã¼¸¦ ÃʱâÈ­ÇÑ´Ù. void Init(User* user); // ¿¬°üµÈ »ç¿ëÀÚ °èÁ¤ÀÇ °Ë»ç Á¦¿Ü/Áֽà ¼³Á¤À» °»½ÅÇÑ´Ù. void SetUserID(); // °Ë»ç°¡ °¡´ÉÇÑ »óÅÂÀÎÁöÀÇ ¿©ºÎ¸¦ ¼³Á¤ÇÑ´Ù. // SecurePackNode4User::EnableHeartbeats() ÇÔ¼ö¿¡¼­ È£ÃâµÈ´Ù. void SetEnabled(bool flag) { enabled_ = flag; } // °Ë»ç°¡ °¡´ÉÇÑ »óÅÂÀÎÁöÀÇ ¿©ºÎ¸¦ ¸®ÅÏÇÑ´Ù. bool IsEnabled() const { return (!excepted_ && enabled_); } // ¶ó¿îµå Ȱ¼ºÈ­ ¿©ºÎ¸¦ °Ë»çÇϰí Ȱ¼ºÈ­µÇ¾î ÀÖÁö ¾ÊÀ¸¸é Á¶°Ç¿¡ µû¶ó ¶ó¿îµå¸¦ Ȱ¼ºÈ­ÇÑ´Ù. bool TryActivate(DWORD current_tick); // ½ÇÇà ·çÇÁ¿¡¼­ ÁÖ±âÀûÀ¸·Î È£ÃâµÇ´Â ÀÛ¾÷ ·çƾ. void Update(); // Ŭ¶óÀÌ¾ðÆ® ÀÀ´ä ¼ö½Å½Ã È£ÃâµÇ´Â ÀÛ¾÷ ·çƾ. void HandleClientResponse(DWORD round, BYTE index, DWORD client_tick, DWORD server_tick2); // ¼³Á¤ ÆÄÀϷκÎÅÍ Àü¿ª ¼³Á¤À» ÀÐ¾î µéÀδÙ. static void LoadSettings(const TCHAR* filename) { GetSettings().Load(filename); } // Àü¿ªÀûÀ¸·Î °Ë»ç°¡ Ȱ¼ºÈ­µÇ¾î ÀÖ´ÂÁöÀÇ ¿©ºÎ¸¦ ¸®ÅÏÇÑ´Ù. static bool IsGloballyEnabled() { return GetSettings().IsEnabled(); } // Àü¿ë ·Î±× ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. static void CreateLogFile(const TCHAR* path) { GetSettings().CreateLog(path); } private: // Æò°¡ ÆÇÁ¤ µî±Þ enum AssessmentGrade { SUSPENDED = -1, // ÆÇÁ¤ À¯º¸ GOOD = 0, // ¾çÈ£ DOUBTFUL, // °¡º­¿î ÀÇ½É SUSPECT, // ÀÇ½É KEY_SUSPECT, // À¯·Â SECURITY_RISK, // º¸¾È À§Çù MAX_ASSESSMENT_GRADE }; // ¶ó¿îµå °Ë»ç °á°ú¸¦ Æò°¡ÇÏ°í ±×¿¡ µû¸¥ ÈÄ¼Ó Á¶Ä¡¸¦ ¼öÇàÇÑ´Ù. void Assess(); // Æò°¡ °á°ú¿¡ µû¸¥ ÈÄ¼Ó Á¶Ä¡¸¦ ¼öÇàÇÑ´Ù. Assess() ³»ºÎ¿¡¼­ È£ÃâµÈ´Ù. void Dispose(AssessmentGrade grade, float average_delta_ratio); // ¶ó¿îµå °Ë»ç Á¤º¸¸¦ ÃʱâÈ­ÇÑ´Ù. void Reset(); // ´ÙÀ½ ¶ó¿îµå ½ÃÀÛ±îÁöÀÇ ½Ã°£(¹Ð¸®ÃÊ ´ÜÀ§)À» °áÁ¤ÇÑ´Ù. // ¹üÀ§°¡ Á¦ÇÑµÈ ·£´ý °ª¿¡ ÀÇÁ¸ÇÑ´Ù. void PickMillisecsToNextRound(); // ¿¬°üµÈ »ç¿ëÀÚ¸¦ °­Á¦ Á¢¼Ó Á¾·á ó¸®ÇÑ´Ù. void DisconnectUser(); // ÃÖ±Ù ÆÇÁ¤ µî±Þ ±â·Ï¿¡¼­ ÁÖ¾îÁø µî±Þ ÀÌ»óÀÇ µî±Þ °³¼ö¸¦ ¸®ÅÏÇÑ´Ù. size_t CountGrades(int grade); // ÁÖ¾îÁø delta ºñÀ² °ªÀ» ÆÇÁ¤ ÀÓ°è°ª°ú ºñ±³ÇØ ÆÇÁ¤ µî±ÞÀ» ¸®ÅÏÇÑ´Ù. static AssessmentGrade GetAssessmentGrade(float delta_ratio); // 32ºñÆ® ƽ Ä«¿îÆ® from°ú toÀÇ Â÷¸¦ ¸®ÅÏÇÑ´Ù. inline static DWORD GetDifference(DWORD from, DWORD to) { if (from <= to) { return (to - from); } return ((DWORD)0xffffffff - from) + to; // wrap-around } // [min, max] ¹üÀ§ÀÇ float ·£´ý °ªÀ» ¸®ÅÏÇÑ´Ù. inline static float frand(float min, float max) { return min + ( (max - min) * ((float)rand()/RAND_MAX) ); } private: // ÇÑ ¶ó¿îµå ´ç ¹Ýº¹¼ö static const unsigned int kNumRepetitionsPerRound = 10; // ÇÑ ¶ó¿îµå ´ç ¾òÀ» ¼ö ÀÖ´Â delta ¼ö´Â ƽ Ä«¿îÆ® Ç¥º» ¼ö¿¡¼­ 1À» »« °ªÀÌ´Ù static const unsigned int kNumDeltaPerRound = kNumRepetitionsPerRound - 1; User* user_; // ¿¬°áµÈ User °´Ã¼ ÂüÁ¶ bool enabled_; // °Ë»ç °¡´É ¿©ºÎ (Heartbeat Ȱ¼ºÈ­ »óÅÂ) bool excepted_; // ¿¬°áµÈ »ç¿ëÀÚ°¡ °Ë»ç Á¦¿Ü ´ë»óÀΰ¡ bool focused_; // ¿¬°áµÈ »ç¿ëÀÚ°¡ °Ë»ç Áֽà ´ë»óÀΰ¡ DWORD last_round_start_tick_; // ÃÖ±Ù ¶ó¿îµå ½ÃÀÛ Æ½ Ä«¿îÆ® DWORD millisecs_to_next_round_; // ´ÙÀ½ ¶ó¿îµå ½ÃÀÛ±îÁöÀÇ Æ½ Ä«¿îÆ® int repetition_; // ¶ó¿îµå ³» ÇöÀç ¹Ýº¹¼ö DWORD server_tick_[kNumRepetitionsPerRound]; DWORD client_tick_[kNumRepetitionsPerRound]; std::deque assessment_; // ÃÖ±Ù nȸÀÇ ÆÇÁ¤ µî±Þ ±â·Ï size_t num_total_rounds_; // °Ë»ç ½ÃµµÇÑ ¶ó¿îµåÀÇ ÃÑ ¼ö size_t num_successful_rounds_; // °Ë»ç ¼º°øÇÑ (ÀÏÁ¤ ¼ö ÀÌ»óÀÇ À¯È¿ Ç¥º»À» È®º¸ÇÑ) ¶ó¿îµåÀÇ ÃÑ ¼ö // ½ºÇǵåÇÙ °Ë»ç °ü·Ã Àü¿ª ¼³Á¤ À¯Æ¿¸®Æ¼ Ŭ·¡½º class Settings { public: Settings(); ~Settings(); // ÁÖ¾îÁø Àü¿ë ¼³Á¤ ÆÄÀϷκÎÅÍ ¼³Á¤À» ÀÐ¾î µéÀδÙ. void Load(const TCHAR* filename); // Àü¿ªÀûÀ¸·Î °Ë»ç°¡ Ȱ¼ºÈ­µÇ¾î ÀÖ´ÂÁöÀÇ ¿©ºÎ¸¦ ¸®ÅÏÇÑ´Ù. bool IsEnabled() const { return enabled_; } // ÃÖ°í µî±ÞÀÇ Á¦Àç ¼ö´ÜÀ¸·Î °­Á¦ Á¢¼Ó Á¾·á¸¦ »ç¿ëÇÒ °ÍÀÎÁöÀÇ ¿©ºÎ¸¦ ¸®ÅÏÇÑ´Ù. bool UsingForcedDisconnection() const { return force_disconnection_; } // ÃÖ°í µî±ÞÀÇ Á¦Àç ¼ö´ÜÀ¸·Î °­Á¦ Á¢¼Ó Á¾·á¸¦ »ç¿ëÇÒ °ÍÀÎÁöÀÇ ¿©ºÎ¸¦ ¼³Á¤ÇÑ´Ù. void UseForcedDisconnection(bool flag) { force_disconnection_ = flag; } // ÁÖ¾îÁø °èÁ¤ÀÌ °Ë»ç Á¦¿Ü ´ë»óÀΰ¡¸¦ ÆÇ´ÜÇÑ´Ù. bool IsException(const char* user_id) const { return (except_users_.find(user_id) != except_users_.end()); } // ÁÖ¾îÁø °èÁ¤ÀÌ °Ë»ç Áֽà ´ë»óÀΰ¡¸¦ ÆÇ´ÜÇÑ´Ù. bool IsFocus(const char* user_id) const { return (focus_users_.find(user_id) != focus_users_.end()); } // Àü¿ë ·Î±× ÆÄÀÏÀ» »ý¼ºÇÑ´Ù. void CreateLog(const TCHAR* path); // Àü¿ë ·Î±×¸¦ ¾ò´Â´Ù. CSunLog* GetLog() { return log_; } // ¶ó¿îµå ½ÃÀÛ »çÀÌÀÇ ±âÁØ ½Ã°£ °£°Ý (ÃÊ ´ÜÀ§) static const unsigned int kMillisecsBetweenRoundStart; // ÆÇÁ¤¿¡ »ç¿ëÇÒ ÃÖ±Ù ¼º°øÇÑ ¶ó¿îµåÀÇ °³¼ö static const size_t kNumRecentRounds; // ÆÇÁ¤ ÀÓ°è°ª delta ºñÀ²Ç¥ static float kThresholds[MAX_ASSESSMENT_GRADE - 1]; // ÃÖ±Ù nȸÀÇ ¼º°øÀûÀÎ ¶ó¿îµå¿¡¼­ Çã¿ëÇÒ µî±Þº° °³¼ö »óÇÑ static float kGradeLimits[MAX_ASSESSMENT_GRADE - 1]; // ÆÇÁ¤ µî±Þ ¹®ÀÚ¿­ Ç¥Çö static const char* kGradeString[MAX_ASSESSMENT_GRADE]; private: bool enabled_; // Àü¿ª Ȱ¼ºÈ­ ¿©ºÎ bool force_disconnection_; // À§¹Ý ŽÁö½Ã °­Á¦ Á¢¼Ó Á¾·á »ç¿ë ¿©ºÎ stdext::hash_set except_users_; // °Ë»ç Á¦¿Ü ´ë»ó UserID ÁýÇÕ stdext::hash_set focus_users_; // °Ë»ç Áֽà ´ë»ó UserID ÁýÇÕ CSunLog* log_; // Àü¿ë ·Î±× }; // Àü¿ª ¼³Á¤ °´Ã¼ ÂüÁ¶¸¦ ¸®ÅÏÇÑ´Ù. static Settings& GetSettings() { static Settings settings; return settings; } friend class TickSpeedCheckerTest_EffectiveForcedDisconnection_Test; friend class TickSpeedCheckerTest_IneffectiveForcedDisconnection_Test; }; #endif // #ifdef _NA_20100802_SPEED_HACK_DETECTION #endif // #ifndef SERVER_AGENTSERVER_TIMECHECK_TICKSPEEDCHECKER_H_