diff --git a/Source/GameSource/WORLDSERVER/DPSrvr.cpp b/Source/GameSource/WORLDSERVER/DPSrvr.cpp index 6ccbf8e7..ba7a5889 100644 --- a/Source/GameSource/WORLDSERVER/DPSrvr.cpp +++ b/Source/GameSource/WORLDSERVER/DPSrvr.cpp @@ -9699,55 +9699,146 @@ void CDPSrvr::OnCreateAngel( CAr & ar, DPID dpidCache, DPID dpidUser, LPBYTE /*l char str1[1024]; ar.ReadString( str1, _countof( str1 ) ); - char * str2 = strstr(str1,"DD"); if (str2==NULL) return; - *str2 = 0; str2=str2+2; - char * split1 = strchr(str1,'D'); if (split1==NULL) return; - *split1 = 0; split1++; - int id1 = atoi(str1); - int cnt1 = atoi(split1); - FLItemElem* pItemElem1 = (FLItemElem*)pUser->GetItemId( id1 ); - if( pItemElem1==NULL || !IsUsableItem( pItemElem1 ) ) return; - int dwid1 = pItemElem1->GetProp()->dwID; - if (dwid1 == ITEM_INDEX(2035,II_GEN_MAT_ORICHALCUM01) || (dwid1 == ITEM_INDEX(2082,II_GEN_MAT_ORICHALCUM01_1) ) ) nOrichalcum+= cnt1; - if (dwid1 == ITEM_INDEX(2036,II_GEN_MAT_MOONSTONE) || (dwid1 == ITEM_INDEX(2083,II_GEN_MAT_MOONSTONE_1) ) ) nMoonstone += cnt1; - - - FLItemElem* pItemElem2 =NULL; - int id2 = 0; + // Exploit fix: never trust client-provided counts / item ids + struct AngelCreatePacket + { + static bool ParseDwordStrict( const char* psz, DWORD& out ) + { + if( psz == NULL || *psz == '\0' ) + return false; + unsigned __int64 v = 0; + for( const unsigned char* p = (const unsigned char*)psz; *p; ++p ) + { + if( *p < '0' || *p > '9' ) + return false; + v = ( v * 10 ) + ( *p - '0' ); + if( v > 0xFFFFFFFFULL ) + return false; + } + out = (DWORD)v; + return true; + } + + static bool ParsePositiveIntStrict( const char* psz, int& out ) + { + if( psz == NULL || *psz == '\0' ) + return false; + unsigned __int64 v = 0; + for( const unsigned char* p = (const unsigned char*)psz; *p; ++p ) + { + if( *p < '0' || *p > '9' ) + return false; + v = ( v * 10 ) + ( *p - '0' ); + if( v > 2147483647ULL ) + return false; + } + if( v == 0 ) + return false; + out = (int)v; + return true; + } + + static bool IsMaterialOrichalcum( const DWORD dwid ) + { + return ( dwid == ITEM_INDEX(2035,II_GEN_MAT_ORICHALCUM01) || dwid == ITEM_INDEX(2082,II_GEN_MAT_ORICHALCUM01_1) ); + } + static bool IsMaterialMoonstone( const DWORD dwid ) + { + return ( dwid == ITEM_INDEX(2036,II_GEN_MAT_MOONSTONE) || dwid == ITEM_INDEX(2083,II_GEN_MAT_MOONSTONE_1) ); + } + }; + + char* str2 = strstr( str1, "DD" ); + if( str2 == NULL ) + return; + *str2 = '\0'; + str2 += 2; + char* split1 = strchr( str1, 'D' ); + if( split1 == NULL ) + return; + *split1 = '\0'; + ++split1; + + DWORD dwObjId1 = 0; + int cnt1 = 0; + if( !AngelCreatePacket::ParseDwordStrict( str1, dwObjId1 ) || !AngelCreatePacket::ParsePositiveIntStrict( split1, cnt1 ) ) + return; + FLItemElem* pItemElem1 = (FLItemElem*)pUser->GetItemId( dwObjId1 ); + if( pItemElem1 == NULL || !IsUsableItem( pItemElem1 ) ) + return; + if( cnt1 > pItemElem1->m_nItemNum ) + return; + const DWORD dwid1 = pItemElem1->GetProp()->dwID; + if( AngelCreatePacket::IsMaterialOrichalcum( dwid1 ) ) + nOrichalcum += cnt1; + else if( AngelCreatePacket::IsMaterialMoonstone( dwid1 ) ) + nMoonstone += cnt1; + else + return; + + FLItemElem* pItemElem2 = NULL; + DWORD dwObjId2 = 0; int cnt2 = 0; - char * str3 = strstr(str2,"DD"); - if (str3) - { - char * split2 = strchr(str2,'D'); if (split2==NULL) return; - *split2 = 0; split2++; - id2 =atoi(str2); - cnt2 = atoi(split2); - pItemElem2 = (FLItemElem*)pUser->GetItemId( id2 ); - if(pItemElem2==NULL || !IsUsableItem( pItemElem2 ) ) return; - int dwid2 = pItemElem2->GetProp()->dwID; - if (dwid2 == ITEM_INDEX(2035,II_GEN_MAT_ORICHALCUM01) || (dwid2 == ITEM_INDEX(2082,II_GEN_MAT_ORICHALCUM01_1) ) ) nOrichalcum+= cnt2; - if (dwid2 == ITEM_INDEX(2036,II_GEN_MAT_MOONSTONE) || (dwid2 == ITEM_INDEX(2083,II_GEN_MAT_MOONSTONE_1) ) ) nMoonstone += cnt2; - } - - -/////////////////////////////////////////////////////// - - // R/B/G/W - float greenAngelRate = (float) (nOrichalcum + nMoonstone); - float blueAngelRate = greenAngelRate * 2.0f; - float whiteAngelRate = greenAngelRate * 0.1f; - float redAngelRate = 100.0f - ( whiteAngelRate + greenAngelRate + blueAngelRate ); + char* str3 = strstr( str2, "DD" ); + if( str3 ) + { + *str3 = '\0'; + char* split2 = strchr( str2, 'D' ); + if( split2 == NULL ) + return; + *split2 = '\0'; + ++split2; + if( !AngelCreatePacket::ParseDwordStrict( str2, dwObjId2 ) || !AngelCreatePacket::ParsePositiveIntStrict( split2, cnt2 ) ) + return; + pItemElem2 = (FLItemElem*)pUser->GetItemId( dwObjId2 ); + if( pItemElem2 == NULL || !IsUsableItem( pItemElem2 ) ) + return; + if( cnt2 > pItemElem2->m_nItemNum ) + return; + const DWORD dwid2 = pItemElem2->GetProp()->dwID; + if( AngelCreatePacket::IsMaterialOrichalcum( dwid2 ) ) + nOrichalcum += cnt2; + else if( AngelCreatePacket::IsMaterialMoonstone( dwid2 ) ) + nMoonstone += cnt2; + else + return; + // avoid double-removing/logging the same stack if a malicious client sends the same item twice + if( dwObjId2 == dwObjId1 ) + { + if( cnt1 + cnt2 > pItemElem1->m_nItemNum ) + return; + cnt1 += cnt2; + cnt2 = 0; + pItemElem2 = NULL; + } + } + + const int nMaterial = ( nOrichalcum + nMoonstone ); + if( nMaterial <= 0 ) + return; + + // R/B/G/W (normalize to prevent negative/overflow rates) + float greenAngelRate = (float)nMaterial; + float blueAngelRate = greenAngelRate * 2.0f; + float whiteAngelRate = greenAngelRate * 0.1f; + float redAngelRate = 100.0f - ( whiteAngelRate + greenAngelRate + blueAngelRate ); + if( redAngelRate < 0.0f ) + redAngelRate = 0.0f; + const float totalRate = redAngelRate + blueAngelRate + greenAngelRate + whiteAngelRate; + if( totalRate <= 0.0f ) + return; // 30035, II_SYS_SYS_QUE_ANGEL_RED // 30036, II_SYS_SYS_QUE_ANGEL_BLUE // 30037, II_SYS_SYS_QUE_ANGEL_GREEN // 30038, II_SYS_SYS_QUE_ANGEL_WHITE static DWORD adwItemId[4] = { 30035, 30036, 30037, 30038}; FLOAT fRate[4]; - fRate[0] = redAngelRate; - fRate[1] = fRate[0] + blueAngelRate; - fRate[2] = fRate[1] + greenAngelRate; - fRate[3] = fRate[2] + whiteAngelRate; - float rand = xRandom(1000) / 10.0f; + const float fScale = 100.0f / totalRate; + fRate[0] = redAngelRate * fScale; + fRate[1] = fRate[0] + ( blueAngelRate * fScale ); + fRate[2] = fRate[1] + ( greenAngelRate * fScale ); + fRate[3] = 100.0f; + float rand = xRandom(1000) / 10.0f; DWORD dwItemId = 0; for( int i = 0; i < 4; i++ ) { if( rand <= fRate[i] ) @@ -9759,9 +9850,6 @@ void CDPSrvr::OnCreateAngel( CAr & ar, DPID dpidCache, DPID dpidUser, LPBYTE /*l if( dwItemId <= 0 ) return; - - - if( pUser->m_Inventory.GetEmptyCountByItemId( dwItemId ) < 1 ) { pUser->AddDiagText( prj.GetText( TID_GAME_LACKSPACE ) ); return;