逆向一种私有二进制格式从文件头和 magic 字节说起
手上有一批设备采集下来的二进制数据,没文档,只有几组不同后缀的文件,要把它们读出来画图。全程靠 hex 编辑器一点点猜,记一下逆向这种私有二进制格式的大概路数,顺便记一个踩得很疼的 off-by-one。
先把头和样点数摸清楚这种采集文件,基本结构都是「一个文件头 + 后面一大片采样数据」。第一步是估文件头多大。把文件丢进 hex 编辑器,前面一段往往是固定模式、跟后面的「数据区」长得不一样,头一般是个整数(512、1024、2048 这种)。这批文件摸下来头是 1024 字节。
样点数通常藏在文件头的某个偏移里。怎么找——算一道简单除法:(文件总大小 - 头) / 每道字节数 = 道数,而 每道字节数 = 样点数 × 每个样点的字节。如果数据是 16 位(2 字节)存的,那去文件头里找一个值,使得「头之后的字节数 / 2 / 这个值」是个整数、而且这个值本身是合理的样点数(比如 512)。这批文件样点数在头偏移 4 的位置,值是 512。
到这里,最朴素的读法就有了:跳过 1024 头,后面按 int16 一道一道读:
nsamp = 512 ...
雷达数据做增益到底在补什么
雷达数据做完前面的清洗,最后一步往往是加增益(gain)。深部反射看不见、一团黑,加完增益深部就亮起来了。但增益到底在补什么、补多少有讲究,不是无脑把深部拉亮就行。另外有篇单独讲做完增益之后显示色阶的坑,这篇只讲增益算法本身。
为什么要补:深部比浅部弱太多电磁波往地下走,越深衰减越厉害,所以深处的反射回到天线时已经弱了一大截。一道信号画出来,浅部反射很强、深部几乎贴着噪声底,深部想看的目标(比如衬砌后面的脱空)就淹没在里面。增益的作用就是把这个随深度掉下去的幅度,人为往上提一提,让深浅在图上都能看见。
衰减有两种,数学形式不一样深部变弱这件事,根上有两个来源,得分开看。一个是几何扩散——波往外是球面扩散的,能量摊到越来越大的球面上,幅度按幂律(大约 1/距离)往下掉。另一个是介电吸收——介质本身把电磁能吃掉一部分,这个是按指数 e^(−α·深度) 掉的。
关键在于这俩数学形式不同:一个是幂律、一个是指数。所以想补偿,也得用对应的形式去补,硬拿一个套另一个补不齐。
两种增益对应两种衰减增益常见的两种类型正好对应上:power 型按幂律补,对应几何扩散;exp 型按指数补,对应介 ...
一张登录许可证启动时到底该查哪些
给桌面软件接了一套登录许可证,启动的时候要过一串校验,任一条不过就直接锁应用。licence 防破解的一些具体坑(时间回拨、指纹漂移)之前写过,这里不重复,主要想理一下:一张许可证启动时到底该查哪些项,以及其中几个不那么直观的、容易漏掉的检查。
整体是个串行链条,前面是许可证本身合不合法,后面是运行环境对不对,最后才是用户名密码。哪一环断了都锁,不是「前面过了后面就随便」。
许可证本体这几项先是把许可证解析出来,格式不对、签名解析失败,直接锁。然后是有效期,过期了锁——但有效期的判断会被改系统时间绕掉,这块得配合本地单调时间记录来防回拨,之前那篇说过的思路,这里不展开。接着是硬件指纹比对,确认现在这台机器就是许可证绑的那台,防一份 licence 拷来拷去到处用。
这几项是基础,大多数做 licence 的人都会想到。
版本号得跟软件版本对上这条我觉得容易被忽略。许可证里带一个版本号字段,校验的时候不能只看它「有没有」,得看它是不是等于当前软件的版本。
为什么重要——因为软件会迭代。一份在 v2 时代签发的 licence,里面版本号写的是 2,用户拿着它去跑 v3,如果不校验版本,v ...
注册机的管理员密码别明文写在配置文件里
做了个注册机给桌面软件签发许可证,注册机本身有个管理员解锁密码——不是最终用户的密码,是防止谁都能进来乱签 license 的那道门。一开始图省事,密码明文写在 gradle.properties 里,程序运行的时候读出来比对。能用是能用,但越想越不对劲。
问题在于,这个密码随包一起发出去了。gradle.properties 里的值,打包完之后会以某种形式落到产物里,最差就是直接生成一个 register.properties 跟着 exe 走。破解者拿到发版包,翻一翻配置文件,明文密码就在那儿躺着,这道门等于没上锁。
后来改成这样:密码在打包阶段就转成 MD5,然后通过 exe4j 的 VM 参数固化进 exe,运行时直接从系统属性里读 MD5 来比对,全程不落外部配置文件。
打包脚本里大致是:
// 打包时把明文密码算成 MD5,塞进 exe4j 的 VM 参数def pwdMd5 = md5(project.property('registerPassword'))def vmArgs = "-Dregister.password.md5=$ ...
exe4j 打包出来的 exe 报找不到 jvm
用 exe4j 把一个 JavaFX 桌面程序打成 exe,自带 JRE 一起发出去,结果双击 exe 直接弹「找不到 jvm」之类的错。机器上明明装了 Java,自带的 JRE 目录也都在,就是起不来。
折腾一圈发现是 exe4j 找 JRE 的路径没配对。
exe4j 打出来的 exe 本质是个启动器,它自己不包含 JVM,运行的时候要先在机器上找一个可用的 JRE 再拉起真正的程序。它找 JRE 是按一个「搜索顺序」来的,默认会先去找系统注册的 Java、JAVA_HOME 之类,找不到才往下走。问题就出在这:开发机上系统 Java 是高版本的,能蒙混过去;到了用户那台干净机器上,啥系统 Java 都没有,它又没被告诉「去我自带的那个 ./jre 里找」,自然就报找不到。
修法是在 exe4j 的配置里显式给一个搜索路径,优先指向打包进去的那个 JRE 目录。exe4j 的配置是份 xml,对应的是 <searchSequence> 这一段:
<searchSequence> <directory location="./jre ...
GPR 背景去除:擦掉公共水平条带
整张 GPR 剖面上常有一层「每道都长一样」的横向条带,像照片蒙了灰雾。擦掉它,局部目标才显出来。
从哪来有些成分在全测线每一道都几乎相同:
直达波:发射天线直接窜到接收天线,每道开头都有;
地表或天线面的反射、耦合响应;
大范围的均匀层状反射。
它们在每个位置出现的时间和强度都差不多,画出来就是水平横条,盖住局部异常。
平均道就是背景对每个时间样点,把所有道的值求平均,得到一条「平均道」。水平背景在每道都一样,在平均里相干保留;局部目标位置相位不同,互相抵消。
从每道减去这条平均道,背景没了,局部目标活下来。
统一公式:
$$D_{out}[t,x]=D[t,x]-\alpha\cdot\hat B[t,x],\qquad \alpha\in[0,1]$$
$\alpha=1$ 全减;$\alpha<1$ 部分减,适合既看点目标又想看连续界面的情况。
背景估计的几种方法
全局 mean/median:所有道一起求均值或中位数。
滑窗 mean/median:只取当前道附近窗口内的道求平均,适合背景沿测线缓慢变化。
SVD:取数据矩阵前 ...
雷达剖面背景去除的几种做法
探地雷达的剖面图(B-scan)上,经常有一些横向几乎不变的条带或底色——直达波、天线环回响、系统本底。它们跟具体的地下反射没关系,却往往占了大头,把真正想看的弱反射压在下面看不清。背景去除就是想办法把这些共模背景估出来再减掉。
先跟 dewow 划清界限:dewow 沿时间轴(每道内部)去道自己的低频漂移;背景去除沿空间轴(道与道之间)去横向的共模背景。两者方向正交。统一写成一个公式就是 D − α·B̂——估一个背景 B̂,乘个强度系数 α 再减掉,alpha 默认 1.0 就是全减。
难点不在「减」,在「背景 B̂ 怎么估才估得准」。试下来几种路子各有擅长的场景。
mean / median:沿线平均,最朴素最直接的是沿空间方向(跨道)求平均当背景。直达波这种几乎每道都一样的东西,一平均就冒出来了,减掉很干净。把均值换成中位数(median)就是 median 法,好处是抗异常——少数几道的强反射不会把整体背景带偏。
这一类对「横向基本恒定」的背景效果好。但雷达数据里的背景不总是规规矩矩水平的,有时候是斜的(剖面沿线有倾角)、有时候是周期性振铃(ringing),mea ...
离线软件怎么防用户改系统时间绕过到期
前面写过一版防时间回拨,思路是本地存一个「上次见过的时间」,下次启动发现现在比它还早,就判定时间被调过。那招够用但比较朴素,只能挡住随手调表的人。后来又琢磨了一圈,离线环境里想认真防「改系统时间续命」,其实有几条不同路数的思路,单独哪一条都有破法,得叠着用。
先说个大前提:完全离线、又不能联网对时的环境下,这是软件防破解里的经典难题,没有银弹。下面这些都是抬门槛,不是绝对防住。
别看「现在几点」,看「过去留的痕」第一类思路是文件时间戳审计。软件运行的时候,往系统各处(日志、临时目录、配置文件深处、注册表角落)偷偷写一些文件。每次启动去读这些文件的最后修改时间,只要发现当前系统时间比某个历史文件的修改时间还早——说明时间被往回调了,直接锁死。逻辑是:正常情况下,现在不可能比过去还早。
这招成本最低,但文件能被删,删干净了就重置,所以只能当一道初筛。
时间能倒退,运行时长不能第二类是单向计数器,我觉得是更靠谱的方向。墙上的时钟能调,但「软件实际跑了多久」这件事,用户没法自然地把它调回去。
具体两种做法。一种是限制使用次数:许可证带一个最大次数,每次启动在本地加密文件里扣一次,用完就锁。另一 ...
dewow 这一步去的是低频漂移
处理探地雷达(GPR)数据,拿来第一步基本都先做 dewow。刚接触的时候光看名字不知道它在去什么,后来弄明白了,记一下。
先说两个词。雷达每次发射接收,得到的是一条沿时间方向的采样序列,叫一道(A-scan);把很多道沿测线方向排开,就成了二维的剖面图(B-scan)。dewow 处理的是「一道」内部的事。
一道信号本来该围绕 0,实际常常整体漂理论上,去掉直达波以后,一道雷达信号应该围绕 0 这条基线上下抖动——有反射的地方抖一下,没反射的地方贴着 0。实际拿到的数据不是这样,整条信号会慢慢往上拱一段、又往下沉一段,整体不贴着 0,像是在一个缓慢起伏的包络上叠加了高频反射。这个慢漂就叫 wow。
wow 这个词是早期音响那边的行话,本来指唱片转速不稳导致的声音低频飘忽,借过来形容信号的低频基线漂移。它的来源一般是天线跟地面的耦合、仪器本底、线缆感应这些,跟真正的地下反射没关系,但它占着基线、带着整道信号跑偏。
为什么要先去掉它——基线不归零,后面一堆处理都受拖累。算能量、判反射强度的时候,慢漂会垫一层虚假的底;显示的时候,整道压在直流偏置上,弱反射更看不清。
沿时间方向算个滑动平均 ...
keygen 这套软件许可服务是怎么设计的
之前研究了一下 keygen(GitHub 上 keygen-sh/keygen-api,一个开源的软件许可服务),想搞清楚工业级的软件授权到底是怎么组织的。看完最大的感受是,它早就不是那种「写个算法、算出一串序列号、客户端拿同样算法校验」的老路子了——那种算法只要被逆出来就全军覆没。keygen 把授权拆成了一套类似图模型的东西在服务端动态管。
整个体系围绕三个东西转:策略(Policy)、许可证(License)、设备(Machine)。策略是规则模板,比如「能激活 3 台机器、有效期一年、每 30 天必须联网一次」;许可证是卖给用户的实体,绑在某个策略上;设备是许可证真正消耗的额度,每激活一台就建一个 Machine 节点、扣一个名额。这套拆法把「卖了多少份」「每份能绑几台」「谁在用」分得很干净,加规则改策略就行,不用动许可证本身。
离线验签靠非对称加密我觉得这是它最核心的一手。光靠联网请求校验有个致命问题:破解者本地起一个假服务器(mock),把你客户端的请求全拦下来返回「合法」,校验就形同虚设。keygen 的做法是服务端用非对称加密(Ed25519)的私钥给许可证 ...
