protected void ReplaceNextOperation(FlashReader inCode, FlashWriter outCode, ASMethod method, OPCode oldOP, object[] oldValues, OPCode newOP, object[] newValues) { while (inCode.IsDataAvailable) { OPCode op = inCode.ReadOP(); object[] values = inCode.ReadValues(op); if (op != oldOP) { outCode.WriteOP(op, values); continue; } if (oldValues != null && (oldValues.Length == values.Length)) { bool valuesMatch = true; for (int i = 0; i < oldValues.Length; i++) { if (oldValues[i] != null && !oldValues[i].Equals(values[i])) { valuesMatch = false; break; } } if (!valuesMatch) { outCode.WriteOP(op, values); continue; } } outCode.WriteOP(newOP, newValues); WriteLog($"Replaced operation '{oldOP}[{string.Join(", ", oldValues)}]' with '{newOP}[{string.Join(", ", newValues)}]' in method '{method}'."); break; } }
protected virtual void WriteMethodHashData(BinaryWriter hashInput, ASMethod asMethod, bool writeInstructions) { WriteTraitsHashData(hashInput, asMethod.Body); hashInput.Write(asMethod.Body.Exceptions.Count); hashInput.Write(asMethod.Body.MaxStack); hashInput.Write(asMethod.Body.LocalCount); hashInput.Write(asMethod.Body.MaxScopeDepth); hashInput.Write(asMethod.Body.InitialScopeDepth); hashInput.Write(asMethod.Parameters.Count); foreach (ASParameter parameter in asMethod.Parameters) { if (parameter.IsOptional) { hashInput.Write(parameter.IsOptional); WriteValueSlotHashData(hashInput, parameter); } if (parameter.Type != null) { WriteMultinameHashData(hashInput, parameter.Type); } } if (writeInstructions) { using (var codeOutput = new FlashReader(asMethod.Body.Bytecode)) { while (codeOutput.IsDataAvailable) { OPCode op = codeOutput.ReadOP(); object[] values = codeOutput.ReadValues(op); hashInput.Write((byte)op); } } } }
protected void FixLocalRegisters(ASMethod method) { // But, what about nested branches? Does not work with those yet, sadly. if (method.Body == null) { return; } if (method.Body.LocalCount == (1 + method.Parameters.Count)) { return; } ABCFile abc = method.ABC; using (var outCode = new FlashWriter()) using (var inCode = new FlashReader(method.Body.Bytecode)) { uint jumpValue = 0; int jumpValueEnd = 0; int jumpValueStart = 0; int totalDifference = 0; bool isJumpingOver = false; var labelPositions = new Stack <int>(); while (inCode.IsDataAvailable) { if (isJumpingOver && inCode.Position >= jumpValueEnd) { isJumpingOver = false; } OPCode op = inCode.ReadOP(); object[] values = inCode.ReadValues(op); if (op != OPCode.Debug || ((byte)values[0] != 1)) { if (IsJumpInstruction(op)) // If a label position is contained in the stack, the next jump instruction will go backwards. { // Not a backwards jump, also check to make sure the jump value isn't crazy big, otherwise it might be a reverse jump instruction. // If large: (uint.MaxValue - value[0]) == Bytes to jump back to get to the last saved label position in stack. if (labelPositions.Count == 0 || ((uint)values[0] <= inCode.Length)) { isJumpingOver = true; jumpValue = (uint)values[0]; // To later check if we're in the middle of the jumping range. jumpValueStart = (outCode.Position + 1); // To re-write the jump value if the data in-between has sgrown. jumpValueEnd = (int)(inCode.Position + jumpValue); } else { // Find by how many bytes we need to move back from current position. values[0] = (uint)(uint.MaxValue - ((outCode.Position + 4) - labelPositions.Pop())); } } else if (op == OPCode.Label) // Store label positions, to easily determine by how many bytes we need to jump back to this position. { labelPositions.Push(outCode.Position + 1); } outCode.WriteOP(op, values); continue; } var local = (byte)values[2]; if (local > method.Parameters.Count) { values[1] = abc.Constants .AddString("local" + local); } outCode.WriteOP(op, values); int difference = (((inCode.Position - outCode.Length) * -1) - totalDifference); totalDifference += difference; if (isJumpingOver) { int curPos = outCode.Position; outCode.Position = jumpValueStart; outCode.WriteS24(jumpValue += (uint)difference); // PlusEqual the value, in-case we've already modifed within the jumping range. outCode.Position = curPos; } } method.Body.Bytecode = outCode.ToArray(); } }
protected int FindMessageReferences(ASClass fromClass, TraitContainer container, ASMethod fromMethod, int rank, int msgReferencesFound) { ABCFile abc = fromClass.ABC; using (var inCode = new FlashReader(fromMethod.Body.Bytecode)) { var multinameStack = new Stack <ASMultiname>(); while (inCode.IsDataAvailable) { OPCode op = inCode.ReadOP(); object[] values = inCode.ReadValues(op); switch (op) { #region Case: GetProperty case OPCode.GetProperty: { multinameStack.Push(abc.Constants.Multinames[(int)values[0]]); break; } #endregion #region Case: NewFunction case OPCode.NewFunction: { ASMethod newFuncMethod = abc.Methods[(int)values[0]]; msgReferencesFound = FindMessageReferences(fromClass, container, newFuncMethod, rank, msgReferencesFound); break; } #endregion #region Case: ConstructProp case OPCode.ConstructProp: { ASMultiname constructPropType = abc.Constants.Multinames[(int)values[0]]; ASClass messageClass = abc.FindFirstClassByName(constructPropType.Name); if (messageClass == null || !IsMessageClass(messageClass)) { continue; } if (!_messageReferences.ContainsKey(messageClass)) { _messageReferences[messageClass] = new List <ASReference>(); } var msgReference = new ASReference(messageClass, fromClass, fromMethod); if (!IsMessageOutgoing(messageClass)) { ASMultiname topName = multinameStack.Pop(); msgReference.FromMethod = null; IEnumerable <MethodGetterSetterTrait> mgsTraits = container.FindMethodGetterSetterTraits(); rank = 0; // TODO: Move this into a method or something, I'm re-writing it below as well. foreach (MethodGetterSetterTrait mgsTrait in mgsTraits) { rank++; if (mgsTrait.Method.TraitName == topName.Name) { msgReference.FromMethod = mgsTrait.Method; break; } } if (msgReference.FromMethod == null && multinameStack.Count > 0) { ASMultiname bottomName = multinameStack.Pop(); foreach (ASTrait trait in container.Traits) { switch (trait.TraitType) { #region Case: Slot, Constant case TraitType.Slot: case TraitType.Constant: { if (trait.Name.Name != bottomName.Name) { continue; } var scTrait = (SlotConstantTrait)trait.Data; if (scTrait.TypeName.MultinameType != ConstantType.QName) { continue; } ASClass slotValueClass = abc.FindFirstClassByName(scTrait.TypeName.Name); rank = 0; mgsTraits = slotValueClass.Instance.FindMethodGetterSetterTraits(); foreach (MethodGetterSetterTrait mgsTrait in mgsTraits) { rank++; if (mgsTrait.Method.TraitName == topName.Name) { msgReference.FromMethod = mgsTrait.Method; break; } } if (msgReference.FromMethod != null) { msgReference.FromClass = slotValueClass; } break; } #endregion } if (msgReference.FromMethod != null) { break; } } } } msgReference.Id = ((++msgReferencesFound) + rank); // We can't rely on the amount of references found, since the hooking of incoming messages are randomized each revision. if (!IsMessageOutgoing(messageClass)) { msgReference.Id = rank; } _messageReferences[messageClass].Add(msgReference); break; } #endregion } } } return(msgReferencesFound); }
/// <summary> /// Returns a unique MD5 hash generated from various information contained in the message class. /// </summary> /// <param name="messageClass">The Outgoing/Incoming message class to create the unique hash from.</param> /// <returns></returns> public string GetMessageHash(ASClass messageClass) { if (!IsMessageClass(messageClass)) { throw new ArgumentException( "The specified class is not a valid Outgoing/Incoming message class.", nameof(messageClass)); } if (_messageHashes.ContainsKey(messageClass)) { return(_messageHashes[messageClass]); } else if (_messageReferences.Count == 0) { FindMessageReferences(); } using (var hashOut = new FlashHasher()) { hashOut.IsSummarizing = true; bool isOutgoing = IsMessageOutgoing(messageClass); hashOut.Write(isOutgoing); Write(hashOut, messageClass, isOutgoing); if (!messageClass.Instance.Name.Name.EndsWith("Composer") && _messageReferences.ContainsKey(messageClass)) { List <ASReference> msgReferences = _messageReferences[messageClass]; hashOut.Write(msgReferences.Count); foreach (ASReference msgReference in msgReferences) { if (!isOutgoing) { string fromClassName = msgReference.FromClass.Instance.Name.Name; if (fromClassName == "IncomingMessages") { hashOut.Write(fromClassName); } } hashOut.Write(msgReference.Id); hashOut.Write(msgReference.FromMethod, true); byte[] bytecode = msgReference.FromMethod.Body.Bytecode; using (var inCode = new FlashReader(bytecode)) { int lineCount = 0; while (inCode.IsDataAvailable) { OPCode op = inCode.ReadOP(); object[] values = inCode.ReadValues(op); // Avoid common integer values, by starting big... if (op == OPCode.DebugLine) { hashOut.Write(int.MaxValue - (++lineCount)); } } } } } string hash = hashOut.ToString(); if (!_messages.ContainsKey(hash)) { _messages[hash] = new List <ASClass>(); } if (!_messages[hash].Contains(messageClass)) { _messages[hash].Add(messageClass); } _messageHashes[messageClass] = hash; return(hash); } }
/// <summary> /// Modifies the bytecode to allow the client to connect to anywhere. /// </summary> public void BypassRemoteHostCheck() { ABCFile abc = ABCFiles[2]; ASInstance habboCommMngr = abc.FindFirstInstanceByName("HabboCommunicationManager"); if (habboCommMngr == null) { return; } int hostValueSlotObjTypeIndex = -1; string hostValueSlotObjName = string.Empty; foreach (ASTrait trait in habboCommMngr.Traits) { if (trait.TraitType != TraitType.Slot) { continue; } if (((SlotConstantTrait)trait.Data).TypeName.Name != "String") { continue; } hostValueSlotObjName = trait.Name.Name; hostValueSlotObjTypeIndex = trait.NameIndex; break; } ASMethod initCompMethod = habboCommMngr .FindFirstMethod("initComponent", "void"); int getPropertyObjTypeIndex = abc.Constants .IndexOfMultiname("getProperty"); ASMethod initConnectionMethod = null; using (var outCode = new FlashWriter()) using (var inCode = new FlashReader(initCompMethod.Body.Bytecode)) { object[] values = inCode.ReadValuesUntil(OPCode.CallPropVoid, null, 0); if (values == null) { return; } CopyBytecode(inCode, outCode, 0, inCode.Position); outCode.WriteOP(OPCode.GetLocal_0); outCode.WriteOP(OPCode.FindPropStrict, getPropertyObjTypeIndex); outCode.WriteOP(OPCode.PushString, abc.Constants.AddString("connection.info.host")); outCode.WriteOP(OPCode.CallProperty, getPropertyObjTypeIndex, 1); outCode.WriteOP(OPCode.InitProperty, hostValueSlotObjTypeIndex); WriteLog($"Method '{initCompMethod}' modified to include '{hostValueSlotObjName} = getProperty(\"connection.info.host\");'."); CopyBytecode(inCode, outCode); initCompMethod.Body.Bytecode = outCode.ToArray(); values = inCode.ReadValuesUntil(OPCode.CallPropVoid); ASMultiname callPropVoidType = abc.Constants.Multinames[(int)values[0]]; initConnectionMethod = habboCommMngr.FindFirstMethod(callPropVoidType.Name, "void"); } using (var outCode = new FlashWriter()) using (var inCode = new FlashReader(initConnectionMethod.Body.Bytecode)) { int ifNeCount = 0; int byteJumpCountPos = 0; int differenceOffset = 0; uint byteJumpCountValue = 0; int magicNumberIndex = abc.Constants.AddInteger(65290); while (inCode.IsDataAvailable) { OPCode op = inCode.ReadOP(); object[] values = null; if (op != OPCode.PushInt) { values = inCode.ReadValues(op); outCode.WriteOP(op, values); if (op == OPCode.IfNe && (++ifNeCount == 2 || ifNeCount == 4)) { byteJumpCountPos = (outCode.Position - 3); byteJumpCountValue = (uint)values[0]; } continue; } bool isFinalPushInt = false; int pushIntIndex = inCode.Read7BitEncodedInt(); int pushIntValue = abc.Constants.Integers[pushIntIndex]; #region Switch: pushIntValue switch (pushIntValue) { case 65244: //97 case 65185: //32 case 65191: //175 case 65189: //123 case 65188: //164 case 65174: //45 case 65238: //297 case 65184: //127 case 65171: //20 case 65172: //58 { pushIntIndex = magicNumberIndex; isFinalPushInt = (pushIntValue == 65172); break; } } #endregion outCode.WriteOP(op, pushIntIndex); int byteDifference = (((inCode.Position - outCode.Length) * -1) - differenceOffset); if (isFinalPushInt) { int curPos = outCode.Position; differenceOffset += byteDifference; outCode.Position = byteJumpCountPos; outCode.WriteS24(byteJumpCountValue + (uint)byteDifference); outCode.Position = curPos; if (ifNeCount == 4) { CopyBytecode(inCode, outCode); initConnectionMethod.Body.Bytecode = outCode.ToArray(); WriteLog($"Method '{initConnectionMethod}' modified to not append suffix to the host value."); return; } } } } }
public ASClass GetIncomingParser(ASInstance incomingInstance) { if (_incomingParsersCache.ContainsKey(incomingInstance)) { return(_incomingParsersCache[incomingInstance]); } ASClass parserClass = null; ABCFile abc = incomingInstance.ABC; try { using (var codeOut = new FlashReader( incomingInstance.Constructor.Body.Bytecode)) { while (codeOut.IsDataAvailable) { OPCode op = codeOut.ReadOP(); object[] values = codeOut.ReadValues(op); if (op != OPCode.GetLex) { continue; } var getLexIndex = (int)values[0]; ASMultiname getLexName = abc.Constants.Multinames[getLexIndex]; parserClass = abc.FindClassByName(getLexName.ObjName); if (parserClass != null) { return(parserClass); } break; } } ASInstance incomingSuperInstance = abc.FindInstanceByName( incomingInstance.SuperType.ObjName); ASMultiname parserReturnType = incomingSuperInstance .FindGetter("parser").Method.ReturnType; SlotConstantTrait parserSlot = incomingSuperInstance .FindSlot("*", parserReturnType.ObjName); foreach (ASTrait trait in incomingInstance.Traits) { if (trait.TraitType != TraitType.Method) { continue; } var mgsTrait = (MethodGetterSetterTrait)trait.Data; if (mgsTrait.Method.Parameters.Count != 0) { continue; } using (var codeOut = new FlashReader( mgsTrait.Method.Body.Bytecode)) { while (codeOut.IsDataAvailable) { OPCode op = codeOut.ReadOP(); object[] values = codeOut.ReadValues(op); if (op != OPCode.GetLex) { continue; } var getLexIndex = (int)values[0]; ASMultiname getLexType = abc.Constants.Multinames[getLexIndex]; if (getLexType.ObjName != parserSlot.ObjName) { continue; } parserClass = abc.FindClassByName(mgsTrait.Method.ReturnType.ObjName); if (parserClass != null) { return(parserClass); } break; } } } return(parserClass); } finally { if (parserClass != null) { _incomingParsersCache[incomingInstance] = parserClass; } } }
protected void ScanForMessageReference(Dictionary <string, ASClass> messageClasses, ASClass asClass, ASMethod method, int traitIndex, int messageRefCount = 0) { ABCFile abc = asClass.ABC; ASClass messageClass = null; using (var outCode = new FlashReader(method.Body.Bytecode)) { while (outCode.IsDataAvailable) { OPCode op = outCode.ReadOP(); object[] values = outCode.ReadValues(op); switch (op) { case OPCode.NewFunction: { var newFuncIndex = (int)values[0]; ASMethod newFuncMethod = abc.Methods[newFuncIndex]; ScanForMessageReference(messageClasses, asClass, newFuncMethod, traitIndex, messageRefCount); break; } case OPCode.ConstructProp: { var constructPropIndex = (int)values[0]; if (messageClass != null) { ASMultiname constructPropType = abc.Constants.Multinames[constructPropIndex]; if (constructPropType.ObjName == messageClass.Instance.Type.ObjName) { if (!_messageReferencesCache.ContainsKey(messageClass)) { _messageReferencesCache[messageClass] = new List <Tuple <ASMethod, int> >(); } _messageReferencesCache[messageClass].Add( new Tuple <ASMethod, int>(method, (traitIndex + (++messageRefCount)))); } messageClass = null; } break; } case OPCode.FindPropStrict: { var findPropStrictIndex = (int)values[0]; string findPropStrictObjName = abc.Constants .Multinames[findPropStrictIndex].ObjName; if (messageClasses.ContainsKey(findPropStrictObjName)) { messageClass = messageClasses[findPropStrictObjName]; // Incoming messages currently not supported. if (IncomingTypes.ContainsValue(messageClass)) { messageClass = null; } } break; } } } } }