knowledge(원가·STC): 검증 지적 4건 반영 — XOR 프로브 스크립트·표현 완화·상태 각주

- _scripts/xor_probe.py 신설: 키스트림 후보 유도 가정·계산 범위·한계 명시,
  파일 간 일치율·공백 복원 데모 출력. STC_난독화_상태 §2에 실행 명령·출력 기록
  (SYSINFO 7파일 100%·BDQTY 83~100%·공백 복원 82~91% — 재현 가능해짐)
- 단수처리_규칙: "설계연도 무관 규칙" → "2024~25 STmate 산출물 5건 관측 구현
  후보 규칙"으로 완화, 일반화 전 미확정 5요소(타 버전·발주기관·법령 의무·
  CUTNM·예외값) 명시, "5건 전부 일치" → "항목 존재 파일 전부 동일"
- 입출력변수사전(verified): 조달수수료·D9-1·D10 신규 행에 검증 범위 밖
  ⚠ 각주 (evidence=observed, 미결 No.20 연동) — 문서 상태는 유지
- 표현 완화 2곳: "포맷 고유 특성 확정" → "강한 증거 (알고리즘 미확정)"
  (교차비교·_STC포맷분석), extract_rounding.py 주석 6개→5개 정정

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 19:46:57 +09:00
co-authored by Claude Fable 5
parent 89e3b034e9
commit 67d04ed8fa
9 changed files with 136 additions and 16 deletions
@@ -125,7 +125,7 @@ status: draft
| PRJLST 필드 | 48 | 48 | 47 | 55 | 48 | 55(2건) | 55 |
| RATE 계열 | 동일 (내장 기준표 — 7파일 전부 스키마·건수 동일) |
- **난독화 전수 재현**: BDQTY·SYSINFO는 **7개 파일 전부** 플래그 `E`+고엔트로피(본문 비ASCII 0.63~0.71). SYSINFO 키스트림 파일 간 100% 동일 — 개별 파일 우연 아닌 STmate 포맷 고유 특성 확정.
- **난독화 전수 재현**: BDQTY·SYSINFO는 **7개 파일 전부** 플래그 `E`+고엔트로피(본문 비ASCII 0.63~0.71). SYSINFO 키스트림 후보는 파일 간 100% 동일(xor_probe.py 재현) — 오프셋별 고정 변환의 강한 증거 (알고리즘 미확정).
- **스키마 가변**: BOQ11 83↔94(임도↔사방 확장)ㆍCOSTN 99/101ㆍPRJLST 47/48/55ㆍBDQTY 37/38. 같은 83필드라도 레코드길이 1214↔1222 차이(필드길이 배분 상이) → **위치 기반 파싱 절대 금지, 필드명 기반 동적 매핑 필수**.
- **신규 발견**: ① `BOQ12.DBF` — 소광 신설(다공구) STC에만 존재, 2공구 분할 시 BOQ11/BOQ12 병렬. ② 소광은 STC 3종(당초 신설·다공구 신설·변경 1차) 보유 = **당초↔변경 차분분석 소스**. ③ PRJLST 다공구본은 레코드 2건(공구별).
- DBF 버전 바이트: 0x03(대부분)ㆍ0x06(BDQTY·SYSINFO·COSTA)ㆍ0xE5(COSTN·PRJLST·QCODE — Visual FoxPro 계열). 난독화 2종은 0x06+`E`플래그 조합.
@@ -52,8 +52,8 @@ status: draft
## 4. 난독화 재현
- BDQTY·SYSINFO는 **7파일 전부** 플래그 0x45(`E`) + 고엔트로피 → 난독화가 포맷 고유 특성임을 전수 확.
- SYSINFO 키스트림 파일 간 100% 동일. 상세 = [STC_난독화_상태 §2](STC_난독화_상태.md).
- BDQTY·SYSINFO는 **7파일 전부** 플래그 0x45(`E`) + 고엔트로피 (stc_cross_compare.py 재현) → 난독화가 포맷 고유 특성임을 전수 확.
- SYSINFO 키스트림 **후보** 파일 간 100% 동일 (xor_probe.py 재현) — 오프셋별 고정 변환의 강한 증거, 알고리즘은 미확정. 상세 = [STC_난독화_상태 §2](STC_난독화_상태.md).
## 5. 후속 (미해명 — 이 폴더에서 추적)
@@ -21,17 +21,30 @@ status: draft
- 헤더의 레코드 수 필드는 정상. 즉 **레코드는 실재**한다.
- 대조: 같은 STC의 COSTN·PRJLST 등은 플래그 0x20 + 비ASCII 0.005~0.08 (정상 텍스트). 난독화는 **BDQTY·SYSINFO 2종에 한정**.
## 2. XOR 부분 검증 (가설·재현)
## 2. XOR 부분 검증 (가설·재현 — 스크립트 재현 가능)
> 방법: 레코드들의 같은 열(오프셋)에서 **최빈 바이트**를 구하면, 평문에 공백(0x20)이 많은 열은 최빈 바이트 = "공백의 암호문"으로 추정된다. 이 추정 키스트림으로 역변환 시 공백 영역이 일괄 복원되는지 확인.
> 방법·가정: 레코드들의 같은 열(오프셋)에서 **최빈 바이트**를 구하면, 평문에 공백(0x20)이 많은 열은 최빈 바이트 = "공백의 암호문"으로 추정된다. 이 추정 키스트림 후보를 파일 간 비교하고, 역적용 시 공백 영역이 복원되는지 확인.
> **재현**: `python _scripts/xor_probe.py` (가정·계산 범위·한계는 스크립트 헤더 주석에 명시).
실행 출력 (2026-08-15, 보유 7개 STC):
```
# SYSINFO.DBF 키스트림 후보 파일 간 비교 (파일 7건)
7개 파일 전부: 일치 100/100 (100%) ← 기준 = 봉화, 비교폭 100B
# BDQTY.DBF 키스트림 후보 파일 간 비교 (공통 415B 절단)
봉화 100% · 기번3 99% · 변경1차 99% · 계류보전 97% · 소광 86% · 다공구 86% · 영월 83%
# 공백 복원 데모 (기번3)
SYSINFO: 레코드당 83~91% 바이트가 공백으로 복원
BDQTY: 레코드당 82~87% 바이트가 공백으로 복원
```
| 검증 | 결과 |
|---|---|
| 기번3 BDQTY 공백영역 복원 | 667 레코드 전체에서 위치 고정 공백 복원 재현 |
| **SYSINFO 키스트림 파일 간 동일성** | 7개 파일 전부 **100% 동일** (100/100 바이트) |
| BDQTY 키스트림 파일 간 동일성 | 83~100% (공백 많은 열 100%, 값 열은 레코드 내용차로 최빈값 흔들림) |
| **SYSINFO 키스트림 후보 파일 간 동일성** | 7개 파일 전부 **100% 동일** (100/100 바이트) |
| BDQTY 키스트림 후보 파일 간 동일성 | 83~100% (공백 많은 열 100%, 값 열은 레코드 내용차로 최빈값 흔들림 — 키 상이의 증거 아님) |
| 공백영역 복원 (기번3) | 키 후보 역적용 시 레코드당 82~91% 바이트 공백 복원 |
- SYSINFO 키스트림 **7파일 100% 동일** → 개별 파일 우연이 아닌 **STmate 포맷 고유의 고정 변환**. 강한 증거.
- SYSINFO 키스트림 후보가 **7파일 100% 동일** → 개별 파일 우연이 아닌 **오프셋별 고정 변환**의 강한 증거.
- 단, "최빈=공백 암호문" 가정 하의 유도이므로 **순수 XOR인지·키 유도 방법은 미확정**. 전 필드가 올바른 평문(BDCODE·QTY 등 알려진 필드값)으로 복원되고 다수 파일에서 재현돼야 알고리즘 확정.
## 3. 증거 수준 상태표 (과장·과소 방지)
@@ -22,7 +22,9 @@ status: draft
| [SIxMemo_구조](SIxMemo_구조.md) | COSTN.SMTㆍPRJLST.SMTㆍQCODE.SMT 커스텀 메모 포맷 |
| [XLSX_INITIAL_구조](XLSX_INITIAL_구조.md) | xlsx veryHidden INITIAL 시트 — STmate 재등록·행↔코드 매핑 |
| [STC_왕복검증](STC_왕복검증.md) | STmate 실기 테스트 결과 기록처 (실기 환경 확보 후 작성) |
| [_scripts/stc_cross_compare.py](_scripts/stc_cross_compare.py) | 교차비교 자동화 스크립트 (재현용) |
| [_scripts/stc_cross_compare.py](_scripts/stc_cross_compare.py) | 교차비교 자동화 스크립트 (엔트리·스키마·플래그·비ASCII) |
| [_scripts/xor_probe.py](_scripts/xor_probe.py) | 난독화 키스트림 프로브 (키 후보 유도·파일 간 일치율·공백 복원 데모) |
| [_scripts/extract_rounding.py](_scripts/extract_rounding.py) | xlsx ROUND 계열 수식 추출 ([단수처리_규칙](../../../technical_info/01_임도/05_원가정보/단수처리_규칙.md) 소스) |
## 대상 파일 (보유 STC 7건)
@@ -1,5 +1,5 @@
# 내역 xlsx에서 단수처리(ROUND 계열) 수식을 항목명과 함께 추출
# 목적: 단계별 절사·반올림 규칙을 6개 공사지 교차로 확인 (값 아닌 로직)
# 목적: 단계별 절사·반올림 규칙을 5개 공사지(FILES) 교차로 확인 (값 아닌 로직)
import zipfile
import re
import sys
@@ -0,0 +1,93 @@
# BDQTY·SYSINFO 난독화 키스트림 프로브
#
# 가정 (명시):
# 난독화가 "레코드 내 오프셋별 고정 변환(XOR 류)"이라면, 평문에 공백(0x20)이
# 많은 열은 여러 레코드에서 같은 암호문 바이트가 최빈값으로 나타난다.
# → 열별 최빈 바이트를 "공백의 암호문"으로 보고 키스트림 후보를 유도한다.
#
# 계산 범위:
# - 키 유도: 파일별·테이블별, 레코드 본문(플래그 바이트 제외 아님 — 전체 레코드
# 길이 기준, SYSINFO 100B / BDQTY 첫 415B로 절단해 파일 간 비교)
# - 파일 간 비교: 기준 파일(첫 번째)의 키 후보와 각 파일 키 후보의 바이트 일치율
#
# 해석 한계 (과잉 주장 방지):
# - 일치율 100% = "오프셋별 고정 변환" 가설의 강한 증거일 뿐, 순수 XOR 여부·
# 키 유도 알고리즘·복호 규칙은 미확정.
# - BDQTY의 값 열(레코드마다 내용이 다른 열)은 최빈값이 공백 암호문이 아닐 수
# 있어 일치율이 떨어진다 — 키가 다르다는 뜻이 아님.
#
# 실행: python xor_probe.py (이 폴더에서)
import zipfile
import struct
import glob
import os
import sys
from collections import Counter
sys.stdout.reconfigure(encoding="utf-8")
KNOW = os.path.normpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..")
)
def records(path, table):
z = zipfile.ZipFile(path)
d = z.read(table)
nrec = struct.unpack("<I", d[4:8])[0]
hlen = struct.unpack("<H", d[8:10])[0]
rlen = struct.unpack("<H", d[10:12])[0]
return [d[hlen + i * rlen : hlen + (i + 1) * rlen] for i in range(nrec)], rlen
def mode_key(recs, width):
"""열별 최빈 바이트 = 키스트림 후보 (공백 암호문 가정)"""
return bytes(
Counter(r[j] for r in recs).most_common(1)[0][0] for j in range(width)
)
def probe(table, clip=None):
paths = sorted(
glob.glob(os.path.join(KNOW, "original", "실무문서", "**", "*.STC"), recursive=True)
)
print(f"# {table} 키스트림 후보 파일 간 비교 (파일 {len(paths)}건)")
keys = []
for p in paths:
recs, rlen = records(p, table)
width = min(rlen, clip) if clip else rlen
keys.append((os.path.basename(p), mode_key(recs, width), len(recs)))
base_name, base_key, _ = keys[0]
print(f" 기준 = {base_name} (키 후보 {len(base_key)}B)")
for name, key, nrec in keys:
n = min(len(base_key), len(key))
same = sum(1 for a, b in zip(base_key[:n], key[:n]) if a == b)
print(
f" {name[:44]:46s} rec={nrec:4d} 일치 {same}/{n} ({round(same / n * 100)}%)"
)
print()
def spaces_demo(table, clip=None, show=2):
"""키 후보 XOR 0x20 역적용 시 공백 영역 복원 여부 (기번3 기준)"""
paths = sorted(
glob.glob(os.path.join(KNOW, "original", "실무문서", "**", "*.STC"), recursive=True)
)
target = [p for p in paths if "기번3" in p][0]
recs, rlen = records(target, table)
width = min(rlen, clip) if clip else rlen
key = mode_key(recs, width)
# 키 = "공백 암호문"이므로 평문 = byte XOR key XOR 0x20
print(f"# {table} 공백 복원 데모 (기번3, 앞 {show}레코드 × {width}B)")
for i in range(show):
pt = bytes(b ^ k ^ 0x20 for b, k in zip(recs[i][:width], key))
sp = sum(1 for b in pt if b == 0x20)
print(f" rec{i}: 공백으로 복원된 바이트 {sp}/{width} ({round(sp / width * 100)}%)")
print()
if __name__ == "__main__":
probe("SYSINFO.DBF")
probe("BDQTY.DBF", clip=415) # 스키마 37/38필드 혼재 → 공통 415B로 절단 비교
spaces_demo("SYSINFO.DBF")
spaces_demo("BDQTY.DBF", clip=415)
@@ -14,8 +14,8 @@ last_updated: 2026-08-15
관련 문서: [원가계산_체계](원가계산_체계.md) · [제비율](제비율.md) · [원가_출력변수_사전 §F](원가_출력변수_사전.md) · [자재단가_적용](자재단가_적용.md)
> **★ 이 문서의 성격**: 실무 내역서(과거 데이터)에서 추출한 것은 **당시 금액·요율이 아니라 단수처리 로직**(어느 단계에서 어느 함수로, 어느 자릿수까지 처리하는가)이다. 로직은 설계연도와 무관하게 재사용 가능한 계산 규칙이므로 기술DB화 대상. **값(0.54 %·14.5 % 등)은 특정 공사지 관측**일 뿐 확정 기본값이 아니다([00_운영지침 ★근본원칙](../../../00_운영지침.md)).
> 관측 소스: 실무 xlsx는 STmate가 생성한 것으로 계산식이 셀 수식에 그대로 노출됨. **5개 공사지(기번3·봉화·영월 임도 + 계류보전 사방 + 소광) 교차** — 아래 규칙은 5건 전부 일치분만 규칙화, 단발 관측은 ⚠.
> **★ 이 문서의 성격**: 실무 내역서(과거 데이터)에서 추출한 것은 **당시 금액·요율이 아니라 단수처리 로직**(어느 단계에서 어느 함수로, 어느 자릿수까지 처리하는가)이다. 단, 이 로직은 **조사한 2024~2025년 STmate 생성 XLSX 5건에서 반복 관측된 구현 후보 규칙**이지 법정·범용 규칙이 아니다 — 현행 규정 원문(§5)·프로그램 설정(Start8x.Ini `CUTNM` 관계 미확정)·타 STmate 버전·타 발주기관 대조 전까지 확정하지 않는다. **값(0.54 %·14.5 % 등)은 특정 공사지 관측**일 뿐 확정 기본값이 아니다([00_운영지침 ★근본원칙](../../../00_운영지침.md)).
> 관측 소스: 실무 xlsx는 STmate가 생성한 것으로 계산식이 셀 수식에 그대로 노출됨. **5개 공사지(기번3·봉화·영월 임도 + 계류보전 사방 + 소광) 교차** — 아래 규칙은 **해당 항목·수식이 존재하는 조사 파일 전부에서 동일**한 것만 규칙화(예: 조달수수료는 관급 있는 파일에서만 관측 가능), 단발 관측은 ⚠.
## 1. 단수처리 함수 — 위치별 3종 (★핵심)
@@ -69,7 +69,15 @@ STmate 내역서는 **계산 위치마다 다른 단수 함수**를 쓴다. 이
| 집계표 반올림 | ROUND | 검산·표시용 추정 — 근거 불요 가능 |
| 보험료 끝수 | — | 국고금관리법 §47(끝수 계산 안 함) = 건보법 §107 경유. 원가계산 절사와 별개 체계 |
> [구현] 단수 함수·자릿수·시점은 5공사지 실무 일관 관측이나 **현행 법령·기준 원문 대조는 미완**. 프로그램 기본값 확정 전 조달청 예정가격 작성기준 등 원문 확인(사용자 협의). 실무 일관성은 강한 실증이나 §00_운영지침상 현행 근거 우선.
**일반화 전 미확정 요소 (전부 ⚠)**:
1. 타 STmate 버전에서도 동일한가 (조사분 = w24.04 계열 2024~2025 산출물)
2. 타 발주기관·설계사무소에서도 동일한가 (조사분 = 남부청 계열 5건)
3. 법령상 의무적인 단수처리인가, 프로그램 관행인가
4. 사용자 설정값으로 바뀌는가 — Start8x.Ini `CUTNM=0` 관계 미확정 ([STC_컨테이너_구조 §5](../../../original/원가계산/STmate/STC_컨테이너_구조.md))
5. 예외값(음수·0원 행·면세 공제) 처리 규칙
> [구현] 단수 함수·자릿수·시점은 조사 파일 전부 일관 관측이나 **현행 법령·기준 원문 대조는 미완**. 프로그램 기본값 확정 전 조달청 예정가격 작성기준 등 원문 확인(사용자 협의). 실무 일관성은 강한 실증이나 §00_운영지침상 현행 근거 우선.
## 근거
@@ -148,6 +148,8 @@ last_updated: 2026-08-15
| rate_vat | 부가가치세율 | 총원가 | 10 % | D9 |
| rate_procurement_fee `[표]` (구매방식·금액별 추정) | 조달수수료율 | 관급 순자재대(government_material_base) | 실무 관측 0.54 % (과거) / **현행 원문 미확보 ⚠** | D9-1 |
> ⚠ **rate_procurement_fee 행은 문서 status(verified) 검증 범위 밖** — 2026-08-15 추가분, evidence = 실무 관측(observed)뿐이고 현행 원문 검증 전([미결 No.20](../../../03_미결_및_확인사항.md)). 원문 확보·검증 후 본 검증 범위 편입.
## G8. 할증·보정 계수 — 품셈 개정 연동 (준정적)
| 변수코드 | 한글명 | 값 구조 | 총람 연결 |
@@ -233,8 +233,10 @@ last_updated: 2026-08-15
| D7 | 일반관리비 | (재+노+경) × 율 | 필수 | |
| D8 | 이윤 | (노무비+경비+일반관리비) × 율 | 필수 | |
| D9 | 부가가치세 | 총원가 × 10% | 필수 | |
| D9-1 | 조달수수료 (procurement_fee) | 관급 순자재대(government_material_base) × 조달수수료율, **원단위 절사** | 높음 (관급 있을 때) | |
| D10 | 관급자재대 (government_material_total) | = ROUNDUP(순자재대 + 조달수수료, **천원**) 별도 표기 (총원가 밖) | 필수 | |
| D9-1 | 조달수수료 (procurement_fee) | 관급 순자재대(government_material_base) × 조달수수료율, **원단위 절사** | 높음 (관급 있을 때) | |
| D10 | 관급자재대 (government_material_total) | = ROUNDUP(순자재대 + 조달수수료, **천원**) 별도 표기 (총원가 밖) ⚠ 단수·산식은 실무 관측 | 필수 | |
> ⚠ **D9-1·D10 산식 갱신분(2026-08-15)은 문서 status(verified) 검증 범위 밖** — evidence = 실무 xlsx 관측(observed, [단수처리_규칙](단수처리_규칙.md))이며 현행 원문 검증 전(조달수수료율 = [미결 No.20](../../../03_미결_및_확인사항.md)). F3-2 갱신분 동일.
| D11 | 품 할증 (작업시기·경사·이동거리·집단화 등 26계열) | 품 × 계수 | 조건부 — 실무 미적용 관측, [품셈_적용기준 §2](../04_수량분석정보/품셈_적용기준.md) | |
| D12 | 자재 할증 — **모래는 용도별** (품셈 1-3-1): 콘크리트·포장용 잔골재 10/12 % · **노상·노반재료 6 %** · **관류·구조물기초 부설 4 %** (임도 주용도 = 후 2자). 그 밖 자갈(굵은골재) 3/5 %·노반 부순돌·자갈 4 %·흄관 3 %·떼 10 % 등 → 전표 [수량산출_일반 §3](../04_수량분석정보/수량산출_일반.md) | 수량 × 계수 | 높음 | |
| D13 | 공구손료·잡자재 (주재료비 2~5%) | 비율행 | 높음 | |