popss sequence

Тема в разделе "WASM.BEGINNERS", создана пользователем Ahimov, 21 ноя 2024.

  1. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    Приветствую.

    В мануалах IA по инструкции pop SS сказано что она задерживает прерывания до завершения исполнения следующей инструкции:
    Это позволяет обнаружить эмуляцию/трансляцию кода статистически и однозначно.

    Есть ли еще инструкции, которые каким либо образом связаны в их последовательности, подобно popss ?
     
  2. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    Код (ASM):
    1. cmp EAX, 0
    2. jz @f
    Код (ASM):
    1. lock
    2. mov EAX, [mem]
     
  3. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    MaKsIm

    В чем суть мува не пойму, два потока чтение и запись переменной?

    Такую связку можно перемещать в памяти произвольно, а значит изменять. Обнаружить изменение переменной не атомарное?
     
  4. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    В данном примере никакой. Суть в префиксе lock, а вот команда к префиксу просто неудачная. Вы же спрашивали про действия связки команд. Вот вам два примера связывания команд.
    Любые условные переходы и lock. Еще можно вспомнить про rep+команда.
     
  5. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    Отладчик/транслятор/эмулятор перемещают инструкции или увеличивают их количество(в случае эмуляции). Таким образом если подобрать последовательность инструкций в которой они связаны так, что их перемещение изменяет поведение кода, тогда получится обнаружить отладку.

    Ветвления используют только контекст и не привязаны к предыдущим инструкциям.
     
  6. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    А прикол с pop ss/mov sp(esp),value работает только в реальном или однозадачном режимах. Во всех остальных стеков у каждой задачи 4-е и при возникновении отладочного исключения процессор не трогает стек текущего (отличного от 0-го) уровня привилегий и перепрыгивает по вектору прерывания (при необходимости может выполнить даже переключение аппаратной таски (TSS))

    И как вы собрались искать отладчики с этой последовательностью? В DOS это еще реально. А в x86_64 у вас даже ss нету.

    ADD: Вот что об этом говорит Intel SDM
     
    Последнее редактирование: 22 ноя 2024
  7. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    MaKsIm

    В защищенном режиме x32, юзермод так же работает. На счет стека в данном случае не важно, вторая инструкция другой может быть. Это сделано для атомарной загрузки ss:esp, если прерывание обрабатывается на том же уровне привилегий(тогда нет переключения стека).

    IA SDM v2:
    8E /r MOV Sreg,r/m16** 64 Valid CM/LM Valid Move r/m16 to segment register.

    Статистикой по прерываниям. Интересно какие еще есть подобные связки инструкций.
     
  8. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    214
    И вот тут то как раз нестыковка. Обработка отладочных исключений происходит в разных задачах (процессах) т.е. каждое такое исключение принудительно транслируется в другой процесс.

    С другой стороны отладчик может просто заменять инструкцию на int3 и потом просто эмитировать выполнение первой инструкции.
    Вам проще запустить свой же процесс под отладкой другой вашей программы (или той же, но с другими параметрами) и контролировать состояние этой отладки.
     
  9. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    MaKsIm

    Прерывание это не исключение, про последние речи небыло. Аналогично прерываниям можно подавить отладочное исключение, через cpu.flags.RF, так например происходит сервисный возврат.

    Проще да, вот только все процессы и отладчик до кучи запустятся под бинарным транслятором/эмулятором, детектить такое не получится через всякие int3 :scratch_one-s_head:

    Тут нужен принципиально иной подход.
     
  10. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    Эксплойт нашелся CVE-2018-8897:

     

    Вложения:

    alex_dz нравится это.
  11. galenkane

    galenkane Active Member

    Публикаций:
    0
    Регистрация:
    13 янв 2017
    Сообщения:
    415
    Основные инструкции со связанным поведением:

    1. STI (Set Interrupt Flag)
    Код (ASM):
    1. sti
    2. nop ; следующая инструкция выполняется без прерываний
    • Задерживает прерывания на одну инструкцию после выполнения
    • Позволяет атомарно включить прерывания и выполнить критическую операцию
    2. MOV SS (Move to Stack Segment)
    Код (ASM):
    1. mov ss, ax
    2. mov esp, new_stack ; выполняется без прерываний
    • Аналогично
      Код (ASM):
      1. POP SS
      , блокирует прерывания до следующей инструкции
    • Обеспечивает атомарную замену SS:ESP
    3. REP префикс
    Код (ASM):
    1. rep movsb ; префикс связан с командой
    • Создает неделимую связь между префиксом и инструкцией
    • Нельзя разделить префикс и команду
    4. LOCK префикс
    Код (ASM):
    1. lock add [mem], eax ; атомарная операция
    • Обеспечивает атомарность доступа к памяти
    • Префикс неразрывно связан с инструкцией
    5. Условные переходы с предсказанием
    Код (ASM):
    1. cmp eax, 0
    2. je target ; связаны через предсказание переходов
    • Создают связь через механизм предсказания ветвлений
    • Изменение порядка может повлиять на производительность
    6. Последовательности с флагами
    Код (ASM):
    1. pushf ; сохранить флаги
    2. cli ; запретить прерывания ; критическая секция
    3. popf ; восстановить флаги и состояние IF
    7. Инструкции с задержкой исключений
    Код (ASM):
    1. mov ss, ax
    2. int 3 ; может использоваться для CVE-2018-8897
    Код (C++):
    1. #include <iostream>
    2. #include <chrono>
    3. #include <vector>
    4. #include <cstring>
    5. #include <functional>
    6. #include <string>
    7. #include <intrin.h>
    8. #include <cmath>
    9.  
    10. class EmulationDetector {
    11. private:
    12.     static constexpr size_t SMALL_BUFFER = 1024;
    13.     static constexpr size_t MEDIUM_BUFFER = 32768;
    14.     static constexpr size_t LARGE_BUFFER = 1048576;
    15.     static constexpr int ITERATIONS = 1000;
    16.  
    17.     struct TimingProfile {
    18.         double rep_movsb_time;
    19.         double memcpy_time;
    20.         double ratio;
    21.         uint64_t rdtsc_overhead;
    22.     };
    23.  
    24.     uint64_t read_performance_counter(int counter_id) {
    25.         return __rdtsc();
    26.     }
    27.  
    28.     uint64_t precise_timing(std::function<void()> func) {
    29.         uint64_t start, end;
    30.  
    31.         int cpuInfo[4];
    32.         __cpuid(cpuInfo, 0);
    33.         start = __rdtsc();
    34.  
    35.         func();
    36.  
    37.         __cpuid(cpuInfo, 0);
    38.         end = __rdtsc();
    39.  
    40.         return end - start;
    41.     }
    42.  
    43.     void rep_movsb_copy(void* dst, const void* src, size_t size) {
    44.         char* d = static_cast<char*>(dst);
    45.         const char* s = static_cast<const char*>(src);
    46.         size_t sz = size;
    47.  
    48.         __asm {
    49.             push esi
    50.             push edi
    51.             push ecx
    52.  
    53.             mov edi, d
    54.             mov esi, s
    55.             mov ecx, sz
    56.             rep movsb
    57.  
    58.             pop ecx
    59.             pop edi
    60.             pop esi
    61.         }
    62.     }
    63.  
    64.     void rep_movsb_copy_intrinsic(void* dst, const void* src, size_t size) {
    65.         char* d = static_cast<char*>(dst);
    66.         const char* s = static_cast<const char*>(src);
    67.  
    68.         for (size_t i = 0; i < size; ++i) {
    69.             d[i] = s[i];
    70.         }
    71.     }
    72.  
    73.     void enhanced_rep_movsb_copy(void* dst, const void* src, size_t size) {
    74.         try {
    75.             rep_movsb_copy(dst, src, size);
    76.         }
    77.         catch (...) {
    78.             rep_movsb_copy_intrinsic(dst, src, size);
    79.         }
    80.     }
    81.  
    82.     TimingProfile profile_rep_movsb(size_t buffer_size) {
    83.         std::vector<char> src(buffer_size, 0xAA);
    84.         std::vector<char> dst(buffer_size, 0x00);
    85.  
    86.         TimingProfile profile = {};
    87.  
    88.         uint64_t rep_movsb_total = 0;
    89.         for (int i = 0; i < ITERATIONS; ++i) {
    90.             rep_movsb_total += precise_timing([&]() {
    91.                 enhanced_rep_movsb_copy(dst.data(), src.data(), buffer_size);
    92.                 });
    93.         }
    94.         profile.rep_movsb_time = static_cast<double>(rep_movsb_total) / ITERATIONS;
    95.  
    96.         uint64_t memcpy_total = 0;
    97.         for (int i = 0; i < ITERATIONS; ++i) {
    98.             memcpy_total += precise_timing([&]() {
    99.                 memcpy(dst.data(), src.data(), buffer_size);
    100.                 });
    101.         }
    102.         profile.memcpy_time = static_cast<double>(memcpy_total) / ITERATIONS;
    103.  
    104.         profile.ratio = profile.rep_movsb_time / profile.memcpy_time;
    105.  
    106.         uint64_t rdtsc_overhead = 0;
    107.         for (int i = 0; i < 100; ++i) {
    108.             rdtsc_overhead += precise_timing([]() {
    109.                 // Empty function to measure timing overhead
    110.                 });
    111.         }
    112.         profile.rdtsc_overhead = rdtsc_overhead / 100;
    113.  
    114.         return profile;
    115.     }
    116.  
    117.     bool test_microarchitectural_behavior() {
    118.         int cpuInfo[4];
    119.         __cpuidex(cpuInfo, 7, 0);
    120.         bool ermsb_supported = (cpuInfo[1] & (1 << 9)) != 0;
    121.  
    122.         if (!ermsb_supported) {
    123.             return true; // Assume hardware if ERMSB not supported
    124.         }
    125.  
    126.         std::vector<char> buffer(4096 + 64);
    127.         char* aligned_ptr = reinterpret_cast<char*>(
    128.             (reinterpret_cast<uintptr_t>(buffer.data()) + 63) & ~63
    129.             );
    130.         char* unaligned_ptr = aligned_ptr + 1;
    131.  
    132.         uint64_t aligned_time = precise_timing([&]() {
    133.             enhanced_rep_movsb_copy(aligned_ptr + 2048, aligned_ptr, 2048);
    134.             });
    135.  
    136.         uint64_t unaligned_time = precise_timing([&]() {
    137.             enhanced_rep_movsb_copy(unaligned_ptr + 2048, unaligned_ptr, 2048);
    138.             });
    139.  
    140.         if (aligned_time == 0) return true; // Avoid division by zero
    141.  
    142.         double alignment_ratio = static_cast<double>(unaligned_time) / aligned_time;
    143.  
    144.         // Real hardware typically shows 1.2-2.0x difference
    145.         // VMs often show less sensitivity (closer to 1.0)
    146.         return alignment_ratio > 1.15 && alignment_ratio < 3.0;
    147.     }
    148.  
    149.     bool detect_via_performance_counters() {
    150.         uint64_t start_cycles = __rdtsc();
    151.         uint64_t start_instructions = read_performance_counter(0);
    152.  
    153.         volatile int dummy = 0;
    154.         for (int i = 0; i < 10000; ++i) {
    155.             dummy += i;
    156.         }
    157.  
    158.         uint64_t end_cycles = __rdtsc();
    159.         uint64_t end_instructions = read_performance_counter(0);
    160.  
    161.         if (end_cycles <= start_cycles) return false;
    162.  
    163.         double ipc = static_cast<double>(end_instructions - start_instructions) /
    164.             (end_cycles - start_cycles);
    165.  
    166.         return ipc > 0.1 && ipc < 4.0;
    167.     }
    168.  
    169.     bool detect_via_timing_patterns() {
    170.         std::vector<uint64_t> timings;
    171.  
    172.         for (int i = 0; i < 100; ++i) {
    173.             uint64_t timing = precise_timing([&]() {
    174.                 volatile int temp = 0;
    175.                 for (int j = 0; j < 1000; ++j) {
    176.                     temp += j;
    177.                 }
    178.                 });
    179.             timings.push_back(timing);
    180.         }
    181.  
    182.         double mean = 0.0;
    183.         for (uint64_t t : timings) {
    184.             mean += t;
    185.         }
    186.         mean /= timings.size();
    187.  
    188.         if (mean == 0) return false;
    189.  
    190.         double variance = 0.0;
    191.         for (uint64_t t : timings) {
    192.             variance += (t - mean) * (t - mean);
    193.         }
    194.         variance /= timings.size();
    195.  
    196.         double coefficient_of_variation = sqrt(variance) / mean;
    197.  
    198.         // Real hardware: CV usually 0.1-0.5
    199.         // VMs: CV often < 0.1 (too consistent) or > 0.6 (too variable)
    200.         return coefficient_of_variation > 0.05 && coefficient_of_variation < 0.6;
    201.     }
    202.  
    203. public:
    204.     struct DetectionResult {
    205.         bool is_emulated;
    206.         double confidence;
    207.         std::string detection_method;
    208.         std::vector<std::string> indicators;
    209.     };
    210.  
    211.     DetectionResult detect_emulation() {
    212.         DetectionResult result = {};
    213.         result.is_emulated = false;
    214.         result.confidence = 0.0;
    215.         std::vector<std::string> indicators;
    216.  
    217.         // Get timing profiles
    218.         auto small_profile = profile_rep_movsb(SMALL_BUFFER);
    219.         auto medium_profile = profile_rep_movsb(MEDIUM_BUFFER);
    220.         auto large_profile = profile_rep_movsb(LARGE_BUFFER);
    221.  
    222.         // **MAIN DETECTION VECTOR: RDTSC Overhead**
    223.         // This is the strongest indicator based on your data
    224.         double avg_rdtsc_overhead = (small_profile.rdtsc_overhead +
    225.             medium_profile.rdtsc_overhead +
    226.             large_profile.rdtsc_overhead) / 3.0;
    227.  
    228.         if (avg_rdtsc_overhead > 5000) {
    229.             indicators.push_back("Very high RDTSC overhead (virtualization detected)");
    230.             result.confidence += 0.8; // Very strong indicator
    231.         }
    232.         else if (avg_rdtsc_overhead > 1000) {
    233.             indicators.push_back("High RDTSC overhead (possible emulation)");
    234.             result.confidence += 0.4;
    235.         }
    236.  
    237.         // **TIMING RATIO ANALYSIS**
    238.         // Hardware typically shows ratios around 0.95-1.05
    239.         double avg_ratio = (small_profile.ratio + medium_profile.ratio + large_profile.ratio) / 3.0;
    240.  
    241.         if (avg_ratio > 1.5 || avg_ratio < 0.3) {
    242.             indicators.push_back("Abnormal REP MOVSB/memcpy performance ratio");
    243.             result.confidence += 0.3;
    244.         }
    245.  
    246.         // **CACHE SCALING ANALYSIS**
    247.         if (small_profile.rep_movsb_time > 0) {
    248.             double cache_scaling = large_profile.rep_movsb_time / small_profile.rep_movsb_time;
    249.  
    250.             // Real hardware: ~500-1000x scaling from L1 to RAM
    251.             // VMs: often different scaling patterns
    252.             if (cache_scaling < 100 || cache_scaling > 2000) {
    253.                 indicators.push_back("Abnormal cache hierarchy scaling");
    254.                 result.confidence += 0.2;
    255.             }
    256.         }
    257.  
    258.         // **ABSOLUTE TIMING ANALYSIS**
    259.         // VMs often show much higher absolute timings
    260.         if (small_profile.rep_movsb_time > 10000) {
    261.             indicators.push_back("Extremely high absolute timing values");
    262.             result.confidence += 0.3;
    263.         }
    264.  
    265.         // **MICROARCHITECTURAL BEHAVIOR**
    266.         if (!test_microarchitectural_behavior()) {
    267.             indicators.push_back("Abnormal alignment sensitivity");
    268.             result.confidence += 0.15;
    269.         }
    270.  
    271.         // **TIMING CONSISTENCY**
    272.         if (!detect_via_timing_patterns()) {
    273.             indicators.push_back("Abnormal timing variance patterns");
    274.             result.confidence += 0.1;
    275.         }
    276.  
    277.         // **FINAL DETERMINATION**
    278.         result.is_emulated = result.confidence > 0.6;
    279.         result.indicators = indicators;
    280.  
    281.         if (result.is_emulated) {
    282.             result.detection_method = "Multi-vector timing analysis (Primary: RDTSC overhead)";
    283.         }
    284.         else {
    285.             result.detection_method = "Hardware execution detected";
    286.         }
    287.  
    288.         return result;
    289.     }
    290.  
    291.     void print_detailed_analysis() {
    292.         std::cout << "=== Enhanced REP MOVSB Emulation Detection Analysis ===" << std::endl;
    293.  
    294.         auto result = detect_emulation();
    295.  
    296.         std::cout << "Detection Result: " << (result.is_emulated ? "EMULATED/VIRTUALIZED" : "HARDWARE") << std::endl;
    297.         std::cout << "Confidence: " << (result.confidence * 100) << "%" << std::endl;
    298.         std::cout << "Method: " << result.detection_method << std::endl;
    299.  
    300.         if (!result.indicators.empty()) {
    301.             std::cout << "\nDetection Indicators:" << std::endl;
    302.             for (const auto& indicator : result.indicators) {
    303.                 std::cout << "  - " << indicator << std::endl;
    304.             }
    305.         }
    306.  
    307.         // Print detailed timing profiles
    308.         std::cout << "\n=== Detailed Timing Analysis ===" << std::endl;
    309.  
    310.         auto small_profile = profile_rep_movsb(SMALL_BUFFER);
    311.         auto medium_profile = profile_rep_movsb(MEDIUM_BUFFER);
    312.         auto large_profile = profile_rep_movsb(LARGE_BUFFER);
    313.  
    314.         std::cout << "Small Buffer (1KB):" << std::endl;
    315.         std::cout << "  REP MOVSB: " << small_profile.rep_movsb_time << " cycles" << std::endl;
    316.         std::cout << "  memcpy: " << small_profile.memcpy_time << " cycles" << std::endl;
    317.         std::cout << "  Ratio: " << small_profile.ratio << std::endl;
    318.         std::cout << "  RDTSC overhead: " << small_profile.rdtsc_overhead << " cycles" << std::endl;
    319.  
    320.         std::cout << "Medium Buffer (32KB):" << std::endl;
    321.         std::cout << "  REP MOVSB: " << medium_profile.rep_movsb_time << " cycles" << std::endl;
    322.         std::cout << "  memcpy: " << medium_profile.memcpy_time << " cycles" << std::endl;
    323.         std::cout << "  Ratio: " << medium_profile.ratio << std::endl;
    324.         std::cout << "  RDTSC overhead: " << medium_profile.rdtsc_overhead << " cycles" << std::endl;
    325.  
    326.         std::cout << "Large Buffer (1MB):" << std::endl;
    327.         std::cout << "  REP MOVSB: " << large_profile.rep_movsb_time << " cycles" << std::endl;
    328.         std::cout << "  memcpy: " << large_profile.memcpy_time << " cycles" << std::endl;
    329.         std::cout << "  Ratio: " << large_profile.ratio << std::endl;
    330.         std::cout << "  RDTSC overhead: " << large_profile.rdtsc_overhead << " cycles" << std::endl;
    331.  
    332.         // Additional analysis
    333.         double avg_rdtsc_overhead = (small_profile.rdtsc_overhead +
    334.             medium_profile.rdtsc_overhead +
    335.             large_profile.rdtsc_overhead) / 3.0;
    336.  
    337.         std::cout << "\n=== Key Metrics ===" << std::endl;
    338.         std::cout << "Average RDTSC overhead: " << avg_rdtsc_overhead << " cycles" << std::endl;
    339.  
    340.         if (small_profile.rep_movsb_time > 0) {
    341.             double cache_scaling = large_profile.rep_movsb_time / small_profile.rep_movsb_time;
    342.             std::cout << "Cache scaling factor: " << cache_scaling << "x" << std::endl;
    343.         }
    344.  
    345.         std::cout << "\n=== Interpretation ===" << std::endl;
    346.         std::cout << "RDTSC overhead < 1000: Likely hardware" << std::endl;
    347.         std::cout << "RDTSC overhead > 5000: Likely VM/emulation" << std::endl;
    348.         std::cout << "Cache scaling 500-1000x: Typical for hardware" << std::endl;
    349.     }
    350. };
    351.  
    352. int main() {
    353.     EmulationDetector detector;
    354.     detector.print_detailed_analysis();
    355.  
    356.     return 0;
    357. }


    вроде как на vmware работает детект

     
    Ahimov нравится это.
  12. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    galenkane

    ia sdm 3c, там где про vmx:

    Наверно тайминг должен отличаться на вирте. Если попробовать посмотреть за прерываниями, таким образом:
    Код (ASM):
    1.    mov edx, 3   ; rpl(3) -> IRET -> !rpl
    2.    mov ax, ss
    3.    mov es, dx
    4. wait_for_int:
    5.    mov cx, es
    6.    jecxz int_loop
    7.    jmp wait_for_int
    8. int_loop:
    9. ;  accum of stat's..
    10.    mov es, dx
    11.    mov ss, ax
    12.    mov cx, es
    13.    jecxz inc_int_count
    Изначально планировалось использовать это, для наблюдения за прерываниями.
    Статистику по пркрываниям можно собрать системным путем используя профайлер(NtStartProfile). Это хороший способ получить покрытие кода. Но есть некоторые сложности из за выравнивания счетчиков(не решено).
    --- Сообщение объединено, 7 июл 2025 ---
    QUOTE]Основные инструкции со связанным поведением [/QUOTE]

    8. SERIALIZING INSTRUCTIONS
     

    Вложения:

    • sl.pdf
      Размер файла:
      156,9 КБ
      Просмотров:
      310
    • cracklab.team_ cv.7z
      Размер файла:
      710 КБ
      Просмотров:
      317
  13. galenkane

    galenkane Active Member

    Публикаций:
    0
    Регистрация:
    13 янв 2017
    Сообщения:
    415
    [TEST] CPUID Latency Analysis...
    -> CPUID cycles: 14257150
    -> Baseline cycles: 18206
    -> Ratio: 783.10x
    ANOMALY: CPUID extremely slow - VM exits detected
    --- Сообщение объединено, 7 июл 2025 ---
    Для комьюнити стоило б собрать все наработки и нормально их задокументировать, а то валяется по архивчикам и долго искать сюда-туда.
     

    Вложения:

    • vmx_detect.zip
      Размер файла:
      82,6 КБ
      Просмотров:
      325
    • build_minimal.zip
      Размер файла:
      77,5 КБ
      Просмотров:
      324
    Ahimov и Research нравится это.
  14. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    galenkane

    Как это работает TestSTIBlocking() -> CLI ?

    cli/sti в юзермоде :swoon:
     
  15. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    611
    Эти инструкции привилегированные — то есть их нельзя вызывать из пользовательского режима (Ring 3). Если ты попытаешься — получишь #GP (General Protection Fault).
     
  16. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    Есть ошибки, из за которых семпл не может работать. С cli/sti допустим может PVI, но такое:
    Код (ASM):
    1. TestMovSSBlocking:
    2.     mov ax, ss
    3.     rdtscp
    4.     mov rsi, rax
    5.     mov rdi, rdx
    6.     mov ss, ax
    Это на необработанном фаулте упадет. Похоже семпл не проверен после сборки :)
     
  17. galenkane

    galenkane Active Member

    Публикаций:
    0
    Регистрация:
    13 янв 2017
    Сообщения:
    415
  18. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    Код (ASM):
    1. ; fasm-код для Windows User Mode (PE-файл)
    2. format PE console
    3. entry start
    4.  
    5. section '.text' code readable executable
    6.  
    7. start:
    8.     ; --- Инициализация ---
    9.  
    10. ; 1. Сохраняем текущий селектор SS для использования в тесте
    11.     MOV [STACK_SEL], SS
    12.  
    13.     ; 2. NULL-селектор: 3 (RPL=3)
    14.     MOV [DS_NULL_SEL], 3
    15.  
    16.     ; 3. Количество итераций для усреднения
    17.     MOV [ITERATIONS], 10000
    18.  
    19.     ; 4. Обнуление суммарных счетчиков
    20.     XOR EAX, EAX
    21.     MOV [REF_TOTAL_COUNT], EAX
    22.     MOV [TEST_TOTAL_COUNT], EAX
    23.  
    24. ; =================================================================
    25.     ; 1. ЭТАЛОННЫЙ ЦИКЛ (Без маскировки SS)
    26.     ; =================================================================
    27.  
    28.     MOV ECX, [ITERATIONS] ; Счетчик итераций
    29.  
    30. REF_INIT:
    31.     PUSH ECX
    32.  
    33.     MOVZX EBX, word [DS_NULL_SEL] ; EBX = 3
    34.     MOV DS, BX                    ; Активация триггера: DS = 3
    35.     XOR EAX, EAX                  ; EAX = Счет инструкций (сброс в 0)
    36.  
    37. REF_WAIT_LOOP:
    38.     INC EAX                       ; 1. Считаем инструкцию
    39.  
    40. MOVZX EBX, DS                 ; 2. Чтение DS
    41.     TEST EBX, EBX                 ; 3. Проверка: DS сброшен до 0?
    42.     JNZ REF_WAIT_LOOP             ; 4. Если нет, ждем IRET
    43.  
    44.     ; IRET сработал
    45.     ADD [REF_TOTAL_COUNT], EAX    ; Добавляем результат к сумме
    46.  
    47.     POP ECX
    48.     LOOP REF_INIT
    49.  
    50. ; =================================================================
    51.     ; 2. ТЕСТИРУЕМЫЙ ЦИКЛ (С маскировкой SS)
    52.     ; =================================================================
    53.  
    54.     MOV ECX, [ITERATIONS] ; Счетчик итераций
    55.  
    56. TEST_INIT:
    57.     PUSH ECX
    58.  
    59.     MOVZX EBX, word [DS_NULL_SEL] ; EBX = 3
    60.     MOV DS, BX                    ; Активация триггера: DS = 3
    61.     XOR EAX, EAX                  ; EAX = Счет инструкций (сброс в 0)
    62.  
    63.     MOVZX EDI, word [STACK_SEL]   ; EDI = Текущий селектор SS
    64.  
    65. TEST_WAIT_LOOP:
    66.     INC EAX                       ; 1. Считаем инструкцию
    67.  
    68.     MOV SS, DI                    ; 2. Загрузка SS (Блокирует INT на 1 инструкцию)
    69.  
    70.     MOVZX EBX, DS                 ; 3. Чтение DS (Атомарно с MOV SS)
    71.     TEST EBX, EBX                 ; 4. Проверка: DS сброшен до 0?
    72.     JNZ TEST_WAIT_LOOP            ; 5. Если нет, ждем IRET
    73.  
    74.     ; IRET сработал
    75.     ADD [TEST_TOTAL_COUNT], EAX   ; Добавляем результат к сумме
    76.  
    77.     POP ECX
    78.     LOOP TEST_INIT
    79.  
    80. ; =================================================================
    81.     ; Вывод результатов (Для простоты оставим их в памяти)
    82.     ; =================================================================
    83.  
    84.     ; После выполнения:
    85.     ; REF_TOTAL_COUNT   - Суммарное количество инструкций в эталоне
    86.     ; TEST_TOTAL_COUNT  - Суммарное количество инструкций с маскировкой SS
    87.  
    88.     ; Выход из программы
    89.     PUSH 0
    90.     CALL [ExitProcess]
    91.  
    92. section '.data' data readable writable
    93.     DS_NULL_SEL         dw 0
    94.     STACK_SEL           dw 0
    95.     ITERATIONS          dd 0
    96.     REF_TOTAL_COUNT     dd 0
    97.     TEST_TOTAL_COUNT    dd 0
    98.  
    99. section '.idata' import data readable writeable
    100.     library kernel32, 'kernel32.dll'
    101.     import kernel32, ExitProcess, 'ExitProcess'
    102.  
    --- Сообщение объединено, 30 ноя 2025 ---
    Код (ASM):
    1. ; fasm-код для Windows User Mode (PE-файл)
    2. format PE console
    3. entry start
    4.  
    5. section '.text' code readable executable
    6.  
    7. start:
    8.     ; --- Инициализация ---
    9.  
    10.     ; 1. Сохраняем текущий селектор SS
    11.     MOV [STACK_SEL], SS
    12.  
    13. ; 2. NULL-селектор: 3 (RPL=3)
    14.     MOV [DS_NULL_SEL], 3
    15.  
    16.     ; 3. Обнуление суммарных счетчиков TSC (64-битные)
    17.     XOR EAX, EAX
    18.     MOV [REF_TOTAL_TSC_LO], EAX
    19.     MOV [REF_TOTAL_TSC_HI], EAX
    20.     MOV [TEST_TOTAL_TSC_LO], EAX
    21.     MOV [TEST_TOTAL_TSC_HI], EAX
    22.  
    23.     ; 4. Установка целевого времени (100 мс)
    24.     CALL [GetTickCount]
    25.     ADD EAX, 100
    26.     MOV [END_TIME_MS], EAX
    27.  
    28. ; =================================================================
    29.     ; 1. ЭТАЛОННЫЙ ЦИКЛ (Без маскировки SS) - Выполняем до END_TIME_MS
    30.     ; =================================================================
    31.  
    32. REF_MAIN_LOOP:
    33.     ; Проверка времени
    34.     CALL [GetTickCount]
    35.     CMP EAX, [END_TIME_MS]
    36.     JAE REF_END                     ; Если время вышло (A > B или A = B), выходим
    37.  
    38.     ; --- Начало одной итерации измерения ---
    39.  
    40.     MOVZX EBX, word [DS_NULL_SEL]
    41.     MOV DS, BX
    42.  
    43. ; Чтение TSC ДО IRET-события
    44.     RDTSC                        
    45.     MOV [TSC_START_LO], EAX
    46.     MOV [TSC_START_HI], EDX
    47.  
    48. REF_WAIT_LOOP:
    49.     ; Ждем сброса DS
    50.     MOVZX EBX, DS
    51.     TEST EBX, EBX
    52.     JNZ REF_WAIT_LOOP
    53.  
    54.     ; IRET сработал
    55.  
    56.     ; Чтение TSC ПОСЛЕ IRET-события
    57.     RDTSC
    58.  
    59. MOV [TSC_END_LO], EAX
    60.     MOV [TSC_END_HI], EDX
    61.  
    62.     ; --- Расчет дельты TSC и суммирование ---
    63.     CALL TSC_DELTA_ADD
    64.     ; TSC_DELTA_ADD суммирует разницу TSC в REF_TOTAL_TSC_...
    65.  
    66.     JMP REF_MAIN_LOOP
    67.  
    68. REF_END:
    69.     ; Сброс счетчика времени для следующего теста
    70.     CALL [GetTickCount]
    71.     ADD EAX, 100
    72.     MOV [END_TIME_MS], EAX
    73.  
    74. ; =================================================================
    75.     ; 2. ТЕСТИРУЕМЫЙ ЦИКЛ (С маскировкой SS) - Выполняем до END_TIME_MS
    76.     ; =================================================================
    77.  
    78. TEST_MAIN_LOOP:
    79.     ; Проверка времени
    80.     CALL [GetTickCount]
    81.     CMP EAX, [END_TIME_MS]
    82.     JAE TEST_END                    ; Если время вышло, выходим
    83.  
    84.     ; --- Начало одной итерации измерения ---
    85.  
    86.     MOVZX EBX, word [DS_NULL_SEL]
    87.     MOV DS, BX
    88.  
    89. ; Чтение TSC ДО IRET-события
    90.     RDTSC                        
    91.     MOV [TSC_START_LO], EAX
    92.     MOV [TSC_START_HI], EDX
    93.  
    94.     MOVZX EDI, word [STACK_SEL]     ; EDI = Текущий селектор SS
    95.  
    96. TEST_WAIT_LOOP:
    97.     MOV SS, DI                      ; Загрузка SS (Маскирует INT на 1 инструкцию)
    98.  
    99.     ; Ждем сброса DS
    100.     MOVZX EBX, DS
    101.     TEST EBX, EBX
    102.     JNZ TEST_WAIT_LOOP
    103.  
    104. ; IRET сработал
    105.  
    106.     ; Чтение TSC ПОСЛЕ IRET-события
    107.     RDTSC
    108.     MOV [TSC_END_LO], EAX
    109.     MOV [TSC_END_HI], EDX
    110.  
    111.     ; --- Расчет дельты TSC и суммирование ---
    112.     CALL TSC_DELTA_ADD_TEST
    113.  
    114.     JMP TEST_MAIN_LOOP
    115.  
    116. TEST_END:
    117.  
    118. ; =================================================================
    119.     ; 3. РАСЧЕТ ОТНОШЕНИЯ (TEST / REF)
    120.     ; =================================================================
    121.  
    122.     ; Для простоты оставим результаты в 64-битных переменных:
    123.     ; REF_TOTAL_TSC_HI:LO - Суммарный TSC эталона
    124.     ; TEST_TOTAL_TSC_HI:LO - Суммарный TSC теста
    125.  
    126.     ; (Расчет 64-битного деления здесь опущен из-за сложности,
    127.     ; но принцип остается: Ratio = TEST_TOTAL_TSC / REF_TOTAL_TSC)
    128.  
    129.     PUSH 0
    130.     CALL [ExitProcess]
    131.  
    132. ; --- Подпрограммы ---
    133. section '.text' code readable executable
    134. ; Подпрограмма для расчета дельты TSC (TSC_END - TSC_START) и суммирования в REF_TOTAL_TSC
    135. ; Использует 64-битное вычитание и сложение.
    136. TSC_DELTA_ADD:
    137.     ; Вычитание (TSC_END - TSC_START)
    138.     PUSHAD
    139.     MOV EAX, [TSC_END_LO]
    140.     SUB EAX, [TSC_START_LO]
    141.     MOV EDX, [TSC_END_HI]
    142.     SBB EDX, [TSC_START_HI] ; SBB учитывает заем из EAX
    143.  
    144. ; Суммирование в REF_TOTAL_TSC
    145.     ADD [REF_TOTAL_TSC_LO], EAX
    146.     ADC [REF_TOTAL_TSC_HI], EDX ; ADC учитывает перенос из LO
    147.  
    148.     POPAD
    149.     RET
    150.  
    151. ; Подпрограмма для расчета дельты TSC (TSC_END - TSC_START) и суммирования в TEST_TOTAL_TSC
    152. TSC_DELTA_ADD_TEST:
    153.     ; Вычитание (TSC_END - TSC_START)
    154.     PUSHAD
    155.     MOV EAX, [TSC_END_LO]
    156.     SUB EAX, [TSC_START_LO]
    157.     MOV EDX, [TSC_END_HI]
    158.     SBB EDX, [TSC_START_HI]
    159.  
    160. ; Суммирование в TEST_TOTAL_TSC
    161.     ADD [TEST_TOTAL_TSC_LO], EAX
    162.     ADC [TEST_TOTAL_TSC_HI], EDX
    163.  
    164.     POPAD
    165.     RET
    166.  
    167. section '.data' data readable writable
    168.     DS_NULL_SEL         dw 0
    169.     STACK_SEL           dw 0
    170.     END_TIME_MS         dd 0
    171.  
    172.     ; 64-битные счетчики для суммарного времени TSC
    173.     REF_TOTAL_TSC_LO    dd 0
    174.     REF_TOTAL_TSC_HI    dd 0
    175. TEST_TOTAL_TSC_LO   dd 0
    176.     TEST_TOTAL_TSC_HI   dd 0
    177.  
    178.     ; Переменные для хранения TSC (64-бит)
    179.     TSC_START_LO        dd 0
    180.     TSC_START_HI        dd 0
    181.     TSC_END_LO          dd 0
    182.     TSC_END_HI          dd 0
    183. section '.idata' import data readable writeable
    184.     library kernel32, 'kernel32.dll'
    185.     import kernel32, ExitProcess, 'ExitProcess', GetTickCount, 'GetTickCount'
    186.  
    --- Сообщение объединено, 30 ноя 2025 ---
    sdm, mov:

    Код (Text):
    1. IF DS, ES, FS, or GS is loaded with non-NULL selector
    2. THEN
    3. IF segment selector index is outside descriptor table limits
    4. OR segment is not a data or readable code segment
    5. OR ((segment is a data or nonconforming code segment) AND ((RPL > DPL) or (CPL > DPL)))
    6. THEN #GP(selector); FI;
    7. IF segment not marked present
    8. THEN #NP(selector);
    9. ELSE
    10. SegmentRegister := segment selector;
    11. SegmentRegister := segment descriptor; FI;
    12. FI;
    13. IF DS, ES, FS, or GS is loaded with NULL selector
    14. THEN
    15. SegmentRegister := segment selector;
    16. SegmentRegister := segment descriptor;
    17. FI;
    iret:
    Код (Text):
    1. FOR each SegReg in (ES, FS, GS, and DS)
    2. DO
    3. tempDesc := descriptor cache for SegReg (* hidden part of segment register *)
    4. IF (SegmentSelector == NULL) OR (tempDesc(DPL) < CPL AND tempDesc(Type) is (data or non-conforming code)))
    5. THEN (* Segment register invalid *)
    6. SegmentSelector := 0; (*Segment selector becomes null*)
    7. FI;
    8. OD;
    - если это обьяснить, начинает норм думать, иначе тупит.

    Код (Text):
    1. ; ... (Начальная инициализация, REF_TOTAL_TSC, TEST_TOTAL_TSC) ...
    2.  
    3. ; --- Дополнительная инициализация ---
    4. MOV [DS_NULL_SEL], 3
    5. MOV [FS_UNIQUE_SEL], DS ; Сохраняем текущий селектор DS как уникальный маркер FS
    6. MOV [ITERATIONS], 1000000
    7. ; ...
    8.  
    9. ; =================================================================
    10. ; 2. ТЕСТИРУЕМЫЙ ЦИКЛ (С маскировкой SS и учетом свапа)
    11. ; =================================================================
    12.  
    13. MOV ECX, [ITERATIONS]
    14.  
    15. TEST_INIT:
    16.     PUSH ECX
    17.  
    18.     MOVZX EBX, word [DS_NULL_SEL]
    19.     MOV DS, BX                    ; DS = 3 (Триггер IRET-сброса)
    20.  
    21.     MOVZX EBX, word [FS_UNIQUE_SEL]
    22.     MOV FS, BX
    23.  
    24. ; Чтение TSC ДО IRET-события
    25.     RDTSC                        
    26.     MOV [TSC_START_LO], EAX
    27.     MOV [TSC_START_HI], EDX
    28.  
    29.     MOVZX EDI, word [STACK_SEL]
    30.     MOVZX ESI, word [FS_UNIQUE_SEL] ; ESI = исходное значение FS
    31.  
    32. TEST_WAIT_LOOP:
    33.     MOV SS, DI
    34.  
    35. ; 1. Проверка DS: Ждем IRET-сброса
    36.     MOVZX EBX, DS
    37.     TEST EBX, EBX
    38.     JNZ TEST_WAIT_LOOP              ; Если DS != 0, ждем
    39.  
    40.     ; --- DS сброшен до 0: IRET или СВАП произошел ---
    41.  
    42.     ; 2. Проверка FS: Детектирование свапа контекста
    43.     MOVZX EAX, FS                   ; Читаем текущее значение FS
    44.     CMP EAX, ESI                    ; Сравниваем с исходным маркером (FS_UNIQUE_SEL)
    45.     JNE CONTEXT_SWAP_DETECTED       ; Если FS изменился, возможно, был свап!
    46.  
    47.     ; --- СВАПА НЕТ, СЧИТАЕМ РЕЗУЛЬТАТ ---
    48.  
    49.     RDTSC
    50.     MOV [TSC_END_LO], EAX
    51.     MOV [TSC_END_HI], EDX
    52.     CALL TSC_DELTA_ADD_TEST         ; Суммируем TSC
    53.     JMP CONTINUE_TEST
    54.  
    55. CONTEXT_SWAP_DETECTED:
    56.     ; Если произошел свап, мы пропускаем это измерение.
    57.     ; Это событие, где шум планировщика полностью доминировал.
    58.     NOP ; Пропускаем TSC_DELTA_ADD_TEST
    59.  
    60. CONTINUE_TEST:
    61.     POP ECX
    62.     LOOP TEST_INIT
    63.     ; ... (Дальнейший код)
     
  19. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    406
    Код (Text):
    1. ; ... (Инициализация и т.д.) ...
    2.    
    3.     ; =================================================================
    4.     ; 2. ТЕСТИРУЕМЫЙ ЦИКЛ (Измерение серией на одном кванте)
    5.     ; =================================================================
    6.    
    7.     MOV ECX, [ITERATIONS] ; Общее число требуемых IRET-событий
    8.    
    9. TEST_START:
    10.     PUSH ECX ; Сохраняем общий счетчик
    11.    
    12.     ; *** 1. Стимуляция начала кванта ***
    13.     PUSH 0
    14.     CALL [Sleep] ; Добровольно отдаем процессор. После возврата - новый квант.
    15. ; 2. Инициализация маркера FS (FS_UNIQUE_SEL)
    16.     MOVZX EBX, word [FS_UNIQUE_SEL]
    17.     MOV FS, BX
    18.     MOV ESI, EBX ; ESI = Исходный маркер FS
    19.    
    20. SERIES_LOOP:
    21.    
    22.     ; 3. Проверка оставшихся итераций
    23.     TEST ECX, ECX
    24.     JZ TEST_END_ALL
    25.    
    26.     ; --- Начало замера одиночного IRET ---
    27.    
    28.     MOVZX EBX, word [DS_NULL_SEL]
    29.     MOV DS, BX
    30.  
    31. RDTSC                           ; TSC_START
    32.     MOV [TSC_START_LO], EAX
    33.     MOV [TSC_START_HI], EDX
    34.    
    35. WAIT_IRET_LOOP:
    36.    
    37.     MOVZX EDI, word [STACK_SEL]
    38.     MOV SS, DI                      ; Маскировка INT
    39.    
    40.     ; Проверка DS: Ждем IRET-сброса
    41.     MOVZX EBX, DS
    42.     TEST EBX, EBX
    43.     JNZ WAIT_IRET_LOOP
    44.  
    45. ; --- IRET сработал ---
    46.    
    47.     ; 4. Проверка FS: Детектирование свапа контекста
    48.     MOVZX EAX, FS                  
    49.     CMP EAX, ESI                  
    50.     JNE CONTEXT_SWAP_TERMINATED     ; Если FS изменился, квант кончился!
    51.    
    52.     ; --- Свапа нет, это чистое измерение ---
    53.     RDTSC
    54.     MOV [TSC_END_LO], EAX
    55.     MOV [TSC_END_HI], EDX
    56.     CALL TSC_DELTA_ADD_TEST         ; Суммируем TSC
    57.    
    58.     DEC ECX                         ; Успешно посчитали одно событие IRET
    59.     JMP SERIES_LOOP
    60.  
    61. CONTEXT_SWAP_TERMINATED:
    62.     ; FS изменился: квант истек или произошел немедленный свап.
    63.     ; Отбрасываем текущее измерение и перезапускаем квант.
    64.     POP ECX
    65.     JMP TEST_START                
    66.    
    67. TEST_END_ALL:
    68.     POP ECX
    69.    
    70. ; ... (Дальнейший код) ...
    71.  
    72. Результат
    73. Теперь мы измеряем серию чистых событий IRET (возможно, десятки или сотни), пока не сработает детектор FS. Это максимально эффективно использует каждый выданный квант времени, давая вам самую высокую плотность "чистых" измерений.
    74.