Огнестрел: таблица стволов с вики и границы реверса
- weapons.csv: 23 ствола с уроном, множителем в голову, темпом, магазином, перезарядкой, разбросом (конус прицела), отдачей, типом патрона, верстаком и ценами исследования. Источники - официальная вики и community-вики, расхождения помечены в колонке source. Не хватает цены (2 из 23) и дальности (18 из 23). Границы реверса ремейка, чтобы не тратить на них время снова: - классы в game.jar помечены DE AD C0 DE и зашифрованы своим форматом, не однобайтовым XOR как ассеты; единственный незашифрованный класс - точка входа net/minecraft/client/main/RustexMain, в ней видны два статических дешифратора строк (qnbt.super и vyuhke.super), а сами классы зашифрованы; - REA не берет .class как цель (только архивы), Ghidra 12.1.4 стоит в D:\ghidra-tools и через analyzeHeadless импортирует распакованные классы; - главное: урона, отдачи и спрея огнестрела в клиенте нет вообще, это серверные данные. Клиент знает только магазин, тип патрона, зум ADS, прочность и reach. REA даст формулы, а не числа, поэтому числа берём с вики. Инструменты в tools/: inspect-constants.py (разбор constant pool класса) и find-decryptor.py (поиск маркера и слов дешифратора). План дополнен этими находками.
This commit is contained in:
@@ -548,7 +548,9 @@ heavy plate helmet 90 % пули, hazmat 30/30; по футболке источ
|
|||||||
|
|
||||||
**Полный список предметов с характеристиками** — в
|
**Полный список предметов с характеристиками** — в
|
||||||
`rsx-protocol/src/main/resources/items/items.csv` (218 шт, выгружено из ремейка,
|
`rsx-protocol/src/main/resources/items/items.csv` (218 шт, выгружено из ремейка,
|
||||||
п. 1.6). Названия и описания — в `assets/rustex/data/item-texts.json` (543 шт).
|
п. 1.6). Названия и описания — в `rsx-protocol/src/main/resources/items/item-texts.json`
|
||||||
|
(543 шт). Огнестрел — в `weapons.csv` (23 ствола: урон, темп, магазин, разброс,
|
||||||
|
отдача, патрон, верстак, цены исследования).
|
||||||
|
|
||||||
### 1.8 Строительные объекты и сокеты
|
### 1.8 Строительные объекты и сокеты
|
||||||
|
|
||||||
@@ -984,8 +986,10 @@ rsx-client\scripts\run-game.ps1 # клиент
|
|||||||
4. **Шаг D** — конфиг, мир 4000×4000 один раз, барьер.
|
4. **Шаг D** — конфиг, мир 4000×4000 один раз, барьер.
|
||||||
5. **Шаг E** — транспорт на NIO.
|
5. **Шаг E** — транспорт на NIO.
|
||||||
6. **Шаг F** — DistantView.
|
6. **Шаг F** — DistantView.
|
||||||
7. **REA** — найти в коде ремейка таблицу оружия по `rweaponid` (урон, отдача, спрей,
|
7. **REA** — код ремейка прочитать нельзя без взлома загрузчика (классы помечены
|
||||||
темп) и шифр конфигов `.obj/.rrs/.nbt`; до этого — цифры с официальной вики.
|
`DEADC0DE`), и урон/отдача/спрей огнестрела в клиенте всё равно серверные.
|
||||||
|
Числа взяты с вики (`weapons.csv`). Если формулы понадобятся — импортировать
|
||||||
|
распакованные классы в Ghidra headless и искать дешифратор.
|
||||||
|
|
||||||
### 6.4 Мелочи, которые легко упустить
|
### 6.4 Мелочи, которые легко упустить
|
||||||
|
|
||||||
@@ -1000,6 +1004,20 @@ rsx-client\scripts\run-game.ps1 # клиент
|
|||||||
- **База предметов** — `rsx-protocol/src/main/resources/items/items.csv` (218 шт,
|
- **База предметов** — `rsx-protocol/src/main/resources/items/items.csv` (218 шт,
|
||||||
из `models/stacks.json`), описания — `assets/rustex/data/item-texts.json` (543 шт),
|
из `models/stacks.json`), описания — `assets/rustex/data/item-texts.json` (543 шт),
|
||||||
звуки — `rsx-protocol/src/main/resources/items/sounds.csv` (505 событий).
|
звуки — `rsx-protocol/src/main/resources/items/sounds.csv` (505 событий).
|
||||||
|
- **Огнестрел** — `rsx-protocol/src/main/resources/items/weapons.csv` (23 ствола):
|
||||||
|
урон, множитель в голову, темп, магазин, перезарядка, разброс (конус прицела),
|
||||||
|
отдача, тип патрона, верстак, цены исследования. Собрано с официальной вики и
|
||||||
|
community-вики; расхождения помечены в колонке `source`.
|
||||||
|
- ⚠️ **Код ремейка прочитать нельзя без взлома.** Классы в `game.jar` помечены
|
||||||
|
`DE AD C0 DE` и зашифрованы своим форматом (не однобайтовым XOR, как ассеты).
|
||||||
|
Единственный незашифрованный класс — точка входа
|
||||||
|
`net/minecraft/client/main/RustexMain`; в ней видны два статических дешифратора
|
||||||
|
строк (`qnbt.super`, `vyuhke.super`), а сами классы зашифрованы. REA берёт
|
||||||
|
архивы, но не `.class`; Ghidra 12.1.4 стоит (`D:\ghidra-tools`), и через
|
||||||
|
`analyzeHeadless` можно импортировать распакованные классы.
|
||||||
|
**И важнее:** урон, отдача и спрей огнестрела в клиенте **отсутствуют** — это
|
||||||
|
серверные данные, в клиенте есть только магазин, тип патрона, зум ADS,
|
||||||
|
прочность и reach. То есть REA даст формулы, а не числа. Числа — из вики.
|
||||||
- Пути: `G:\RustNotMC` (репозиторий), `G:\decompiled-rustex` (референс, только читать),
|
- Пути: `G:\RustNotMC` (репозиторий), `G:\decompiled-rustex` (референс, только читать),
|
||||||
`G:\rustexremake` (ремейк: `gamefiles\res`, `gamefiles\game.jar`, `working_dir`).
|
`G:\rustexremake` (ремейк: `gamefiles\res`, `gamefiles\game.jar`, `working_dir`).
|
||||||
- ⚠️ **`G:\decompiled-rustex` — НЕ источник фактов.** Он копиял community-вику 2017–2020
|
- ⚠️ **`G:\decompiled-rustex` — НЕ источник фактов.** Он копиял community-вику 2017–2020
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
id,name,damage,headshot,fire_rate,magazine,reload,range,accuracy,recoil,ammo_type,price,bench,blueprint,source
|
||||||
|
m92pistol,M92 Pistol,45,x2,400 RPM,15,2.39 sec,90 m,aim cone 1 deg / accuracy 75%,medium,Pistol Bullet (also HV + incendiary),250 scrap (bandit camp),,,official=https://wiki.facepunch.com/rust/item/pistol.m92 ; community=https://rustwiki.net/items/m92-pistol ; fandom=https://rust.fandom.com/wiki/M92_Pistol ; conflicts: headshot official x2 vs community 1.5x ; recoil community medium vs fandom very low ; price 250 scrap from fandom ; official lists weapon as not craftable and not researchable
|
||||||
|
revolver,Revolver,35,x2,343 RPM,8,3.4 sec,,aim cone 0.75 deg / accuracy 65%,low,Pistol Bullet (also HV + incendiary),,Workbench Level 1,Research Table 30 scrap ; Tech Tree 125 scrap (bench level 1),official=https://wiki.facepunch.com/rust/item/pistol.revolver ; community=https://rustwiki.net/items/revolver ; fandom=https://rust.fandom.com/wiki/Revolver ; conflicts: headshot official x2 vs community 1.5x ; range not documented
|
||||||
|
pythonrevolver,Python Revolver,55,x2,400 RPM,6,3.75 sec,72 m,aim cone 0.5 deg / accuracy 80%,medium,Pistol Bullet (also HV + incendiary),,Workbench Level 2,Research Table 60 scrap ; Tech Tree 250 scrap (bench level 2),official=https://wiki.facepunch.com/rust/item/pistol.python ; community=https://rustwiki.net/items/python-revolver ; fandom=https://rust.fandom.com/wiki/Python_Revolver ; conflicts: headshot official x2 vs community 1.5x ; recoil community medium vs fandom high ; blueprint fandom says 125 scrap research vs official 60 + 250 ; range 72 m from fandom only
|
||||||
|
eokapistol,Eoka Pistol,80,x2,30 RPM,1,2 sec,Short,aim cone 2 deg / accuracy 20%,very high,12 Gauge Shell (handmade / buckshot / slug / incendiary),,None (no workbench needed),Research Table 15 scrap ; blueprint known by default (Industrial Crafter),official=https://wiki.facepunch.com/rust/item/pistol.eoka ; community=https://rustwiki.net/items/eoka-pistol ; fandom=https://rust.fandom.com/wiki/Eoka_Pistol ; conflicts: damage official 80 (12 gauge slug) vs community 180 (total per shell) ; fire rate community 30 RPM vs official DPS 53.3 (slug) = 40 RPM ; recoil community very high vs fandom high ; range Short from fandom only
|
||||||
|
semiautomaticpistol,Semi-Automatic Pistol,40,x2,400 RPM,10,2.9 sec,Low,aim cone 0.75 deg / accuracy 70%,low,Pistol Bullet (also HV + incendiary),,Workbench Level 2,Research Table 60 scrap ; Tech Tree 250 scrap (bench level 2),official=https://wiki.facepunch.com/rust/item/pistol.semiauto ; community=https://rustwiki.net/items/semi-automatic-pistol ; fandom=https://rust.fandom.com/wiki/Semi_Automatic_Pistol ; conflicts: headshot official x2 vs community 1.5x ; recoil community low vs fandom medium ; range Low from fandom only
|
||||||
|
waterpipeshotgun,Waterpipe Shotgun,80,x2,13 RPM,1,4.5 sec,Short,aim cone 1 deg / accuracy 25%,very high,12 Gauge Shell (handmade / buckshot / slug),,Workbench Level 1,Research Table 30 scrap ; Tech Tree 125 scrap (bench level 1),official=https://wiki.facepunch.com/rust/item/shotgun.waterpipe ; community=https://rustwiki.net/items/waterpipe-shotgun ; fandom=https://rust.fandom.com/wiki/Waterpipe_Shotgun ; conflicts: damage official 80 (12 gauge slug; buckshot 15 per pellet / handmade 9 per pellet) vs community 180 (total) ; headshot official x2 vs community 1.5x ; reload community 4.5 sec vs fandom 6 sec ; fire rate community 13 RPM vs official DPS 40 (slug) = 30 RPM ; blueprint fandom says 100 scrap research vs official 30 + 125
|
||||||
|
pumpshotgun,Pump Shotgun,80,x2,55 RPM,8,5.5 sec,10 m,aim cone 0 deg / accuracy 40%,high,12 Gauge Shell (handmade / buckshot / slug / incendiary),,Workbench Level 2,Research Table 60 scrap ; Tech Tree 250 scrap (bench level 2),official=https://wiki.facepunch.com/rust/item/shotgun.pump ; community=https://rustwiki.net/items/pump-shotgun ; fandom=https://rust.fandom.com/wiki/Pump_Shotgun ; conflicts: damage official 80 (12 gauge slug; buckshot 15 per pellet / handmade 9 per pellet) vs community 210 (total) ; magazine official description says 8 rounds vs fandom 6 ; headshot official x2 vs community 1.5x ; range 10 m from fandom only
|
||||||
|
doublebarrelshotgun,Double Barrel Shotgun,80,x2,120 RPM,2,5.5 sec,Short,aim cone 0.5 deg / accuracy 35%,very high,12 Gauge Shell (handmade / buckshot / slug / incendiary),,Workbench Level 1,Research Table 60 scrap ; Tech Tree 125 scrap (bench level 1),official=https://wiki.facepunch.com/rust/item/shotgun.double ; community=https://rustwiki.net/items/double-barrel-shotgun ; fandom=https://rust.fandom.com/wiki/Double_Barrel_Shotgun ; conflicts: damage official 80 (12 gauge slug; buckshot 15 per pellet / handmade 9 per pellet) vs community 180 (total) ; headshot official x2 vs community 1.5x ; range Short from fandom only
|
||||||
|
spas12shotgun,Spas-12 Shotgun,60,x2,240 RPM,6,5.8 sec,,aim cone 0 deg,,12 Gauge Shell (handmade / buckshot / slug / incendiary),,,,official=https://wiki.facepunch.com/rust/item/shotgun.spas12 ; community=https://rustwiki.net/items/spas-12-shotgun ; fandom=https://rust.fandom.com/wiki/Spas-12 ; conflicts: damage official 60 (12 gauge slug; buckshot 11.2 per pellet / handmade 6.8 per pellet) vs community 157.5 vs fandom 135 ; fire rate official DPS 240 (slug) = 240 RPM vs fandom 180 RPM ; headshot official x2 (slug) ; official lists weapon as not craftable and not researchable
|
||||||
|
nailgun,Nailgun,18,x1.5,400 RPM,16,3.1 sec,short (fires in an arc),aim cone 0.75 deg,,Nailgun Nails,,Workbench Level 1,Research Table 30 scrap ; blueprint known by default (Industrial Crafter),official=https://wiki.facepunch.com/rust/item/pistol.nailgun ; community=https://rustwiki.net/items/nailgun ; fandom=https://rust.fandom.com/wiki/Nailgun ; conflicts: recoil not documented by any source
|
||||||
|
boltactionrifle,Bolt Action Rifle,80,x2,35 RPM,4,5 sec,350-500 m,aim cone 0 deg / accuracy 92%,medium,5.56 Rifle Ammo (also HV + explosive + incendiary),,Workbench Level 3,Research Table 120 scrap ; Tech Tree 500 scrap (bench level 3),official=https://wiki.facepunch.com/rust/item/rifle.bolt ; community=https://rustwiki.net/items/bolt-action-rifle ; fandom=https://rust.fandom.com/wiki/Bolt_Action_Rifle ; conflicts: damage official 80 vs fandom body 75 (+15 bleed) ; recoil community medium vs fandom severe ; range 350-500 m from fandom only
|
||||||
|
huntingbow,Hunting Bow,50,x1.5,60 RPM,1,1 sec,~150 m,aim cone 1 deg / accuracy 75%,low,Arrow (wooden / bone / fire / high velocity),,None (no workbench needed),Research Table 120 scrap ; blueprint known by default (Industrial Crafter),official=https://wiki.facepunch.com/rust/item/bow.hunting ; community=https://rustwiki.net/items/hunting-bow ; fandom=https://rust.fandom.com/wiki/Hunting_Bow ; conflicts: headshot official x1.5 vs community 2x ; fire rate community 60 RPM vs official DPS 40 (wooden arrow) = 48 RPM
|
||||||
|
crossbow,Crossbow,60,x1.5,17 RPM,1,3.6 sec,~250 m,aim cone 1 deg / accuracy 80%,low,Arrow (wooden / bone / fire / high velocity),,Workbench Level 1,Research Table 30 scrap ; blueprint known by default (Industrial Crafter),official=https://wiki.facepunch.com/rust/item/crossbow ; community=https://rustwiki.net/items/crossbow ; fandom=https://rust.fandom.com/wiki/Crossbow ; conflicts: headshot official x1.5 vs community 2x ; fire rate community 17 RPM vs official DPS 60 (wooden arrow) = 60 RPM ; reload community 3.6 sec vs fandom 4.783 sec
|
||||||
|
hmlmg,HMLMG,57.5,x2,480 RPM,60,6.5 sec,,aim cone 0.2 deg,,5.56 Rifle Ammo (also HV + explosive + incendiary),,Workbench Level 3,Research Table 120 scrap ; Tech Tree 500 scrap (bench level 3),official=https://wiki.facepunch.com/rust/item/hmlmg ; community=https://rustwiki.net/items/hmlmg ; fandom has no page for this weapon ; conflicts: recoil and range not documented
|
||||||
|
thompson,Thompson,37.5,x2,462 RPM,20,4 sec,~250 m,aim cone 0.5 deg / accuracy 60%,medium,Pistol Bullet (also HV + incendiary),,Workbench Level 2,Research Table 60 scrap ; Tech Tree 250 scrap (bench level 2),official=https://wiki.facepunch.com/rust/item/smg.thompson ; community=https://rustwiki.net/items/thompson ; fandom=https://rust.fandom.com/wiki/Thompson ; conflicts: headshot official x2 vs community 1.5x ; recoil community medium vs fandom low/medium ; range ~250 m from fandom only
|
||||||
|
customsmg,Custom SMG,30,x2,600 RPM,30,4 sec,200 m,aim cone 0.5 deg / accuracy 55%,low,Pistol Bullet (also HV + incendiary),,Workbench Level 2,Research Table 60 scrap ; Tech Tree 250 scrap (bench level 2),official=https://wiki.facepunch.com/rust/item/smg.2 ; community=https://rustwiki.net/items/custom-smg ; fandom=https://rust.fandom.com/wiki/Custom_SMG ; conflicts: headshot official x2 vs community 1.5x ; damage official 30 vs fandom body 35 ; recoil community low vs fandom medium
|
||||||
|
mp5a4,MP5A4,37.5,x2,600 RPM,30,4 sec,72 m,aim cone 0.5 deg / accuracy 65%,medium,Pistol Bullet (also HV + incendiary),,Workbench Level 3,Research Table 120 scrap ; Tech Tree 500 scrap (bench level 3),official=https://wiki.facepunch.com/rust/item/smg.mp5 ; community=https://rustwiki.net/items/mp5a4 ; fandom=https://rust.fandom.com/wiki/MP5A4 ; conflicts: headshot official x2 vs community 1.5x ; range 72 m from fandom only ; fandom attackinterval 600 RPM agrees
|
||||||
|
assaultrifle,Assault Rifle,50,x2,450 RPM,30,4.4 sec,250-400 m,aim cone 0.2 deg / accuracy 62%,high,5.56 Rifle Ammo (also HV + explosive + incendiary),,Workbench Level 3,Research Table 120 scrap ; Tech Tree 500 scrap (bench level 3),official=https://wiki.facepunch.com/rust/item/rifle.ak ; community=https://rustwiki.net/items/assault-rifle ; fandom=https://rust.fandom.com/wiki/Assault_Rifle ; conflicts: damage official 50 vs fandom body 51 ; headshot official x2 vs fandom flat 100 ; range 250-400 m from fandom only
|
||||||
|
m39rifle,M39 Rifle,50,x2,343 RPM,20,3.25 sec,,aim cone 0.1 deg / accuracy 85%,medium,5.56 Rifle Ammo (also HV + explosive + incendiary),400 scrap (bandit camp),,,official=https://wiki.facepunch.com/rust/item/rifle.m39 ; community=https://rustwiki.net/items/m39-rifle ; fandom=https://rust.fandom.com/wiki/M39_Rifle ; conflicts: official lists weapon as not craftable and not researchable ; price 400 scrap from fandom ; range not documented
|
||||||
|
lr300assaultrifle,LR-300 Assault Rifle,40,x2,500 RPM,30,4 sec,188 m,aim cone 0.2 deg / accuracy 70%,medium,5.56 Rifle Ammo (also HV + explosive + incendiary),,,,official=https://wiki.facepunch.com/rust/item/rifle.lr300 ; community=https://rustwiki.net/items/lr-300-assault-rifle ; fandom=https://rust.fandom.com/wiki/LR-300_Assault_Rifle ; conflicts: recoil community medium vs fandom low ; official lists weapon as not craftable and not researchable ; range 188 m from fandom only
|
||||||
|
m249,M249,65,x2,500 RPM,100,7.5 sec,Moderate,aim cone 0.2 deg / accuracy 50%,very high,5.56 Rifle Ammo (also HV + explosive + incendiary),,,,official=https://wiki.facepunch.com/rust/item/lmg.m249 ; community=https://rustwiki.net/items/m249 ; fandom=https://rust.fandom.com/wiki/M249 ; conflicts: official lists weapon as not craftable and not researchable ; range Moderate from fandom only
|
||||||
|
sks,SKS,42.5,x2,400 RPM,15,4.4 sec,,aim cone 0.25 deg,,5.56 Rifle Ammo (also HV + explosive + incendiary),,Workbench Level 3,Research Table 60 scrap ; Tech Tree 500 scrap (bench level 3),official=https://wiki.facepunch.com/rust/item/rifle.sks ; community=https://rustwiki.net/items/sks ; fandom has no page for this weapon ; conflicts: recoil and range not documented
|
||||||
|
rocketlauncher,Rocket Launcher,0,x1,10 RPM,1,6.162 sec,~250 m,aim cone 2.25 deg / accuracy 90%,very high,Rocket (also HV + incendiary + smoke),,Workbench Level 2,Research Table 120 scrap ; Tech Tree 250 scrap (bench level 2),official=https://wiki.facepunch.com/rust/item/rocket.launcher ; community=https://rustwiki.net/items/rocket-launcher ; fandom=https://rust.fandom.com/wiki/Rocket_Launcher ; conflicts: damage official 0 (rockets do no direct hit damage) vs community 350 (explosion damage) ; blueprint fandom says 500 scrap research vs official 120 + 250 ; range ~250 m from fandom only
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Find who reads the encrypted classes of the remake.
|
||||||
|
|
||||||
|
The class files inside game.jar start with DE AD C0 DE and are not readable
|
||||||
|
after that. Something has to decrypt them at runtime, and that something
|
||||||
|
lives in the launcher, in one of the jars or in the java directory. This
|
||||||
|
script looks for the marker and for the words that go with it.
|
||||||
|
|
||||||
|
Usage: find-decryptor.py <file or folder> [...]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
MARKER = b"\xde\xad\xc0\xde"
|
||||||
|
WORDS = [b"deadcode", b"DEADC0DE", b"decrypt", b"Decrypt", b"cipher", b"Cipher",
|
||||||
|
b"ClassLoader", b"defineClass", b"xload", b"scramble", b"obfus"]
|
||||||
|
|
||||||
|
|
||||||
|
def scan(path):
|
||||||
|
hits = []
|
||||||
|
try:
|
||||||
|
data = path.read_bytes()
|
||||||
|
except OSError:
|
||||||
|
return hits
|
||||||
|
pos = data.find(MARKER)
|
||||||
|
if pos >= 0:
|
||||||
|
hits.append(("marker", pos))
|
||||||
|
for word in WORDS:
|
||||||
|
pos = data.find(word)
|
||||||
|
if pos >= 0:
|
||||||
|
hits.append((word.decode("ascii", "replace"), pos))
|
||||||
|
return hits
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
if len(argv) < 2:
|
||||||
|
print(__doc__)
|
||||||
|
return 1
|
||||||
|
for name in argv[1:]:
|
||||||
|
path = Path(name)
|
||||||
|
if path.is_dir():
|
||||||
|
for child in path.rglob("*"):
|
||||||
|
if child.is_file() and child.stat().st_size < 200_000_000:
|
||||||
|
hits = scan(child)
|
||||||
|
for what, where in hits:
|
||||||
|
print("%-70s %s at %d" % (str(child)[-70:], what, where))
|
||||||
|
elif path.is_file():
|
||||||
|
for what, where in scan(path):
|
||||||
|
print("%-70s %s at %d" % (str(path)[-70:], what, where))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Look at the constant pool of a class of the remake and see what is hidden.
|
||||||
|
|
||||||
|
The remake obfuscates the string constants: they are not readable in the class
|
||||||
|
file. This script pulls the UTF-8 entries out of the constant pool and tries
|
||||||
|
to work out how they were scrambled, because the same cipher is probably what
|
||||||
|
protects the .obj and .rrs files.
|
||||||
|
|
||||||
|
Usage: inspect-constants.py <class file> [...]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
TAGS = {
|
||||||
|
1: "Utf8", 3: "Integer", 4: "Float", 5: "Long", 6: "Double",
|
||||||
|
7: "Class", 8: "String", 9: "Fieldref", 10: "Methodref", 11: "InterfaceMethodref",
|
||||||
|
12: "NameAndType", 15: "MethodHandle", 16: "MethodType", 17: "Dynamic",
|
||||||
|
18: "InvokeDynamic", 19: "Module", 20: "Package",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def read_pool(data):
|
||||||
|
"""The constant pool of a class: a list of (tag, value) pairs."""
|
||||||
|
if data[:4] != b"\xca\xfe\xba\xbe":
|
||||||
|
raise ValueError("not a class file")
|
||||||
|
count = struct.unpack_from(">H", data, 8)[0]
|
||||||
|
pool = []
|
||||||
|
pos = 10
|
||||||
|
i = 1
|
||||||
|
while i < count:
|
||||||
|
tag = data[pos]
|
||||||
|
pos += 1
|
||||||
|
if tag == 1:
|
||||||
|
length = struct.unpack_from(">H", data, pos)[0]
|
||||||
|
pos += 2
|
||||||
|
pool.append((tag, data[pos:pos + length]))
|
||||||
|
pos += length
|
||||||
|
elif tag in (3, 4):
|
||||||
|
pool.append((tag, data[pos:pos + 4]))
|
||||||
|
pos += 4
|
||||||
|
elif tag in (5, 6):
|
||||||
|
pool.append((tag, data[pos:pos + 8]))
|
||||||
|
pos += 8
|
||||||
|
elif tag in (7, 8, 16, 19, 20):
|
||||||
|
pool.append((tag, struct.unpack_from(">H", data, pos)[0]))
|
||||||
|
pos += 2
|
||||||
|
elif tag in (9, 10, 11, 12, 17, 18):
|
||||||
|
pool.append((tag, struct.unpack_from(">HH", data, pos)))
|
||||||
|
pos += 4
|
||||||
|
elif tag == 15:
|
||||||
|
pool.append((tag, data[pos:pos + 3]))
|
||||||
|
pos += 3
|
||||||
|
else:
|
||||||
|
raise ValueError("unknown tag %d at %d" % (tag, pos))
|
||||||
|
if tag in (5, 6):
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
return pool, pos
|
||||||
|
|
||||||
|
|
||||||
|
def printable(data):
|
||||||
|
good = 0
|
||||||
|
for b in data:
|
||||||
|
if 32 <= b < 127:
|
||||||
|
good += 1
|
||||||
|
return good / max(1, len(data))
|
||||||
|
|
||||||
|
|
||||||
|
def crack(text):
|
||||||
|
"""The single byte key that makes a string readable, or None."""
|
||||||
|
best = None
|
||||||
|
for key in range(1, 256):
|
||||||
|
out = bytes(b ^ key for b in text)
|
||||||
|
score = printable(out)
|
||||||
|
if score > 0.98 and all(32 <= b < 127 or b in (9, 10, 13) for b in out):
|
||||||
|
best = (key, out)
|
||||||
|
break
|
||||||
|
return best
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
if len(argv) < 2:
|
||||||
|
print(__doc__)
|
||||||
|
return 1
|
||||||
|
for name in argv[1:]:
|
||||||
|
path = Path(name)
|
||||||
|
data = path.read_bytes()
|
||||||
|
pool, _ = read_pool(data)
|
||||||
|
strings = [v for t, v in pool if t == 1]
|
||||||
|
readable = [s for s in strings if printable(s) > 0.95]
|
||||||
|
hidden = [s for s in strings if printable(s) <= 0.95]
|
||||||
|
print("=== %s: %d strings, %d readable, %d hidden"
|
||||||
|
% (path.name, len(strings), len(readable), len(hidden)))
|
||||||
|
for s in readable[:12]:
|
||||||
|
print(" plain %r" % s[:60])
|
||||||
|
for s in hidden[:8]:
|
||||||
|
cracked = crack(s)
|
||||||
|
if cracked:
|
||||||
|
print(" xor %r key 0x%02X" % (s[:40], cracked[0]))
|
||||||
|
else:
|
||||||
|
print(" hidden %r" % s[:40])
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
||||||
Reference in New Issue
Block a user