Авторские статьи Flash безопасность. Часть I

Discussion in 'Статьи' started by randman, 13 Aug 2012.

  1. randman

    randman Members of Antichat

    Joined:
    15 May 2010
    Messages:
    1,366
    Likes Received:
    610
    Reputations:
    1,101
    Доброго времени суток всем, кто читает этот цикл статей. К моменту написания цикла будет состоять из IV частей, ну и собственно это его первая часть. В ней я хотел бы вас познакомить с основами Flash технологии, методами создания Flash приложений, исследования, типичных ошибках создания приложения с обоих сторон(Клиентская и серверная), и познакомить с простыми примерами использования приложения в целях, непредусмотренный разработчикам.

    Для работы мы будем использовать кроссплатформенное ПО, так что вы можете повторить всё(Конечно же только на localhost, и на своими Flash приложениями) под системами Windows, Linux(В частности и *nix), и в большинстве случаев даже под MacOS X.


    I. Технология Flash​



    Adobe Flash (ранее Macromedia Flash), далее просто Flash — мультимедийная платформа компании Adobe для создания веб-приложений или мультимедийных презентаций. Широко используется для создания рекламных баннеров, анимации, игр, а также воспроизведения на веб-страницах видео- и аудиозаписей.​
    [​IMG]
    Я думаю вы знаете, что технология Flash обычно располагается на клиентской части, подобно JavaScript в виде частично скомпилированных
    SWF​
    файлов(Расширение может быть изменено). Как я думаю, вы уже знаете, всё, что находиться на клиентской части может быть прочитано, изменено(Или заменено полностью), модифицировано и использовано в совершенно других целях, без ведома серверной части, и Flash этому не исключение; однако часть Flash разработчиков не имеет представления о вторжении в Flash, изменении кода, интерфейса, перехвата данных и это тоже следует учитывать.

    Также далее в тесте будет употребляться термин «Правильные Flash приложения» ,- под ними будут подразумеваться Flash приложения, действия реверсинга с которыми которые не будет иметь никого смысла, за исключением изучения исходного кода для самообучения создания подобных приложений.

    II. Используемое ПО​


    Браузер:

    Mozilla Firefox, - http://www.mozilla.org/ru/firefox/new/
    Требуемые дополнения: NoScript, NoFlash, FireBug, FlashFireBug, Greasmonkey.

    Flash:


    Debug версия Adobe Flash: http://www.adobe.com/support/flashplayer/downloads.html

    Сниффер:


    Wireshark,- http://www.wireshark.org/
    tcpdump,- http://ru.wikipedia.org/wiki/Tcpdump

    JRE or JDK from Oracle , Adobe AIR:


    http://java.com/ru/download/index.jsp
    http://get.adobe.com/ru/air/

    HTTP Анализатор:


    Charles,- http://www.charlesproxy.com/

    Декомпилятор:


    Asdec,- http://code.google.com/p/asdec/

    Отладчик:


    De Monster Debugger,-http://demonsterdebugger.com/downloads

    Версия 3.0 совместима с последней версией AIR for Linux.

    Adobe Flex(328 МБ):


    http://www.adobe.com/devnet/flex/flex-sdk-download-all.html

    Так-же нам понадобиться любой текстовый редактор с подсветкой синтаксиса, HEX-редактор. В случаи необходимости этот инструментарий будет расширяться.


    III. Классификация уязвимостей​


    Перейдем собственно к уязвимостям:
    • Уязвимости Flash платформы
    • Уязвимости серверной части, связанные с недостаточной фильтрацией данных.
    • Недостаточная проверка данных на серверной стороне.
    • Возможность обмана серверной части посредством использования UDP протокола связи между Flash и сервером.
    • Использование клиентской части для формирования данных, которые пользователь изменять ни в коем случае не должен.
    • «Отсутствие» как таковой северной части, служащей ля обработки важных данных.

    Список составлен приблизительно и в будующем будет пополняться.


    IV. INTRO​




    1. В первой части цикла мы рассмотрим как таковые уязвимости и наиболее простые примеры.
    2. Вторая часть будет примерно о этом же, но с более сложными примерами.


    В этих двух частях будут рассказаны основные идеи, способы выполнения, ошибки.

    3. В этой части будет рассказано о нестандартном использовании Flash.
    4. Ну и эта самая сложная часть вам расскажет о секретах нелегального заработка с использованием различных систем онлайн казино. Сразу скажу, тут не будет приведено примеров реального заработка, однако объем информации будет очень большой, так что вы ход статьи на ближайшее время не планируется. Статья приводиться только для ознакомления и не может являться руководством к побуждению противоправных действий ни в коем случае.


    В данном цикле не будет рассматриваться изменение переменных с помощью ArtMoney и подобных программ, однако скажу, что это всё же возможно.


    V. BODY 1​


    Как вы уже поняли, тут будут рассматриваться наиболее простые примеры, - для понимания общих технологий использования уязвимостей и базовой работе с ПО, с которым у вас могут возникнуть большие проблемы. Сразу говорю, - тут не будет приводиться руководство к установке ПО, настройке и использованию. Основная часть действий с ним требует предварительного ознакомления, что выходит за рамки этого цикла. Так же говорю, - большинство текущих уязвимостей на момент выхода будет закрыто, так что вам не надо пытаться повторить это. Ну что же, начнём...

    Начать я планировал с рассказа о моей первой найденной уязвимости в Flash, на mail.ru, опубликованной под видом «Взлом mini.games.mail.ru BETA версия»(В настоящее время закрыта), и была растиражирована по всему интернету скрипткиддерами, и даже продавалась за деньги:

    http://www.youtube.com/watch?v=XAK6-fj2Jzk
    http://www.youtube.com/watch?v=pAyHxuLW3LU
    http://pro-xaker.do.am/load/poleznye_materialy/internet/vzlom_mini_games_mail_ru_beta_versija/27-1-0-320

    И т. д.

    Сейчас я расскажу, как это всё было найдено работало.

    С помощью Charles был полностью проанализирован весь трафик Flash приложения, После чего было выяснено, что Flash по HTTP протоколу запрашивал данные о цене различных бонусов к играм в формате JSON, - эти данные были подменены, а т. к. серверная часть доверяла клиентской можно было их использовать бесплатно.

    Изучение Flash приложения начинается именно с этапа мониторинга всех его соединений с сетью интернет, по HTTP,- Сharles, по другим протоколам, - Wireshark. Во время этого исследования выясняется в первую очередь, - что передаётся серверу, а что клиенту. Этого обычно хватает для общего представления о структуре приложения, однако при выполнении данного этапа следует учитыватть, что данные в передаваемом соединении могут быть зашифрованы.

    С помощью Charles можно видеть все обращения к скриптам, которые не отображаются на странице, к примеру с помощью него была найдена эта XSS(Не закрыта):
    PHP:
    http://forum.antichat.ru/showthread.php?p=2988185#post2988185
    http://ads.depositfiles.com/p.php?v=5678%27%29; alert%28%27xss%27%29; $.get%28%275678
    так что эту программу следует всегда держать на компьютере.

    Рассмотрим пример, найденный специально для этой статьи, демонстрирующий полезные возможности Charles.

    Сайт http://flashplayer.ru/ , - специализируется на публиковании Flash-игр, при этом зачастую у игр бывает рейтинг лист(High Score) на сайте разработчика(Почти у всех игр). Одну из таких игр, в которой используется «Защита разработанная автором» мы сейчас рассмотрим.

    Gold fishing ,- http://flashplayer.ru/play_6988.php .
    Открываем, играем, ждём момента когда нас попросят ввести ник для записи очков(После проигрыша), вводим, смотрим LOG Charles:


    [​IMG]
    [​IMG]


    PHP:
    POST /saveScore.aspx HTTP/1.1 
    Host
    www.fupa.com 
    User
    -AgentMozilla/5.0 ***
    Accepttext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
    Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3 
    Accept-Encoding: gzip, deflate 
    Cookie: ASPSESSIONIDQASRTCCT=GMPJIDFDFPGKKKIBFEENCGHJ; ASP.NET_SessionId=mmsramukt2k4muvbogqeft55 
    Referer: http://flashplayer.ru/games/flash/6988_92442.swf 
    Content-Type: application/x-www-form-urlencoded 
    Content-Length: 96 

    onLoad=%5Btype%20Function%5D&hash=226b9d8ff5b7ae9619d8a74823fb6649&score=5237&name=AlexR&id=3913
    success=true
    Как видим, количество очков и ник не шифруются, однако на сервер передаётся ещё какой то хэш, который должен сравниваться на серверной части по определённому алгоритму. Пытаемся переотправить запрос с нужным кол-вом очков(L Mouse Click->Edit), получаем в ответе success=false.

    Значит всё же придётся декомпилировать. Скачиваем(http://flashplayer.ru/games/flash/6988_92442.swf), открываем в Asdec. Как видим, игра написана на AS2:
    [​IMG]
    Немного покопавшись в коде, находим следующее(Использовалось сразу несколько декомпиляторов):
    PHP:
    //Object.registerClass("HighScores",com.novelgames.flashgames.highscoresAS2.HighScores);
    Push "com.novelgames.flashgames.highscoresAS2.HighScores"
    GetVariable
    Push 
    "HighScores" "Object"
    GetVariable
    Push 
    "registerClass"
    CallMethod
    Pop
    Значит нам нужно искать код самого com.novelgames.flashgames.highscoresAS2, который как ни странно находиться на самом видном месте и состоит из:
    DoInitActionTag (153: __Packages.com.novelgames.flashgames.highscoresAS2.Config)
    DoInitActionTag (154: __Packages.com.novelgames.flashgames.highscoresAS2.Record)
    DoInitActionTag (155: __Packages.com.novelgames.flashgames.highscoresAS2.MD5)
    DoInitActionTag (80: __Packages.com.novelgames.flashgames.highscoresAS2.HighScores) ​

    Теперь у нас имеется 2 варианта: Внести необходимые изменения в P-Код, или Самому сгенерировать нужный хэш и отправить его на сервер. Мы пойдём вторым путём, хотя первый был немного проще. Иногда для генерации хэша при наличии исходных кодов будет удобнее переписать функцию на JS, однако перепись на PHP будет немного сложна, так как точность работы с малыми числами у JS выше и придётся использовать библиотеку BCMath, иначе результаты работы будут отличаться.

    Функция сохранения очков, как ни странно называется saveScore():

    PHP:
     function saveScore()
        {
            var 
    highScores this;
            var 
    __reg3 = new LoadVars();
            var 
    __reg2 undefined;
            if (
    this.records == null
            {
                
    this.finishedLoadingScoresAction this.saveScore;
                
    this.showLoadingMessage true;
                
    this.loadScores();
                return 
    undefined;
            }
            else 
            {
                
    __reg2 0;
                while (
    __reg2 this.records.length
                {
                    if (
    this.records[__reg2].score <= this.score
                    {
                        break;
                    }
                    ++
    __reg2;
                }
                
    this.highScoresHighlightIndex __reg2;
                
    this.records.splice(__reg20, new com.novelgames.flashgames.highscoresAS2.Record(this.playerNamethis.score));
                
    this.records.splice(this.__maxNoOfScores);
            }
            
    __reg3.id this.__gameID;
            
    __reg3.name this.playerName;
            
    __reg3.score this.score;
            
    __reg3.hash com.novelgames.flashgames.highscoresAS2.MD5.md5("" this.__gameID this.playerName this.score this.__get__hashKey());
            
    __reg3.onLoad = function (success)
            {
                
    highScores.finishedSavingScore(thissuccess);
            }
            ;
            
    __reg3.sendAndLoad(this.__saveScoreURL__reg3"POST");
            
    this.showSavingScore();
        }
    Нас интересует строка:
    PHP:
    com.novelgames.flashgames.highscoresAS2.MD5.md5("" this.__gameID this.playerName this.score this.__get__hashKey());

    __gameID = 3913 //Похожих игр больше 4k
    playerName = 'Ret3' // Наш ник
    score = 1264364//Для первого места хватит


    Разведаем, что выдаёт this.__get__hashKey():
    PHP:
    dynamic class com.novelgames.flashgames.highscoresAS2.HighScores extends mx.core.UIComponent 
    {
        var 
    __hashKey;
        function 
    get hashKey() 
        { 
            return 
    this.__hashKey
        }
    }
    Осталось только узнать, что должно находиться в this.__hashKey;

    Опять же, как ни странно скорее всего это 'superwazooo':
    PHP:
    onClipEvent(construct)
    {
        
    backgroundAlpha 0.5;
        
    backgroundColour 11711154;
        
    dialogAlpha 1;
        
    dialogBorderAlpha 1;
        
    dialogBorderColour 11711154;
        
    dialogBorderThickness 5;
        
    dialogColour 14277081;
        
    gameID 0;
        
    hashKey "superwazooo";
        
    highScoresHighlightColour 16711680;
        
    loadScoresAtStart false;
        
    loadScoresURL "http://www.fupa.com/loadScores.aspx";
        
    maxNoOfScores 50;
        
    nameSWFVariable "";
        
    saveScoreURL "http://www.fupa.com/saveScore.aspx";
        
    enabled true;
        
    visible true;
        
    minHeight 0;
        
    minWidth 0;
    }
    Будем проверять наши догадки, - Играем, набираем очки, получаем с помощью Charles md5.

    ID = 3913
    Name = Asd654
    Score = 1474
    ------
    md5 от Flash = 9f1785cd7c7d132e776792acb2b87bdf
    md5(id+name+score+'superwazooo') = 9f1785cd7c7d132e776792acb2b87bdf


    Осталось только отправить нужный нам запрос, но на этот раз мы будем использовать хэш 7062398f7b519d9b186d59eb8f7a3741. Результат:

    [​IMG]
    Нам всё же удалось занять первое место.
     
    #1 randman, 13 Aug 2012
    Last edited: 13 Aug 2012
    5 people like this.
  2. randman

    randman Members of Antichat

    Joined:
    15 May 2010
    Messages:
    1,366
    Likes Received:
    610
    Reputations:
    1,101

    VI. BODY 2​



    А сейчас я хотел бы опять перейти к неприступным играм на mail.ru, о которых в дальнейшем будет сказано ещё очень много и рассказать о назначении плагина FlashFireBug, предназначенного для AS3, который мы так же будем часто использовать.

    Игра «Покер на костях».

    Покер на костях — азартная игра в кости. В неё могут играть от двух человек и более, оптимальное число игроков*— четыре. Для игры используют 5 шестигранных кубиков с числовыми достоинствами от 1 до 6. Все кости выбрасываются одновременно. Цель игры*— набирать очки за выполнение определённых комбинаций.
    В варианте приведённом на mail.ru бросать кости можно три раза, при этом часть костей можно сохранять, и иметься возможность играть в блиц. Конечно в этой игре многое зависит от вероятности, однако игра есть игра, и нам нужно найти способ в неё выигрывать. Следуя теории вероятности, шанс сохранить к концу раунда 5 костей одного достоинства при 3 бросках значительно меньше чем при >3 бросках, а если мы сможем манипулировать выпадающими значением(1-6), мы будем выигрывать всегда, за исключением случая возникновения аналогичного нечестного игрока, который возможно сможет набрать кол-во очков, превосходящее возможное, а именно число (1 * 5) + (2 * 5) + (3 * 5) + (4 * 5) + (5 * 5) + (6 * 5) + (6 * 5) + (6 * 5) + (6 * 5) + 50 + 20 + 30 = 295

    Перейдём от теории покера к самой игре.


    [​IMG]
    Среднестатистическая партия в покер на костях.

    Запускаем FlashFireBug:
    [​IMG]
    Спешу вас обрадовать - интерфейс у плагина довольно простой и удобный, в главном разделе окна вы можете путешествовать по дереву элементов интерфейсов и редактировать параметры отдельных элементов, хотя не так хорошо, как это реализовано в De Monster Debugger. Иметься возможность выбора элемента щелчком мыши по нему. На сайте разработчика можно купить Pro версию.

    Как видим, мы можем изменять любой элемент интерфейса, в том числе и его параметры. Нас интересует элемент SmallCup, кнопка при нажатии на которую происходит бросок оставленных костей, а точнее её Bool свойство visible, отвечающие за видимость.
    [​IMG]
    А что если на сервере не проверяется кол-во бросков? Надо бы проверить, - бросаем кости 3 раза, вновь активируем кнопку и нажимаем на неё. И действительно, серверная часть не проверяет количество — теперь мы можем бросать кости неограниченное число раз, и выбрасывать любую нужную комбинацию. Теперь всегда можно набирать 295 очков, оставив далеко позади любого честного человека.
    [​IMG]
    А что, если игрок играющий против нас, каким то способом наберёт 295 очков, а вы — меньше, так как время на бросок ограничено, а нужные кости могут и не выпасть. А если он наберёт больше 295 очков? Невозможно, скажите вы? Для нас нет ничего невозможного :D


    В большинстве игр работает очень простой способ проверки поступающих вам данных. Перед их получением вы отключаете сетевое соединение, и пытаетесь их получить. Если они пришли, значит они формируются на вашей стороне, или изначально там хранятся. Пробуем... Перед броском костей выполняем:

    Code:
    sudo ifconfig eth0 down
    
    Бросаем... Значения костей выпадают(Как и при повторном броске), - значит нам нужно всего лишь их подменить, а заодно проверить возможность хранения кости числа больше 6.

    В файле dice-r1.swf, найденном через Charles храниться любезно предоставленный разработчиками класс dice и package diceClasses где скорее всего находиться основная часть кода работы с костями, а именно:

    Функция проверки на комбинацию:
    PHP:
    public static function getAvailableCombos(arg1:Array, arg2:Array):Array
            {
                var 
    loc2:*;
                
    loc2 null;
                var 
    loc3:*;
                
    loc3 0;
                var 
    loc4:*;
                
    loc4 false;
                var 
    loc5:*;
                
    loc5 null;
                var 
    loc6:*;
                
    loc6 0;
                var 
    loc1:*;
                
    loc1 = new Array(12);
                
    diceClasses.rules.Rules.enNullArray(loc1);
                
    loc3 0;
                while (
    loc3 6
                {
                    if (
    arg2[loc3] == -1)
                    {
                        
    loc2 count(arg1loc3 1);
                        
    loc1[loc3] = loc2.count * (loc3 1);
                    }
                    ++
    loc3;
                }
                if (
    arg2[6] == -1)
                {
                    
    loc4 false;
                    
    loc3 0;
                    while (
    loc3 6
                    {
                        
    loc2 count(arg1loc3 1);
                        if (
    loc2.count >= 4)
                        {
                            
    loc4 true;
                            break;
                        }
                        ++
    loc3;
                    }
                    
    loc1[6] = loc4 loc2.count * (loc3 1) + loc2.restSum 0;
                }
                if (
    arg2[7] == -1)
                {
                    
    loc4 false;
                    
    loc3 0;
                    while (
    loc3 6
                    {
                        
    loc2 count(arg1loc3 1);
                        if (
    loc2.count == && loc2.restArr[0] == loc2.restArr[1])
                        {
                            
    loc4 true;
                            break;
                        }
                        ++
    loc3;
                    }
                    
    loc1[7] = loc4 loc2.count * (loc3 1) + loc2.restSum 0;
                }
                if (
    arg2[8] == -1)
                {
                    
    loc4 false;
                    if (!(
    arg1.indexOf(1) == -1) && !(arg1.indexOf(2) == -1) && !(arg1.indexOf(3) == -1) && !(arg1.indexOf(4) == -1) || !(arg1.indexOf(2) == -1) && !(arg1.indexOf(3) == -1) && !(arg1.indexOf(4) == -1) && !(arg1.indexOf(5) == -1) || !(arg1.indexOf(3) == -1) && !(arg1.indexOf(4) == -1) && !(arg1.indexOf(5) == -1) && !(arg1.indexOf(6) == -1))
                    {
                        
    loc4 true;
                    }
                    
    loc1[8] = loc4 25 0;
                }
                if (
    arg2[9] == -1)
                {
                    
    loc4 true;
                    
    loc5 arg1.concat();
                    
    loc5.sort(Array.NUMERIC);
                    
    loc3 1;
                    while (
    loc3 5
                    {
                        if (
    loc5[loc3] != loc5[(loc3 1)] + 1)
                        {
                            
    loc4 false;
                            break;
                        }
                        ++
    loc3;
                    }
                    
    loc1[9] = loc4 30 0;
                }
                if (
    arg2[10] == -1)
                {
                    
    loc4 false;
                    
    loc3 0;
                    while (
    loc3 6
                    {
                        
    loc2 count(arg1loc3 1);
                        if (
    loc2.count == 5)
                        {
                            
    loc4 true;
                            break;
                        }
                        ++
    loc3;
                    }
                    
    loc1[10] = loc4 50 0;
                }
                if (
    arg2[11] == -1)
                {
                    
    loc6 0;
                    
    loc3 0;
                    while (
    loc3 5
                    {
                        
    loc6 loc6 arg1[loc3];
                        ++
    loc3;
                    }
                    
    loc1[11] = loc6;
                }
                return 
    loc1;
            }

    PHP:
    private static function count(combos:Array, combos:int):Object
            
    {
                var 
    loc1:*;
                
    loc1 0;
                var 
    loc2:*;
                
    loc2 0;
                var 
    loc4:*;
                
    loc4 0;
                var 
    loc3:*;
                
    loc3 = [];
                var 
    loc5:*;
                
    loc5 0;
                var 
    loc6:*;
                
    loc6 combos;
                for 
    each (loc4 in loc6)
                {
                    if (
    loc4 == combos)
                    {
                        ++
    loc1;
                        continue;
                    }
                    
    loc2 loc2 loc4;
                    
    loc3.push(loc4);
                }
                return {
    "count":loc1"restSum":loc2"restArr":loc3};
    Класс Cup, в котором находиться очень интересная строка(Можете проверить это сами):

    PHP:
        public class Cup extends mg.display.ReplacingContainer
        
    {
        ***
            public const 
    helperTxt:String="Чашка с г*вном";
        ***
         }
    Класс DiceConfig, который нам обязательно понадобиться:

    PHP:
    package diceClasses 
    {
        public class 
    DiceConfig extends Object
        
    {
            public function 
    DiceConfig()
            {
                
    super();//Уже не раз встречаемся с различными super
                
    return;
            }

            
            {
                
    TIMER_PER_MOVE 60;//время
                
    INTERROUND_DELAY 1500;
            }

            public static const 
    MAX_THROWS:int=3;//Количество бросков

            
    public static const MAX_SLOTS:int=5;

            public static const 
    COMBO_COUNT:int=12;

            public static var 
    TIMER_PER_MOVE:int=60;

            public static var 
    TEST:Boolean;

            public static var 
    INTERROUND_DELAY:int=1500;
        }
    }
    И его P-код:
    PHP:
    getlocal_0
    pushscope
    debug 1 
    "TIMER_PER_MOVE" 0 3
    findproperty m
    [474]"TIMER_PER_MOVE"
    pushbyte 60
    setproperty m
    [474]"TIMER_PER_MOVE"
    debug 1 "INTERROUND_DELAY" 1 4
    findproperty m
    [476]"INTERROUND_DELAY"
    pushshort 1500
    setproperty m
    [476]"INTERROUND_DELAY"
    debug 1 "TEST" 2 5
    debug 1 
    "MAX_SLOTS" 3 6
    findproperty m
    [477]"MAX_SLOTS"
    pushbyte 5
    initproperty m
    [477]"MAX_SLOTS"
    debug 1 "MAX_THROWS" 1 4
    findproperty m
    [473]"MAX_THROWS"
    pushbyte 100
    initproperty m
    [473]"MAX_THROWS"
    debug 1 "COMBO_COUNT" 5 8
    findproperty m
    [478]"COMBO_COUNT"
    pushbyte 12
    initproperty m
    [478]"COMBO_COUNT"
    returnvoid
    [​IMG]

    Как видите, мы подменили MAX_THROWS с 3 на 100, при поставлении больших чисел возможно добавление отрицательного знака, и у вас будет только одна попытка. Сохраняем.... Для подмены текстовых данных на странице можно использовать использовать Charles, и его функцию BreakPoints, позволяющую подменить пакет с данными к серверу и от сервера:
    [​IMG]
    Однако подменять файл таким способом не очень удобно, поэтому мы будем использовать его же функцию Map Local, которая позволяет указать файл на сервере и на локальном диске и автоматически подменять его:
    [​IMG]
    Всё, теперь для броска костей в пределах 100 нам не нужен FlashFireBug. А с функцией генерацией чисел вам предлагается поработать самостоятельно, - в этой статье я это затрагивать не буду, т. к. алгоритм действий очень похожий и игра настолько дырявая, что подобных багов можно расписать ещё очень много.


    VII. End​



    Ну вот собственно первая часть подходит к концу. Почему же я не написал первую и вторую часть одной частью — различных способов и хитростей много, расписать за дин раз это невозможно, да и примеров мало, а без них статья была бы неинтересной. Так же кто то может возразить, - Для чего заносить это в статью, если это было известно и раньше? Отвечаю: Статей с подобным материалом на русском языке я не видел.

    [​IMG]

    До момента выхода второй части вам предоставляется возможность поиска багов в вашем любимом приложении, достаточно лишь отправить мне в ПМ ссылку, или написать в теме, что более предпочтительно. Часть этих приложений войдёт во вторую статью в качестве примеров.

    (с) XAMEHA, 2012. На Mail.ru было отправлено письмо с списком указанных багов.​
     
    #2 randman, 13 Aug 2012
    Last edited: 13 Aug 2012