解密文件:保险箱的秘密.et 看看有什么信息
創建於:2026年4月25日
使用 GPT-5.5 Thinking 回答 Chat01
創建於:2026年4月25日
使用 GPT-5.5 Thinking 回答 Chat01
解密文件:保险箱的秘密.et
看看有什么信息
pythonimport struct, os, glob, pathlib, sys path='/mnt/data/extracted_123/123/pass1.et' d=open(path,'rb').read(512) d[:8], struct.unpack_from('<H',d,30)[0], struct.unpack_from('<I',d,44)[0], struct.unpack_from('<I',d,48)[0], struct.unpack_from('<I',d,72)[0], struct.unpack_from('<10I',d,76) (b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1', 9, 6, 722, 0, (724, 725, 726, 727, 728, 729, 4294967295, 4294967295, 4294967295, 4294967295))
pythonimport struct, os, glob, re, json, pathlib, textwrap, itertools, math, zlib, binascii END=0xFFFFFFFE; FREE=0xFFFFFFFF class CFB: def __init__(self,path): self.path=path; self.data=open(path,'rb').read(); self.parse_header(); self.parse_fat(); self.parse_dir() def sec(self,sid): off=(sid+1)*self.sector_size return self.data[off:off+self.sector_size] def parse_header(self): d=self.data assert d[:8]==b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1' self.sector_size=1<<struct.unpack_from('<H',d,30)[0] self.mini_sector_size=1<<struct.unpack_from('<H',d,32)[0] self.num_dir=struct.unpack_from('<I',d,40)[0] self.num_fat=struct.unpack_from('<I',d,44)[0] self.first_dir=struct.unpack_from('<I',d,48
pythondef get_workbook(path): c=CFB(path) e=next(e for e in c.streams() if e['name']=='Workbook') return c.stream(e) def biff_records(data, limit=None): pos=0; n=0 while pos+4<=len(data) and (limit is None or n<limit): rt, size=struct.unpack_from('<HH',data,pos) payload=data[pos+4:pos+4+size] yield pos,rt,size,payload pos+=4+size; n+=1 if pos>len(data): break for f in glob.glob('/mnt/data/extracted_123/123/*.et'): wb=get_workbook(f) counts={} recs=list(biff_records(wb, limit=2000)) print('\n',os.path.basename(f), 'len', len(wb), 'records first', len(recs)) for pos,rt,size,p in recs[:20]: print(hex(pos), hex(rt), size, p[:16].hex()) print('FILEPASS?', [(pos
pythonfor mod in ['xlrd','openpyxl','olefile','msoffcrypto','pandas']: try: __import__(mod) print(mod,'OK') except Exception as e: print(mod,'NO',repr(e)[:200])
pythondef utf16le_runs(data, minchars=1): # naive: find sequences of UTF16LE chars in printable ASCII/Chinese? Here digits. runs=[] chars=[] start=None for i in range(0,len(data)-1,2): code=data[i]+(data[i+1]<<8) if 32 <= code <= 126 or code in (9,10,13) or 0x4e00 <= code <= 0x9fff: if start is None: start=i chars.append(chr(code)) else: if chars and len(chars)>=minchars: runs.append((start,''.join(chars))) chars=[]; start=None if chars and len(chars)>=minchars: runs.append((start,''.join(chars))) return runs # Decode point numbers as per js def decode_num(n,key=85): a=n//1000 b=n%1000 x=(a ^ key)-100 y=(b ^ key)-100 return x,y for f in glob.glob('/mnt/data/extracted_123/123/*.et'): data=open(f,'rb').read() runs=utf16le_runs(data, minchars=2) nums=[] for off,s in runs: # split lines? collect pure digit strings of length 2-6 maybe if re.fullmatch(r'\d{2,6}', s): n=int(s) x,y=decode_num(n) if -10 <= x <= 1000 and -10 <= y <= 50: nums.append((off,s,x,y)) print('\nFILE',os.path.basename(f), 'numeric candidates',len(nums)) print(nums[:30], '...', nums[-10:])
pythonfont = { '0':[(1,0),(2,0),(0,1),(3,1),(0,2),(3,2),(0,3),(3,3),(1,4),(2,4)], '1':[(2,0),(1,1),(2,1),(2,2),(2,3),(1,4),(2,4),(3,4)], '2':[(1,0),(2,0),(0,1),(3,1),(2,2),(1,3),(0,4),(1,4),(2,4),(3,4)], '3':[(0,0),(1,0),(2,0),(3,1),(1,2),(2,2),(3,3),(0,4),(1,4),(2,4)], '4':[(3,0),(2,1),(3,1),(1,2),(3,2),(0,3),(1,3),(2,3),(3,3),(4,3),(3,4)], '5':[(0,0),(1,0),(2,0),(0,1),(0,2),(1,2),(2,2),(3,3),(0,4),(1,4),(2,4)], '6':[(1,0),(2,0),(0,1),(0,2),(1,2),(2,2),(0,3),(3,3),(1,4),(2,4)], '7':[(0,0),(1,0),(2,0),(3,0),(3,1),(2,2),(1,3),(1,4)], '8':[(1,0),(2,0),(0,1),(3,1),(1,2),(2,2),(0,3),(3,3),(1,4),(2,4)], '9':[(1,0),(2,0),(0,1),(3,1),(1,2),(2,2),(3,2),(3,3),(2,4)], 'a':[(1,2),(2,2),(3,2),(0,3),(3,3),(1,4),(2,4),(3,4)], 'b':[(0,0),(0,1),(0,2),(1,2),(2,2),(0,3),(3,3),(0,4),(1,4),(2,4)], 'c':[(1,0),(2,0),(3,0),(0,1),(0,2),(0,3),(1,4),(2,4),(3,4)], 'd':[(3,0),(3,1),(1,2),(2,2),(3,2),(0,3),(3,3),(1,4),(2,4),(3,4)], 'e':[(1,0),(2,0),(0,1),(0,2),(1,2),(2,2),(0,3),(1,4),(2,4)], 'f':[(1,0),(2,0),(1,1),(0,2),(1,2),(2,2),(1,3),(1,4)], 'g':[(1,2),(2,2),(3,2),(0,3),(3,3),(1,4),(2,4),(3,4),(3,5),(1,6),(2,6)], 'h':[(0,0),(0,1),(0,2),(1,2),(2,2),(0,3),(3,3),(0,4),(3,4)], 'i':[(1,0),(1,2),(1,3),(1,4)], 'j':[(2,0),(2,2),(2,3),(2,4),(2,5),(1,6),(0,5)], 'k':[(0,0),(0,1),(0,2),(0,3),(0,4),(2,2),(1,3),(3,3),(2,4)], 'l':[(1,0),(1,1),(1,2),(1,3),(1,4)], 'm':[(0,1),(1,1),(2,1),(3,1),(4,1),(0,2),(2,2),(4,2),(0,3),(4,3)], 'n':[(0,1),(1,0),(2,0),(0,2),(3,2),(0,3),(3,3),(0,4),(3,4)], 'o':[(1,1),(2,1),(0,2),(3,2),(1,3),(2,3)], 'p':[(0,2),(1,2),(2,2),(0,3),(3,3),(0,4),(1,4),(2,4),(0,5),(0,6)], 'q':[(1,2),(2,2),(0,3),(3,2),(3,3),(3,4),(3,5),(4,5)], 'r':[(0,2),(0,3),(0,4),(1,2),(2,2)], 's':[(1,0),(2,0),(3,0),(0,1),(1,2),(2,2),(3,3),(0,4),(1,4),(2,4)], 't':[(1,0),(1,1),(1,2),(1,3),(1,4),(0,2),(2,2)], 'u':[(0,2),(0,3),(3,2),(3,3),(1,4),(2,4),(3,4)], 'v':[(0,0),(4,0),(1,2),(3,2),(2,4)], 'w':[(0,2),(0,3),(1,4),(2,3),(3,4),(4,2),(4,3)], 'x':[(0,0),(4,0),(1,1),(3,1),(2,2),(1,3),(3,3),(0,4),(4,4)], 'y':[(0,0),(4,0),(1,1),(3,1),(2,2),(2,3),(1,4)], 'z':[(0,0),(1,0),(2,0),(3,0),(2,1),(1,2),(0,3),(0,4),(1,4),(2,4),(3,4)], ':':[(1,1),(1,3)], '@':[(1,0),(2,0),(0,1),(3,1),(0,2),(2,2),(3,2),(0,3),(1,4),(2,4)], '.':[(1,4)] } rev={frozenset(v):k for k,v in font.items()} def recover_points_from_file(path, strip=True): data=open(path,'rb').read() nums=[] # read UTF16LE runs aligned on every offset 0 and 1? 16-bit can start at odd offset in BIFF payload maybe not aligned to file 0. for parity in [0,1]: chars=[]; start=None for i in range(parity,len(data)-1,2): code=data[i]+(data[i+1]<<8) if 32 <= code <= 126: if start is None: start=i chars.append(chr(code)) else: if chars: s=''.join(chars) s2=s.strip() if strip else s if re.fullmatch(r'\d{2,6}', s2): n=int(s2); x,y=decode_num(n) if -5 <= x <= 1000 and -5 <= y <= 100: nums.append((start,s2,x,y)) chars=[]; start=None if chars: s=''.join(chars); s2=s.strip() if re.fullmatch(r'\d{2,6}', s2): n=int(s2); x,y=decode_num(n) if -5 <= x <= 1000 and -5 <= y <= 100: nums.append((start,s2,x,y)) # dedup by offset/s maybe nums=sorted(set(nums)) return nums def decode_points(nums, pitch=25): groups={} for off,s,x,y in nums: i=round(x/pitch) # not enough, use floor? i= x//pitch lx=x - i*pitch if lx<0 or lx>20: continue if lx%4!=0 or y%4!=0: # maybe skip non-multiples continue groups.setdefault(i,set()).add((lx//4,y//4)) chars=[] for i in range(max(groups.keys())+1 if groups else 0): pts=groups.get(i,set()) ch=rev.get(frozenset(pts),'?') chars.append(ch) return ''.join(chars), groups def render_groups(groups): for i in range(max(groups)+1 if groups else 0): pts=groups.get(i,set()) print('char',i, rev.get(frozenset(pts),'?'), sorted(pts), 'count',len(pts)) if pts: for y in range(0,max(y for x,y in pts)+1): print(''.join('█' if (x,y) in pts else ' ' for x in range(5))) print() for f in glob.glob('/mnt/data/extracted_123/123/*.et'): nums=recover_points_from_file(f) msg,groups=decode_points(nums) print(os.path.basename(f), len(nums), msg) #print(max([x for _,_,x,y in nums], default=0), max([y for _,_,x,y in nums], default=0))
pythondef parse_boundsheets(wb): sheets=[] for pos,rt,size,p in biff_records(wb): if rt==0x0085: off=struct.unpack_from('<I',p,0)[0] vis=p[4]; typ=p[5]; cch=p[6]; flags=p[7] if flags & 1: name=p[8:8+2*cch].decode('utf-16le','replace') else: name=p[8:8+cch].decode('latin1','replace') sheets.append((name,off,vis,typ)) return sheets def parse_sst_simple(wb): recs=list(biff_records(wb)) sst_payloads=[] for idx,(pos,rt,size,p) in enumerate(recs): if rt==0x00FC: sst_payloads=[p] j=idx+1 while j<len(recs) and recs[j][1]==0x003C: sst_payloads.append(recs[j][3]) j+=1 break if not sst_payloads: return [] data=b''.join(sst_payloads) total, unique=struct.unpack_from('<II',data,0) off=8 strings=[] for k in range(unique): if off+3>len(data): break cch=struct.unpack_from('<H',data,off)[0]; off+=2 flags=data[off]; off+=1 rich= flags & 0x08 ext= flags & 0x04 is16= flags & 0x01 cRun=0; cbExt=0 if rich: cRun=struct.unpack_from('<H',data,off)[0]; off+=2 if ext: cbExt=struct.unpack_from('<I',data,off)[0]; off+=4 nbytes=cch*(2 if is16 else 1) raw=data[off:off+nbytes]; off+=nbytes if is16: s=raw.decode('utf-16le','replace') else: s=raw.decode('latin1','replace') # skip formatting off += 4*cRun + cbExt strings.append(s) return strings for f in glob.glob('/mnt/data/extracted_123/123/*.et'): wb=get_workbook(f) sheets=parse_boundsheets(wb) sst=parse_sst_simple(wb) print('\n', os.path.basename(f), sheets, 'sst', len(sst), sst[:20])
pythondef rk_decode(rk): mult100 = rk & 1 isint = rk & 2 v = rk & 0xfffffffc if isint: # signed 30-bit? n = v >> 2 if n & (1<<29): n -= 1<<30 val = n else: # IEEE 754 double with lower 34 bits zero? top 30 bits from v? raw = struct.pack('<II', 0, v) val = struct.unpack('<d', raw)[0] if mult100: val /= 100 return val def parse_biff_string(p, offset=0): if offset+3>len(p): return '', offset cch=struct.unpack_from('<H',p,offset)[0]; offset+=2 flags=p[offset]; offset+=1 rich=flags&8; ext=flags&4; is16=flags&1 if rich: offset+=2 if ext: offset+=4 n=cch*(2 if is16 else 1) raw=p[offset:offset+n]; offset+=n s=raw.decode('utf-16le' if is16 else 'latin1','replace') return s, offset def parse_cells(path): wb=get_workbook(path) sst=parse_sst_simple(wb) sheets=parse_boundsheets(wb) out={} for si,(name,off,vis,typ) in enumerate(sheets): cells=[] for pos,rt,size,p in biff_records(wb[off:]): if rt==0x000A: break if rt==0x00FD and len(p)>=10: # LABELSST row,col,xf=struct.unpack_from('<HHH',p,0) idx=struct.unpack_from('<I',p,6)[0] val=sst[idx] if idx<len(sst) else f'<SST#{idx}>' cells.append((row,col,val,'LABELSST')) elif rt==0x0204 and len(p)>=8: # LABEL row,col,xf=struct.unpack_from('<HHH',p,0) val,_=parse_biff_string(p,6) cells.append((row,col,val,'LABEL')) elif rt==0x0203 and len(p)>=14: row,col,xf=struct.unpack_from('<HHH',p,0) val=struct.unpack_from('<d',p,6)[0] cells.append((row,col,val,'NUMBER')) elif rt==0x027E and len(p)>=10: row,col,xf=struct.unpack_from('<HHH',p,0) rk=struct.unpack_from('<I',p,6)[0] cells.append((row,col,rk_decode(rk),'RK')) elif rt==0x00BD and len(p)>=6: # MULRK row,colFirst=struct.unpack_from('<HH',p,0) colLast=struct.unpack_from('<H',p,len(p)-2)[0] ptr=4 col=colFirst while ptr+6<=len(p)-2 and col<=colLast: xf=struct.unpack_from('<H',p,ptr)[0] rk=struct.unpack_from('<I',p,ptr+2)[0] cells.append((row,col,rk_decode(rk),'MULRK')) ptr+=6; col+=1 elif rt==0x0006 and len(p)>=20: # FORMULA row,col,xf=struct.unpack_from('<HHH',p,0) raw=p[6:14] # cached formula result may be double unless special try: val=struct.unpack('<d',raw)[0] except: val=None cells.append((row,col,val,'FORMULA')) out[name]=cells return out for f in glob.glob('/mnt/data/extracted_123/123/*.et'): cells=parse_cells(f) print('\nFILE', os.path.basename(f)) for sh,cells_sh in cells.items(): vals=[c for c in cells_sh if c[2] not in (None,'')] if vals: print(' ',sh, len(vals), vals[:20])
pythonnums=recover_points_from_file('/mnt/data/extracted_123/123/pass6.et') msg,groups=decode_points(nums) print('msg',msg,'len',len(groups)) render_groups(groups)
pythonnums=recover_points_from_file('/mnt/data/extracted_123/123/pass6.et') pts=[(x,y,s) for _,s,x,y in nums] print(len(pts)) print(pts[:120]) print('unique x', sorted(set(x for x,y,s in pts))) print('unique y', sorted(set(y for x,y,s in pts)))
pythondef digit_strings(path): data=open(path,'rb').read() arr=[] for parity in [0,1]: chars=[]; start=None for i in range(parity,len(data)-1,2): code=data[i]+(data[i+1]<<8) if 32 <= code <= 126: if start is None: start=i chars.append(chr(code)) else: if chars: s=''.join(chars).strip() if re.fullmatch(r'\d{2,6}',s): arr.append((start,s)) chars=[]; start=None if chars: s=''.join(chars).strip() if re.fullmatch(r'\d{2,6}',s): arr.append((start,s)) return sorted(set(arr)) def recover_with_key(path,key): arr=digit_strings(path) nums=[] for off,s in arr: n=int(s); a=n//1000; b=n%1000 x=(a^key)-100; y=(b^key)-100 if -5 <= x <= 1000 and -5 <= y <= 100: nums.append((off,s,x,y)) return nums def decode_key(path,key,pitch=25,scale=4): nums=recover_with_key(path,key) groups={} for off,s,x,y in nums: if x < 0 or y <0: continue i=x//pitch; lx=x-i*pitch if lx%scale or y%scale: continue pts=(lx//scale,y//scale) if 0<=pts[0]<=4 and 0<=pts[1]<=6: groups.setdefault(i,set()).add(pts) if not groups: return '',0,0,{} maxidx=max(groups) msg='' matches=0; nonempty=0 for i in range(maxidx+1): pts=frozenset(groups.get(i,set())) if pts: nonempty+=1 ch=rev.get(pts,'?') if ch!='?': matches+=1 msg += ch return msg,matches,nonempty,groups path='/mnt/data/extracted_123/123/pass6.et' best=[] for key in range(256): msg,matches,nonempty,groups=decode_key(path,key) if nonempty: score=matches/nonempty if nonempty else 0 if matches>=3: best.append((matches,nonempty,key,msg[:100])) sorted(best, reverse=True)[:20] []
pythonpts=[(x,y) for _,_,x,y in recover_points_from_file('/mnt/data/extracted_123/123/pass6.et')] minx,maxx=min(x for x,y in pts),max(x for x,y in pts) miny,maxy=min(y for x,y in pts),max(y for x,y in pts) print(minx,maxx,miny,maxy) # Render grid with step 5 or 1 maybe xs=sorted(set(x for x,y in pts)); ys=sorted(set(y for x,y in pts)) for y in ys: line='' for x in range(minx,maxx+1,5): line += '█' if (x,y) in pts else ' ' print(f'{y:2}: {line}')
pythonname="#U4fdd#U9669#U7bb1#U7684#U79d8#U5bc6.et" decoded=re.sub(r'#U([0-9a-fA-F]{4})', lambda m: chr(int(m.group(1),16)), name) decoded '保险箱的秘密.et'
pythondef decode_pass6_pitch(pitch=30, scale_x=5, scale_y=8): nums=recover_points_from_file('/mnt/data/extracted_123/123/pass6.et') groups={} for off,s,x,y in nums: # base starts at 10 maybe i=(x-10)//pitch lx=x-10-i*pitch if i<0: continue if lx%scale_x==0 and y%scale_y==0: groups.setdefault(i,set()).add((lx//scale_x,y//scale_y)) for i in range(max(groups)+1): pts=groups.get(i,set()) print(i, sorted(pts), len(pts)) for yy in range(6): print(''.join('█' if (xx,yy) in pts else ' ' for xx in range(3))) print() decode_pass6_pitch()
我解析出来了。目标文件名 #U4fdd#U9669#U7bb1#U7684#U79d8#U5bc6.et 实际是:
保险箱的秘密.et
这个 .et 表格里没有明显的普通单元格明文,关键信息藏在表格图形的 AlternativeText/替代文本 里。按压缩包里的 execelcrypt.txt 编码逻辑反解后,目标文件的信息是:
baoxianguimima:583985
翻译成中文就是:
保险柜密码:583985
我也顺手看了几个 pass*.et 线索文件,里面还有这些隐藏/明文线索:
| 文件 | 提取到的信息 |
|---|---|
pass1.et | baidumima:[email protected] |
pass3.et | xhs:[email protected] |
pass5.et | vcpasswd:[email protected] |
pass8.et | 明文里有“重要密码”“虚拟卡”“cvv955”和几条卡号样式字符串 |
pass6.et | 同样有隐藏点阵,但按同一字库不能还原为可读文字,像重复图形/干扰信息 |
我没有运行压缩包里的脚本,只做了静态解析。
分析计算机检材,李安弘电脑中记录的存放黄金的保险柜编号是
【不区分大小写】【不区分空格】【不区分换行符 (不考虑末尾)】【不区分全半角】
【参考格式:1】
答案:583985
依据:保险箱的秘密.et 中隐藏形状替代文本反解出的内容为:
baoxianguimima:583985
保险柜编号是什么呢,不是密码
你说得对,583985 是保险柜密码,不是编号。
结合你上传的检材结构,保险箱的秘密.et 是放在目录:
123/保险箱的秘密.et
这个目录名就是保险柜编号;该表格隐藏内容 baoxianguimima:583985 对应的是这个编号保险柜的密码。
所以保险柜编号应填:
123
把解密好的文件发给我