Skip to content

v0.0.150

Choose a tag to compare

@github-actions github-actions released this 12 Aug 08:44
· 127 commits to main since this release

rePIU v0.0.150

이전 릴리스 v0.0.149 이후의 변경입니다. 이번 릴리스는 실행 probe에 게스트 메모리 구간
dump 진단을 추가하고, 그 진단으로 확정한 원인에 따라 DOS 저메모리 읽기 대행을 문자열 명령까지
확장해 pumpit8의 배경 로딩 중 종료를 제거합니다.

실행 probe 메모리 구간 dump

  • REPIU_EXECUTION_PROBE_DUMP_*로 probe first-hit 시점의 게스트 메모리 구간을 파일에
    보존합니다. 기준은 레지스터 이름 또는 절대 주소이며, 구조체 안의 포인터를 따라가는 간접
    역참조와 최대 1 MiB 복사를 지원합니다.
  • 복사 buffer는 실행 시작 전에 한 번 할당해, first-hit recorder가 예외 처리 중 할당하지
    않습니다. 파일 기록은 게스트 스레드가 멈춘 뒤 수행하며 VEH 안에서 파일 I/O를 하지 않습니다.
  • 바이트는 telemetry snapshot을 거치지 않고, snapshot에는 포착·기록 상태와 주소만 전달합니다.
  • 전체 구간이 게스트 arena 안에서 읽을 수 있을 때만 복사하고, 부분 범위나 host 임의 주소는
    읽지 않습니다. 환경 변수가 없으면 기존 실행 경로가 변하지 않습니다.
  • 반복 수행 절차는 docs/guides/execution-probe-memory-dump.md에 있습니다.

DOS 저메모리 문자열 명령 대행

  • HandleGuestLowMemoryReadFault는 MOV, MOVZX, MOVSX만 처리하고 나머지 mnemonic을
    거부해, 저메모리를 읽는 문자열 명령의 접근 위반이 처리되지 않았습니다.
  • SCAS, LODS, CMPS의 저메모리 읽기를 대행하는 전용 모듈을 추가했습니다. MOVS는 기존
    전담 handler가 있어 제외하고, STOS는 쓰기이므로 이 읽기 대행 범위에서 제외했습니다.
  • REP 계열이 반복 사이에서 재개 가능하다는 성질을 이용해, 반복이 끝났을 때만 EIP를
    전진시키고 남았으면 그대로 둡니다. 따라서 handler가 저메모리 경계를 추적하지 않습니다.
  • 폭은 operand width가 아니라 mnemonic에서 얻습니다. 66h prefix가 mnemonic 자체를 바꾸므로
    매핑이 정확합니다. SSE cmpsd와 문자열 CMPSD가 mnemonic을 공유하므로
    ZYDIS_CATEGORY_STRINGOP으로 먼저 거릅니다.
  • SetCompareFlags8을 폭 인자를 받는 SetCompareFlags로 일반화하고 기존 함수는 이를
    호출하도록 바꿨습니다. 8-bit 결과는 이전과 같습니다.
  • address size가 32-bit가 아니거나, 접근이 저메모리 경계를 걸치거나, CMPS 반대편이 게스트
    범위 검사를 통과하지 못하거나, REP인데 ECX가 이미 0이면 대행하지 않고 기존 진단이
    원인을 그대로 보고하게 둡니다.
  • 특정 target이나 실행 파일 주소에 대한 동작 우회는 추가하지 않았습니다.

pumpit8 배경 로딩 종료 해소

  • BGA/083.DAT의 BR01_01.tga(실제 PNG)를 읽던 중 발생하던 0xC0000005 종료가
    사라졌습니다.
  • 원인은 자산의 iCCP chunk에 있는 zlib datastream이 4바이트 adler32 없이 작성된 것이며,
    chunk CRC32가 저장값과 계산값 모두 0x8FC1BA08로 일치해 원본 그대로 메모리에 도달했음을
    확인했습니다. rePIU의 파일 I/O, RES 복호화, PNG 전달에는 결함이 없습니다.
  • 원본 libpng 1.0.6은 png_decompress_chunk의 null 반환을 검사하지 않고 prefix 0x17을
    더해 repne scasb로 역참조합니다. 정적 역어셈블로 call과 fault 사이에 조건 분기가 없음을
    확인했습니다.
  • 분석은 docs/analysis/pumpit8-bga-iccp-crash.md에 누적했습니다.

검증

  • Win32 x86 Debug 빌드와 Release 빌드가 성공했습니다.
  • Debug pumpit8 실행에서 문자열 대행 2,522회, 반복 2,522회, 마지막 EIP 0x040E5D0D,
    주소 0x00000017, mnemonic SCASB, 예외 0건을 기록했습니다.
  • 같은 실행이 BGA/083.DAT를 지나 AUDIO/004.AUD까지 진행했고 약 6.9 FPS로 렌더링했습니다.
    종료는 접근 위반이 아니라 관측 종료를 위한 SDL 종료 요청입니다.
  • OpenWatcom 샘플 819개를 빌드하고 baseline과 같은 Release 구성으로 suite를 재측정해
    RunPassed 528, 회귀 0건, 신규 통과 0건을 확인했습니다.

알려진 제한

  • pumpit8은 이 지점만 통과했을 뿐 완주를 확인한 것이 아니며, 이후 구간은 별도 관측
    대상입니다.
  • DOS/4GW에서 linear 0x17이 실제로 읽힌다는 것은 보호 모드 익스텐더의 표준 동작에서 온
    추정이며, 원본 환경에서 직접 관측하지 않았습니다.
  • 저메모리 대행은 읽기에 한정합니다. STOS처럼 저메모리에 쓰는 경로는 아직 대행하지
    않습니다.
  • 67h prefix가 붙은 16-bit address size 문자열 명령은 관측된 바 없어 구현하지 않았습니다.
  • 샘플 suite는 저메모리 문자열 읽기 경로에 도달하지 않으므로, 회귀 0건은 영향이 없음을
    확인한 것이지 이 경로를 검증한 것이 아닙니다.

rePIU v0.0.150

Everything since the previous release, v0.0.149. This release adds a guest memory-range dump
diagnostic to the execution probe and, acting on the cause that diagnostic established, extends DOS
low-memory read servicing to string instructions, removing the pumpit8 termination during
background loading.

Execution probe memory-range dump

  • REPIU_EXECUTION_PROBE_DUMP_* preserves a guest memory range to a file at the probe's first hit.
    The base is a register name or an absolute address, with optional indirect dereference to follow a
    pointer inside a structure and up to 1 MiB copied.
  • The capture buffer is allocated once before execution starts, so the first-hit recorder never
    allocates during exception handling. The file is written after the guest thread stops, and no file
    I/O happens inside the VEH.
  • The bytes never travel through the telemetry snapshot, which carries only capture and write status
    with the addresses.
  • The copy happens only when the complete range is readable inside the guest arena; no partial range
    or arbitrary host address is read. Without the environment variables the existing execution path
    is unchanged.
  • The repeatable procedure is in docs/guides/execution-probe-memory-dump.md.

DOS low-memory string instruction servicing

  • HandleGuestLowMemoryReadFault accepted only MOV, MOVZX, and MOVSX and rejected every other
    mnemonic, leaving access violations from string instructions reading low memory unserviced.
  • Added a dedicated module servicing SCAS, LODS, and CMPS reads of low memory. MOVS is
    excluded because a dedicated handler already owns it, and STOS is excluded because it writes and
    falls outside this read facility.
  • Using the restartability of REP forms between iterations, EIP advances only when the
    repetition finished and is otherwise left in place, so the handler never tracks where low memory
    ends.
  • Width comes from the mnemonic rather than the operand width, since a 66h prefix changes the
    mnemonic itself and makes the mapping exact. ZYDIS_CATEGORY_STRINGOP gates first because the SSE
    cmpsd shares its mnemonic with the string form.
  • SetCompareFlags8 is generalized into a width-taking SetCompareFlags with the original
    delegating to it; 8-bit results are unchanged.
  • Servicing declines, leaving the existing diagnostics to report the cause, when the address size is
    not 32-bit, the access straddles the low-memory boundary, the CMPS counterpart fails the
    guest-range check, or a REP has ECX already zero.
  • No target-specific or executable-address behavior bypass was added.

pumpit8 background-loading termination resolved

  • The 0xC0000005 termination while reading BR01_01.tga — actually a PNG — from BGA/083.DAT is
    gone.
  • The cause is that the asset's iCCP chunk carries a zlib datastream written without its four-byte
    adler32. The chunk CRC32 is 0x8FC1BA08 both stored and computed, confirming the chunk reached
    memory exactly as authored, so rePIU's file I/O, RES decryption, and PNG delivery are sound.
  • The original libpng 1.0.6 does not check the null return from png_decompress_chunk before
    adding the prefix 0x17 and dereferencing it with repne scasb. Static disassembly confirms no
    conditional branch separates the call from the fault.
  • The analysis is accumulated in docs/analysis/pumpit8-bga-iccp-crash.md.

Verification

  • Win32 x86 Debug and Release builds passed.
  • A Debug pumpit8 run recorded 2,522 string services over 2,522 iterations, last EIP
    0x040E5D0D, address 0x00000017, mnemonic SCASB, and zero exceptions.
  • The same run proceeded past BGA/083.DAT to AUDIO/004.AUD, rendering at roughly 6.9 FPS. It
    ended on an SDL exit request issued to stop the observation, not on an access violation.
  • All 819 OpenWatcom samples were built and the suite re-measured in the baseline's Release
    configuration, giving RunPassed 528 with zero regressions and zero new passes.

Known limitations

  • pumpit8 only passes this site; completion is not verified, and later stages remain separate
    observation targets.
  • That linear 0x17 is actually readable under DOS/4GW is inferred from standard protected-mode
    extender behavior and was not observed directly in the original environment.
  • Low-memory servicing covers reads only. Paths that write to low memory, such as STOS, are not
    serviced yet.
  • No 67h-prefixed 16-bit address-size string instruction has been observed, so that form is not
    implemented.
  • The sample suite never reaches a low-memory string read, so zero regressions confirms the absence
    of impact rather than validating this path.