今天又是值班的一天 闲着也是闲着 之前LOG4J还没仔细看过 今天抽空调一下 记录一下学习过程
测试代码如下
首先获取Logger对象后 调用info记录日志(这里我设置了等级)
在读取xml配置后 进入 logmessage
这里把string打包成message后传入logmessagesafely方法
private void logMessageSafely(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
this.logMessageTrackRecursion(fqcn, level, marker, message, throwable);
} finally {
ReusableMessageFactory.release(message);
}
}
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
private void tryLogMessage(final String fqcn, final StackTraceElement location, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
this.log(level, marker, fqcn, location, message, throwable);
} catch (Exception var8) {
this.handleLogMessageException(var8, fqcn, message);
}
}
这里获取配置中配置的ReliabilityStrategy 并调用其中对应的log方法
其中config是LoggerConfig对象
public void log(final String loggerName, final String fqcn, final StackTraceElement location, final Marker marker, final Level level, final Message data, final Throwable t) {
List<Property> props = null;
if (!this.propertiesRequireLookup) {
props = this.properties;
} else if (this.properties != null) {
props = new ArrayList(this.properties.size());
LogEvent event = Log4jLogEvent.newBuilder().setMessage(data).setMarker(marker).setLevel(level).setLoggerName(loggerName).setLoggerFqcn(fqcn).setThrown(t).build();
for(int i = 0; i < this.properties.size(); ++i) {
Property prop = (Property)this.properties.get(i);
String value = prop.isValueNeedsLookup() ? this.config.getStrSubstitutor().replace(event, prop.getValue()) : prop.getValue();
((List)props).add(Property.createProperty(prop.getName(), value));
}
}
LogEvent logEvent = this.logEventFactory instanceof LocationAwareLogEventFactory ? ((LocationAwareLogEventFactory)this.logEventFactory).createEvent(loggerName, marker, fqcn, location, level, data, (List)props, t) : this.logEventFactory.createEvent(loggerName, marker, fqcn, level, data, (List)props, t);
try {
this.log(logEvent, LoggerConfig.LoggerConfigPredicate.ALL);
} finally {
ReusableLogEventFactory.release(logEvent);
}
}
其中逻辑会走到17行 这里会调用默认的对应的日志事件createEvent方法 处理后 打包成一个LogEvent对象
private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level, final Message message, final Throwable thrown, final ThrowableProxy thrownProxy, final StringMap contextData, final ContextStack contextStack, final long threadId, final String threadName, final int threadPriority, final StackTraceElement source, final long nanoTime) {
this.instant = new MutableInstant();
this.endOfBatch = false;
this.loggerName = loggerName;
this.marker = marker;
this.loggerFqcn = loggerFQCN;
this.level = level == null ? Level.OFF : level;
this.message = message; //payload被打包到这里了
this.thrown = thrown;
this.thrownProxy = thrownProxy;
this.contextData = contextData == null ? ContextDataFactory.createContextData() : contextData;
this.contextStack = (ContextStack)(contextStack == null ? ThreadContext.EMPTY_STACK : contextStack);
this.threadId = threadId;
this.threadName = threadName;
this.threadPriority = threadPriority;
this.source = source;
if (message instanceof LoggerNameAwareMessage) {
((LoggerNameAwareMessage)message).setLoggerName(loggerName);
}
this.nanoTime = nanoTime;
}
打包完后往下会调用this.log方法 并把刚刚返回的logevent对象传进去处理
try {
this.log(logEvent, LoggerConfig.LoggerConfigPredicate.ALL);
} finally {
ReusableLogEventFactory.release(logEvent);
}
protected void log(final LogEvent event, final LoggerConfig.LoggerConfigPredicate predicate) {
if (!this.isFiltered(event)) { //没设置过滤器
this.processLogEvent(event, predicate); //跟到这
}
}
private void processLogEvent(final LogEvent event, final LoggerConfig.LoggerConfigPredicate predicate) {
event.setIncludeLocation(this.isIncludeLocation());
if (predicate.allow(this)) {
this.callAppenders(event); //跟入这里 重点方法
}
this.logParent(event, predicate);
}
通过方法名称可以知道是调用appenders
appenders是控制日志输出的方式,通过设置不同的appenders可以达到不同的输出形式 官方提供了以下
1、FileAppender 普通地输出到本地文件
2、FlumeAppender 将几个不同源的日志汇集、集中到一处。
3、JMSQueueAppender & JMSTopicAppender 与JMS相关的日志输出
4、RewriteAppender 对日志事件进行掩码或注入信息
5、RollingFileAppender 对日志文件进行封存(详细)
6、RoutingAppender 在输出地之间进行筛选路由
7、SMTPAppender 将LogEvent发送到指定邮件列表
8、SocketAppender 将LogEvent以普通格式发送到远程主机
9、SyslogAppender 将LogEvent以RFC 5424格式发送到远程主机
10、AsynchAppender 将一个LogEvent异步地写入多个不同输出地
11、ConsoleAppender 将LogEvent输出到命令行
12、FailoverAppender 维护一个队列,系统将尝试向队列中的Appender依次输出LogEvent,直到有一个成功为止
我设置的是console
@PerformanceSensitive({"allocation"})
protected void callAppenders(final LogEvent event) {
AppenderControl[] controls = this.appenders.get();
for(int i = 0; i < controls.length; ++i) {
controls[i].callAppender(event);
}
}
之后就是调用对应的callappender
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
0
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
1
callappender0->trycallappender->this.appender.append(event) //调用consoleappender的append ->tryappend->directEncodeEvent->(this.getLayout().encode)
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
2
在5行有个totext对我们的event进行处理
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
3
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
4
这里会循环调用formatter中对应的converter中format方法来进行处理
默认有11个
当调用到messagepatternconverter的format时
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
5
注意
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
6
在判断存在$的情况下且$后面是{ 会截取字符串然后调用replace方法
replace方法最终调用到substitute
private void logMessageTrackRecursion(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable throwable) {
try {
incrementRecursionDepth();
this.tryLogMessage(fqcn, this.getLocation(fqcn), level, marker, message, throwable);
} finally {
decrementRecursionDepth();
}
}
7
走到resolveVariable 最终获取lookup对象后 进行lookup.lookup
最终造成jndi注入
关于log4j的编写payload
关于log4j的变形payload 需要看一下substitute
关注以下valueDelimiterMatcher
在下面的判断中遇到: - 会进行substring的操作
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...