Skip to content

Commit bf6ce5a

Browse files
committed
fix: isolated interprocedural logic to add for exemption
1 parent c7e32e9 commit bf6ce5a

28 files changed

Lines changed: 1308 additions & 528 deletions

File tree

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
package dev.skidfuscator.failsafe;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.function.Consumer;
6+
7+
public final class Failsafe {
8+
public interface Action {
9+
/**
10+
* Execute the logic associated with this action when handling an exception.
11+
*
12+
* @param builder The current failsafe builder.
13+
* @param e The exception that triggered this action.
14+
* @param attempt The current attempt count (1-based).
15+
* @param maxAttempts The maximum number of attempts allowed by this action.
16+
* @return true if done (stop), false if continue retrying.
17+
*/
18+
boolean execute(FailsafeBuilder builder, Exception e, int attempt, int maxAttempts);
19+
}
20+
21+
public static final Action RETRY = new Action() {
22+
@Override
23+
public boolean execute(FailsafeBuilder builder, Exception e, int attempt, int maxAttempts) {
24+
int effectiveMax = (maxAttempts > 0) ? maxAttempts : builder.defaultMaxAttempts;
25+
if (attempt < effectiveMax) {
26+
return false; // try again
27+
} else {
28+
// max attempts reached, rethrow
29+
throw asRuntimeException(e);
30+
}
31+
}
32+
};
33+
34+
public static final Action CANCEL = (builder, e, attempt, maxAttempts) -> true;
35+
public static final Action UNDO = (builder, e, attempt, maxAttempts) -> true;
36+
37+
public static FailsafeBuilder run(Runnable code) {
38+
return new FailsafeBuilder().run(code);
39+
}
40+
41+
public static <T> FailsafeBuilder run(Iterable<T> values, Consumer<T> code) {
42+
return new FailsafeBuilder().run(values, code);
43+
}
44+
45+
public static <T> FailsafeBuilder run(T value, Consumer<T> code) {
46+
return new FailsafeBuilder().run(value, code);
47+
}
48+
49+
public static class FailsafeBuilder {
50+
private Runnable baseAction;
51+
private Runnable finalAction;
52+
private Runnable successAction;
53+
54+
private final List<ExceptionHandler<? extends Exception>> exceptionHandlers = new ArrayList<>();
55+
private int defaultMaxAttempts = 1;
56+
57+
private FailsafeBuilder run(Runnable code) {
58+
this.baseAction = code;
59+
return this;
60+
}
61+
62+
private <T> FailsafeBuilder run(Iterable<T> values, Consumer<T> code) {
63+
this.baseAction = () -> {
64+
for (T val : values) {
65+
code.accept(val);
66+
}
67+
};
68+
return this;
69+
}
70+
71+
private <T> FailsafeBuilder run(T value, Consumer<T> code) {
72+
this.baseAction = () -> code.accept(value);
73+
return this;
74+
}
75+
76+
public OnExceptionBuilder<Exception> onException() {
77+
return new OnExceptionBuilder<>(Exception.class, this);
78+
}
79+
80+
public <E extends Exception> OnExceptionBuilder<E> onException(Class<E> exceptionType) {
81+
return new OnExceptionBuilder<>(exceptionType, this);
82+
}
83+
84+
public FailsafeBuilder onSuccess(Runnable action) {
85+
this.successAction = action;
86+
return this;
87+
}
88+
89+
public FailsafeBuilder finallyDo(Runnable action) {
90+
this.finalAction = action;
91+
return this;
92+
}
93+
94+
public void finish() {
95+
if (baseAction == null) {
96+
throw new IllegalStateException("No base action provided. Call run(...) before execute().");
97+
}
98+
99+
boolean success = false;
100+
try {
101+
runWithHandlers();
102+
success = true;
103+
if (successAction != null) {
104+
successAction.run();
105+
}
106+
} finally {
107+
if (finalAction != null) {
108+
finalAction.run();
109+
}
110+
}
111+
}
112+
113+
private void runWithHandlers() {
114+
int attempt = 0;
115+
boolean done = false;
116+
117+
while (!done) {
118+
attempt++;
119+
try {
120+
baseAction.run();
121+
done = true;
122+
} catch (Exception e) {
123+
ExceptionHandler<? extends Exception> handler = findMatchingHandler(e);
124+
if (handler == null) {
125+
throw asRuntimeException(e);
126+
}
127+
128+
// Apply strategy modifiers
129+
handler.applyStrategy(e);
130+
131+
// Execute the action
132+
boolean result = handler.getAction().execute(this, e, attempt, handler.getMaxAttempts());
133+
134+
// If exception occurred and action triggered, run the post callback if any
135+
if (handler.getPostCallback() != null) {
136+
handler.getPostCallback().run();
137+
}
138+
139+
done = result;
140+
}
141+
}
142+
}
143+
144+
private ExceptionHandler<? extends Exception> findMatchingHandler(Exception e) {
145+
for (ExceptionHandler<? extends Exception> handler : exceptionHandlers) {
146+
if (handler.handles(e)) {
147+
return handler;
148+
}
149+
}
150+
return null;
151+
}
152+
153+
void addExceptionHandler(ExceptionHandler<? extends Exception> handler) {
154+
exceptionHandlers.add(handler);
155+
}
156+
}
157+
158+
private static RuntimeException asRuntimeException(Exception e) {
159+
return (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
160+
}
161+
162+
private static class ExceptionHandler<E extends Exception> {
163+
private final Class<E> type;
164+
private final Action action;
165+
private final Runnable strategyModifierRunnable;
166+
private final Consumer<E> strategyModifierConsumer;
167+
private final int maxAttempts;
168+
private final Runnable postCallback;
169+
170+
ExceptionHandler(
171+
Class<E> type,
172+
Action action,
173+
Runnable strategyModifierRunnable,
174+
Consumer<E> strategyModifierConsumer,
175+
int maxAttempts,
176+
Runnable postCallback
177+
) {
178+
this.type = type;
179+
this.action = action;
180+
this.strategyModifierRunnable = strategyModifierRunnable;
181+
this.strategyModifierConsumer = strategyModifierConsumer;
182+
this.maxAttempts = maxAttempts;
183+
this.postCallback = postCallback;
184+
}
185+
186+
boolean handles(Exception e) {
187+
return type.isAssignableFrom(e.getClass());
188+
}
189+
190+
Action getAction() {
191+
return action;
192+
}
193+
194+
int getMaxAttempts() {
195+
return maxAttempts;
196+
}
197+
198+
Runnable getPostCallback() {
199+
return postCallback;
200+
}
201+
202+
void applyStrategy(Exception e) {
203+
if (strategyModifierRunnable != null) {
204+
strategyModifierRunnable.run();
205+
} else if (strategyModifierConsumer != null) {
206+
@SuppressWarnings("unchecked")
207+
E castException = (E) e;
208+
strategyModifierConsumer.accept(castException);
209+
}
210+
}
211+
}
212+
213+
public static class OnExceptionBuilder<E extends Exception> {
214+
private final Class<E> exceptionType;
215+
private final FailsafeBuilder parent;
216+
217+
private Action pendingAction;
218+
private Runnable strategyModifierRunnable;
219+
private Consumer<E> strategyModifierConsumer;
220+
private int maxAttempts = -1;
221+
private Runnable postCallback;
222+
223+
OnExceptionBuilder(Class<E> exceptionType, FailsafeBuilder parent) {
224+
this.exceptionType = exceptionType;
225+
this.parent = parent;
226+
}
227+
228+
public OnExceptionBuilder<E> retry(int attempts) {
229+
this.pendingAction = RETRY;
230+
this.maxAttempts = attempts;
231+
return this;
232+
}
233+
234+
public OnExceptionBuilder<E> cancel() {
235+
this.pendingAction = CANCEL;
236+
return this;
237+
}
238+
239+
public OnExceptionBuilder<E> undo() {
240+
this.pendingAction = UNDO;
241+
return this;
242+
}
243+
244+
public OnExceptionBuilder<E> modify(Consumer<E> modifier) {
245+
this.strategyModifierConsumer = modifier;
246+
return this;
247+
}
248+
249+
public OnExceptionBuilder<E> modify(Runnable modifier) {
250+
this.strategyModifierRunnable = modifier;
251+
return this;
252+
}
253+
254+
/**
255+
* Sets a callback to run if this exception handler is triggered.
256+
* This does not override the action. If no action was specified before,
257+
* defaults to CANCEL.
258+
*/
259+
public FailsafeBuilder execute(Runnable callback) {
260+
this.postCallback = callback;
261+
finalizeHandler();
262+
return parent;
263+
}
264+
265+
/**
266+
* If the user calls execute() without arguments (not requested, but we can keep it),
267+
* we finalize without a callback.
268+
*/
269+
public FailsafeBuilder execute() {
270+
finalizeHandler();
271+
return parent;
272+
}
273+
274+
private void finalizeHandler() {
275+
if (pendingAction == null) {
276+
// No action was set, default to CANCEL
277+
pendingAction = CANCEL;
278+
}
279+
ExceptionHandler<E> handler = new ExceptionHandler<>(
280+
exceptionType,
281+
pendingAction,
282+
strategyModifierRunnable,
283+
strategyModifierConsumer,
284+
maxAttempts,
285+
postCallback
286+
);
287+
parent.addExceptionHandler(handler);
288+
}
289+
}
290+
}
291+

dev.skidfuscator.obfuscator.pureanalysis/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repositories {
1212

1313
dependencies {
1414
api project(':modasm')
15-
api 'com.github.xxDark:SSVM:d559ea90bb'
15+
api 'com.github.terminalsin:SSVM:dev-SNAPSHOT'
1616

1717
testImplementation platform('org.junit:junit-bom:5.10.0')
1818
testImplementation 'org.junit.jupiter:junit-jupiter'

dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.typesafe.config.Config;
44
import com.typesafe.config.ConfigFactory;
5+
import dev.skidfuscator.failsafe.Failsafe;
56
import dev.skidfuscator.jghost.GhostHelper;
67
import dev.skidfuscator.jghost.tree.GhostLibrary;
78
import dev.skidfuscator.obfuscator.analytics.SkidTracker;
@@ -10,7 +11,6 @@
1011
import dev.skidfuscator.obfuscator.creator.SkidCache;
1112
import dev.skidfuscator.obfuscator.dependency.CommonDependency;
1213
import dev.skidfuscator.obfuscator.dependency.DependencyDownloader;
13-
import dev.skidfuscator.obfuscator.dependency.matcher.DependencyMatcher;
1414
import dev.skidfuscator.obfuscator.directory.SkiddedDirectory;
1515
import dev.skidfuscator.obfuscator.event.EventBus;
1616
import dev.skidfuscator.obfuscator.event.impl.TransformEvent;
@@ -48,10 +48,11 @@
4848
import dev.skidfuscator.obfuscator.transform.impl.flow.*;
4949
import dev.skidfuscator.obfuscator.transform.impl.flow.condition.BasicConditionTransformer;
5050
import dev.skidfuscator.obfuscator.transform.impl.flow.exception.BasicExceptionTransformer;
51+
import dev.skidfuscator.obfuscator.transform.impl.flow.interprocedural.InterproceduralTransformer;
52+
import dev.skidfuscator.obfuscator.transform.impl.flow.interprocedural.RandomInitTransformer;
5153
import dev.skidfuscator.obfuscator.transform.impl.misc.AhegaoTransformer;
5254
import dev.skidfuscator.obfuscator.transform.impl.number.NumberTransformer;
5355
import dev.skidfuscator.obfuscator.transform.impl.string.StringEncryptionType;
54-
import dev.skidfuscator.obfuscator.transform.impl.string.StringTransformer;
5556
import dev.skidfuscator.obfuscator.transform.impl.string.StringTransformerV2;
5657
import dev.skidfuscator.obfuscator.util.ConsoleColors;
5758
import dev.skidfuscator.obfuscator.util.MapleJarUtil;
@@ -79,7 +80,6 @@
7980
import org.piwik.java.tracking.PiwikRequest;
8081
import org.topdank.byteengineer.commons.data.JarClassData;
8182
import org.topdank.byteengineer.commons.data.JarContents;
82-
import org.topdank.byteengineer.commons.data.JarResource;
8383

8484
import java.io.File;
8585
import java.io.IOException;
@@ -675,6 +675,9 @@ public List<Transformer> getTransformers() {
675675
}
676676

677677
transformers.addAll(Arrays.asList(
678+
// BASE
679+
new RandomInitTransformer(this),
680+
new InterproceduralTransformer(this),
678681
// ----- COMMUNITY -----
679682
new NumberTransformer(this),
680683
new SwitchTransformer(this),
@@ -751,14 +754,26 @@ private void _verify() {
751754
System.exit(1);
752755
return;
753756
}
754-
commonDependencies.forEach(e -> {
755-
LOGGER.warn("Found common dependency: " + e.name() + "...\n");
756-
dependencyDownloader.download(e);
757-
});
758757

759-
760-
final Path mappingsDir = Paths.get("mappings-cloud");
761-
this.importMappingFolder(mappingsDir.toFile());
758+
// [failsafe] some people cancel mid download, corrupting the library
759+
// and sometimes i fuck up, rendering it wrong
760+
// so we should nuke and redownload common deps to
761+
// prevent bad things. if it fails again, i want to know
762+
// about it
763+
764+
Failsafe.run(() -> downloadCommonDependencies(commonDependencies))
765+
.onException()
766+
.retry(1)
767+
.execute(() -> {
768+
LOGGER.warn("Failed to download common dependencies... retrying...\n");
769+
final Path mappingsDir = Paths.get("mappings-cloud");
770+
try {
771+
Files.delete(mappingsDir);
772+
} catch (IOException e) {
773+
LOGGER.error("Failed to download common dependencies again... Please contact the developer\n", e);
774+
}
775+
})
776+
.finish();
762777

763778
LOGGER.warn(String.format(
764779
"Resolved %d common dependencies... retrying verification...\n",
@@ -771,6 +786,16 @@ private void _verify() {
771786
LOGGER.log("Finished verification!");
772787
}
773788

789+
private void downloadCommonDependencies(Collection<CommonDependency> dependencies) {
790+
dependencies.forEach(e -> {
791+
LOGGER.warn("Found common dependency: " + e.name() + "...\n");
792+
dependencyDownloader.download(e);
793+
});
794+
795+
final Path mappingsDir = Paths.get("mappings-cloud");
796+
this.importMappingFolder(mappingsDir.toFile());
797+
}
798+
774799
private void importMappingFolder(final File folder) {
775800
for (File lib : folder.listFiles()) {
776801
if (lib.isDirectory()) {

0 commit comments

Comments
 (0)