- research/tuning-re/cil_disasm.py — минимальный CIL-дизассемблер (dnfile). - research/tuning-re/command-templates.txt — извлечённые шаблоны всех команд. - docs/tuning-software-protocol.md: точная карта команд-полей + раскладка bufDigTune + ВЫВОД: частотная калибровка = ЗАМКНУТЫЙ ЦИКЛ (Set-команды несут band/опорные частоты, не абсолютное смещение; WriteAll данных не несёт). Слепой скрипт записи freq-трима может снова сбить калибровку -> безопаснее гнать штатным тулом с MMDVM BER вместо частотомера, либо сперва рид-онли снять текущую калибровку. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NikMGoqQWWk9wy2ww2vJAr
99 строки
4.6 KiB
Python
99 строки
4.6 KiB
Python
import dnfile, struct
|
|
pe = dnfile.dnPE("tuning.exe")
|
|
net = pe.net
|
|
data = bytes(pe.__data__)
|
|
def rva2off(rva):
|
|
for s in pe.sections:
|
|
vs=max(s.Misc_VirtualSize,s.SizeOfRawData)
|
|
if s.VirtualAddress<=rva<s.VirtualAddress+vs:
|
|
return s.PointerToRawData+(rva-s.VirtualAddress)
|
|
def sstr(x):
|
|
try: return str(x)
|
|
except: return "?"
|
|
# token resolvers
|
|
mdef=net.mdtables.MethodDef.rows
|
|
mref=net.mdtables.MemberRef.rows if net.mdtables.MemberRef else []
|
|
fld=net.mdtables.Field.rows if net.mdtables.Field else []
|
|
tref=net.mdtables.TypeRef.rows if net.mdtables.TypeRef else []
|
|
def us(tok):
|
|
# #US token 0x70xxxxxx
|
|
idx=tok&0xffffff
|
|
try:
|
|
return '"'+sstr(net.user_strings.get(idx).value)+'"'
|
|
except Exception as e:
|
|
return f"US#{idx:x}"
|
|
def resolve(tok):
|
|
t=tok>>24; i=(tok&0xffffff)
|
|
if t==0x0A and i-1<len(mref): return "ref:"+sstr(mref[i-1].Name)
|
|
if t==0x06 and i-1<len(mdef): return "mdef:"+sstr(mdef[i-1].Name)
|
|
if t==0x04 and i-1<len(fld): return "fld:"+sstr(fld[i-1].Name)
|
|
if t==0x0A: return f"memberref#{i}"
|
|
return f"tok0x{tok:08x}"
|
|
|
|
# opcode table (subset)
|
|
OPS={0x00:"nop",0x01:"break",0x02:"ldarg.0",0x03:"ldarg.1",0x04:"ldarg.2",0x05:"ldarg.3",
|
|
0x06:"ldloc.0",0x07:"ldloc.1",0x08:"ldloc.2",0x09:"ldloc.3",0x0a:"stloc.0",0x0b:"stloc.1",
|
|
0x0c:"stloc.2",0x0d:"stloc.3",0x0e:("ldarg.s",1),0x0f:("ldarga.s",1),0x10:("starg.s",1),
|
|
0x11:("ldloc.s",1),0x12:("ldloca.s",1),0x13:("stloc.s",1),0x14:"ldnull",0x15:"ldc.i4.m1",
|
|
0x16:"ldc.i4.0",0x17:"ldc.i4.1",0x18:"ldc.i4.2",0x19:"ldc.i4.3",0x1a:"ldc.i4.4",0x1b:"ldc.i4.5",
|
|
0x1c:"ldc.i4.6",0x1d:"ldc.i4.7",0x1e:"ldc.i4.8",0x1f:("ldc.i4.s",1),0x20:("ldc.i4",4),
|
|
0x21:("ldc.i8",8),0x22:("ldc.r4",4),0x23:("ldc.r8",8),0x25:"dup",0x26:"pop",
|
|
0x28:("call",4,"tok"),0x29:("calli",4),0x2a:"ret",
|
|
0x2b:("br.s",1,"br"),0x2c:("brfalse.s",1,"br"),0x2d:("brtrue.s",1,"br"),0x2e:("beq.s",1,"br"),
|
|
0x2f:("bge.s",1,"br"),0x30:("bgt.s",1,"br"),0x31:("ble.s",1,"br"),0x32:("blt.s",1,"br"),
|
|
0x33:("bne.un.s",1,"br"),0x38:("br",4,"br"),0x39:("brfalse",4,"br"),0x3a:("brtrue",4,"br"),
|
|
0x3b:("beq",4,"br"),0x58:"add",0x59:"sub",0x5a:"mul",0x5b:"div",0x5f:"and",0x60:"or",
|
|
0x61:"xor",0x62:"shl",0x63:"shr",0x65:"neg",0x66:"not",
|
|
0x6f:("callvirt",4,"tok"),0x72:("ldstr",4,"us"),0x73:("newobj",4,"tok"),
|
|
0x74:("castclass",4,"tok"),0x75:("isinst",4,"tok"),
|
|
0x7b:("ldfld",4,"tok"),0x7d:("stfld",4,"tok"),0x7e:("ldsfld",4,"tok"),0x80:("stsfld",4,"tok"),
|
|
0x8c:("box",4,"tok"),0x8d:("newarr",4,"tok"),0x8e:"ldlen",
|
|
0x91:"ldelem.i1",0x92:"ldelem.u1",0x93:"ldelem.i2",0x94:"ldelem.u2",0x95:"ldelem.i4",
|
|
0x9c:"stelem.i1",0x9d:"stelem.i2",0x9e:"stelem.i4",0xa2:"stelem.ref",0x8f:("ldelema",4,"tok"),
|
|
0xa3:("ldelem",4,"tok"),0xa4:("stelem",4,"tok"),
|
|
0x6a:"conv.i8",0x69:"conv.i4",0x67:"conv.i1",0x68:"conv.i2",0xd1:"conv.u2",0xd2:"conv.u1",
|
|
0x20:("ldc.i4",4)}
|
|
|
|
def disasm(rva, name):
|
|
off=rva2off(rva)
|
|
b0=data[off]
|
|
if (b0&3)==2:
|
|
codesize=b0>>2; ilstart=off+1
|
|
else:
|
|
# fat header 12 bytes
|
|
codesize=struct.unpack_from("<I",data,off+4)[0]; ilstart=off+12
|
|
il=data[ilstart:ilstart+codesize]
|
|
print(f"\n===== {name} (RVA 0x{rva:x}, {codesize} bytes IL) =====")
|
|
p=0
|
|
while p<len(il):
|
|
op=il[p]; p0=p; p+=1
|
|
if op==0xfe:
|
|
op2=il[p]; p+=1
|
|
nm={0x01:"ceq",0x02:"cgt",0x04:"clt",0x09:("ldarg",2),0x0b:("ldloc",2),0x0c:("ldloca",2),0x0d:("stloc",2),0x16:"constrained",0x1e:"readonly"}.get(op2,f"fe{op2:02x}")
|
|
if isinstance(nm,tuple):
|
|
sz=nm[1]; val=int.from_bytes(il[p:p+sz],'little'); p+=sz
|
|
print(f" {p0:04x}: {nm[0]} {val}")
|
|
else:
|
|
print(f" {p0:04x}: {nm}")
|
|
continue
|
|
info=OPS.get(op)
|
|
if info is None:
|
|
print(f" {p0:04x}: .byte 0x{op:02x}"); continue
|
|
if isinstance(info,str):
|
|
print(f" {p0:04x}: {info}"); continue
|
|
nm=info[0]; sz=info[1]; kind=info[2] if len(info)>2 else None
|
|
raw=il[p:p+sz]; p+=sz
|
|
if kind=="tok":
|
|
tok=int.from_bytes(raw,'little'); print(f" {p0:04x}: {nm} {resolve(tok)}")
|
|
elif kind=="us":
|
|
tok=int.from_bytes(raw,'little'); print(f" {p0:04x}: {nm} {us(tok)}")
|
|
elif kind=="br":
|
|
disp=int.from_bytes(raw,'little',signed=True); print(f" {p0:04x}: {nm} -> {p+disp:04x}")
|
|
else:
|
|
val=int.from_bytes(raw,'little',signed=(nm in('ldc.i4','ldc.i4.s'))); print(f" {p0:04x}: {nm} {val} (0x{val&0xffffffff:x})")
|
|
|
|
import sys
|
|
for nm,rva in [("btnRead_U_Freq_Click",0x5f330),("btnCal_U_Freq_Click",0x5f53c),
|
|
("num_U_DigFreq_ValueChanged",0x5f160)]:
|
|
disasm(rva,nm)
|