Hessian源码分析和Hack --让Hessian携带远程调用端的信息

本文介绍如何通过修改Hessian源码实现在远程调用时获取客户端IP地址的方法,涉及HessianServlet及ServiceContext类的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目选定Hessian作为web service的实现方式,确实很轻量级,速度就跟直接用socket差不多,全是二进制传送节约了不少开销。但是在使用过程中有业务需要是必须获得远程端的ip地址,主机名等信息的。翻便Hessian的文档和google了n次未果,迫不得已到caucho和spring论坛去问,都没有得到答复。今天心一横把hessian的源代码加入到项目中单步跟踪,总算有点小收获。献丑分享出来,一方面给需要的朋友,主要还是希望各位找找是否存在bug,以及是否有更好的改良。

一:先撇开Spring不谈,来看看纯Hessian的调用
按照hessian文档里边介绍的demo,在web.xml里边如下配置

<servlet>   
<servlet-name>hello</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-class</param-name>
<param-value>example.BasicService</param-value>
</init-param>
<init-param>
<param-name>home-api</param-name>
<param-value>example.Basic</param-value>
</init-param>
</servlet>

<servlet-mapping>
<url-pattern>/hello</url-pattern>
<servlet-name>hello</servlet-name>
</servlet-mapping>

<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-class</param-name>
<param-value>example.BasicService</param-value>
</init-param>
<init-param>
<param-name>home-api</param-name>
<param-value>example.Basic</param-value>
</init-param>
</servlet>

<servlet-mapping>
<url-pattern>/hello</url-pattern>
<servlet-name>hello</servlet-name>
</servlet-mapping>



由此可知Hessian调用的入口是HessianServlet这个Servlet,进去看看


/**  
* Servlet for serving Hessian services.
*/
public class HessianServlet extends GenericServlet {
private Class _homeAPI;
private Object _homeImpl;

private Class _objectAPI;
private Object _objectImpl;

private HessianSkeleton _homeSkeleton;
private HessianSkeleton _objectSkeleton;

private SerializerFactory _serializerFactory;

public String getServletInfo()
{
return "Hessian Servlet";
}

/**
* Sets the home api.
*/
public void setHomeAPI(Class api)
{
_homeAPI = api;
}

/**
* Sets the home implementation
*/
public void setHome(Object home)
{
_homeImpl = home;
}

/**
* Sets the object api.
*/
public void setObjectAPI(Class api)
{
_objectAPI = api;
}

/**
* Sets the object implementation
*/
public void setObject(Object object)
{
_objectImpl = object;
}

/**
* Sets the service class.
*/
public void setService(Object service)
{
setHome(service);
}

/**
* Sets the api-class.
*/
public void setAPIClass(Class api)
{
setHomeAPI(api);
}

/**
* Gets the api-class.
*/
public Class getAPIClass()
{
return _homeAPI;
}

/**
* Sets the serializer factory.
*/
public void setSerializerFactory(SerializerFactory factory)
{
_serializerFactory = factory;
}

/**
* Gets the serializer factory.
*/
public SerializerFactory getSerializerFactory()
{
if (_serializerFactory == null)
_serializerFactory = new SerializerFactory();

return _serializerFactory;
}

/**
* Sets the serializer send collection java type.
*/
public void setSendCollectionType(boolean sendType)
{
getSerializerFactory().setSendCollectionType(sendType);
}

/**
* Initialize the service, including the service object.
*/
public void init(ServletConfig config)
throws ServletException
{
super.init(config);

try {
if (_homeImpl != null) {
}
else if (getInitParameter("home-class") != null) {
String className = getInitParameter("home-class");

Class homeClass = loadClass(className);

_homeImpl = homeClass.newInstance();

init(_homeImpl);
}
else if (getInitParameter("service-class") != null) {
String className = getInitParameter("service-class");

Class homeClass = loadClass(className);

_homeImpl = homeClass.newInstance();

init(_homeImpl);
}
else {
if (getClass().equals(HessianServlet.class))
throw new ServletException("server must extend HessianServlet");

_homeImpl = this;
}

if (_homeAPI != null) {
}
else if (getInitParameter("home-api") != null) {
String className = getInitParameter("home-api");

_homeAPI = loadClass(className);
}
else if (getInitParameter("api-class") != null) {
String className = getInitParameter("api-class");

_homeAPI = loadClass(className);
}
else if (_homeImpl != null) {
_homeAPI = findRemoteAPI(_homeImpl.getClass());

if (_homeAPI == null)
_homeAPI = _homeImpl.getClass();
}

if (_objectImpl != null) {
}
else if (getInitParameter("object-class") != null) {
String className = getInitParameter("object-class");

Class objectClass = loadClass(className);

_objectImpl = objectClass.newInstance();

init(_objectImpl);
}

if (_objectAPI != null) {
}
else if (getInitParameter("object-api") != null) {
String className = getInitParameter("object-api");

_objectAPI = loadClass(className);
}
else if (_objectImpl != null)
_objectAPI = _objectImpl.getClass();

_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
if (_objectAPI != null)
_homeSkeleton.setObjectClass(_objectAPI);

if (_objectImpl != null) {
_objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);
_objectSkeleton.setHomeClass(_homeAPI);
}
else
_objectSkeleton = _homeSkeleton;
} catch (ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}

private Class findRemoteAPI(Class implClass)
{
if (implClass == null || implClass.equals(GenericService.class))
return null;

Class []interfaces = implClass.getInterfaces();

if (interfaces.length == 1)
return interfaces[0];

return findRemoteAPI(implClass.getSuperclass());
}

private Class loadClass(String className)
throws ClassNotFoundException
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();

if (loader != null)
return Class.forName(className, false, loader);
else
return Class.forName(className);
}

private void init(Object service)
throws ServletException
{
if (service instanceof Service)
((Service) service).init(getServletConfig());
else if (service instanceof Servlet)
((Servlet) service).init(getServletConfig());
}

/**
* Execute a request. The path-info of the request selects the bean.
* Once the bean's selected, it will be applied.
*/
public void service(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;

if (! req.getMethod().equals("POST")) {
res.setStatus(500, "Hessian Requires POST");
PrintWriter out = res.getWriter();

res.setContentType("text/html");
out.println("<h1>Hessian Requires POST</h1>");

return;
}

String serviceId = req.getPathInfo();
String objectId = req.getParameter("id");
if (objectId == null)
objectId = req.getParameter("ejbid");

ServiceContext.begin(req, serviceId, objectId);

try {
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();

Hessian2Input in = new Hessian2Input(is);
AbstractHessianOutput out;

SerializerFactory serializerFactory = getSerializerFactory();

in.setSerializerFactory(serializerFactory);

int code = in.read();

if (code != 'c') {
// XXX: deflate
throw new IOException("expected 'c' in hessian input at " + code);
}

int major = in.read();
int minor = in.read();

if (major >= 2)
out = new Hessian2Output(os);
else
out = new HessianOutput(os);

out.setSerializerFactory(serializerFactory);

if (objectId != null)
_objectSkeleton.invoke(in, out);
else
_homeSkeleton.invoke(in, out);

out.close();
} catch (RuntimeException e) {
throw e;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException(e);
} finally {
ServiceContext.end();
}
}
}

/**
* Servlet for serving Hessian services.
*/
public class HessianServlet extends GenericServlet {
private Class _homeAPI;
private Object _homeImpl;

private Class _objectAPI;
private Object _objectImpl;

private HessianSkeleton _homeSkeleton;
private HessianSkeleton _objectSkeleton;

private SerializerFactory _serializerFactory;

public String getServletInfo()
{
return "Hessian Servlet";
}

/**
* Sets the home api.
*/
public void setHomeAPI(Class api)
{
_homeAPI = api;
}

/**
* Sets the home implementation
*/
public void setHome(Object home)
{
_homeImpl = home;
}

/**
* Sets the object api.
*/
public void setObjectAPI(Class api)
{
_objectAPI = api;
}

/**
* Sets the object implementation
*/
public void setObject(Object object)
{
_objectImpl = object;
}

/**
* Sets the service class.
*/
public void setService(Object service)
{
setHome(service);
}

/**
* Sets the api-class.
*/
public void setAPIClass(Class api)
{
setHomeAPI(api);
}

/**
* Gets the api-class.
*/
public Class getAPIClass()
{
return _homeAPI;
}

/**
* Sets the serializer factory.
*/
public void setSerializerFactory(SerializerFactory factory)
{
_serializerFactory = factory;
}

/**
* Gets the serializer factory.
*/
public SerializerFactory getSerializerFactory()
{
if (_serializerFactory == null)
_serializerFactory = new SerializerFactory();

return _serializerFactory;
}

/**
* Sets the serializer send collection java type.
*/
public void setSendCollectionType(boolean sendType)
{
getSerializerFactory().setSendCollectionType(sendType);
}

/**
* Initialize the service, including the service object.
*/
public void init(ServletConfig config)
throws ServletException
{
super.init(config);

try {
if (_homeImpl != null) {
}
else if (getInitParameter("home-class") != null) {
String className = getInitParameter("home-class");

Class homeClass = loadClass(className);

_homeImpl = homeClass.newInstance();

init(_homeImpl);
}
else if (getInitParameter("service-class") != null) {
String className = getInitParameter("service-class");

Class homeClass = loadClass(className);

_homeImpl = homeClass.newInstance();

init(_homeImpl);
}
else {
if (getClass().equals(HessianServlet.class))
throw new ServletException("server must extend HessianServlet");

_homeImpl = this;
}

if (_homeAPI != null) {
}
else if (getInitParameter("home-api") != null) {
String className = getInitParameter("home-api");

_homeAPI = loadClass(className);
}
else if (getInitParameter("api-class") != null) {
String className = getInitParameter("api-class");

_homeAPI = loadClass(className);
}
else if (_homeImpl != null) {
_homeAPI = findRemoteAPI(_homeImpl.getClass());

if (_homeAPI == null)
_homeAPI = _homeImpl.getClass();
}

if (_objectImpl != null) {
}
else if (getInitParameter("object-class") != null) {
String className = getInitParameter("object-class");

Class objectClass = loadClass(className);

_objectImpl = objectClass.newInstance();

init(_objectImpl);
}

if (_objectAPI != null) {
}
else if (getInitParameter("object-api") != null) {
String className = getInitParameter("object-api");

_objectAPI = loadClass(className);
}
else if (_objectImpl != null)
_objectAPI = _objectImpl.getClass();

_homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
if (_objectAPI != null)
_homeSkeleton.setObjectClass(_objectAPI);

if (_objectImpl != null) {
_objectSkeleton = new HessianSkeleton(_objectImpl, _objectAPI);
_objectSkeleton.setHomeClass(_homeAPI);
}
else
_objectSkeleton = _homeSkeleton;
} catch (ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}

private Class findRemoteAPI(Class implClass)
{
if (implClass == null || implClass.equals(GenericService.class))
return null;

Class []interfaces = implClass.getInterfaces();

if (interfaces.length == 1)
return interfaces[0];

return findRemoteAPI(implClass.getSuperclass());
}

private Class loadClass(String className)
throws ClassNotFoundException
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();

if (loader != null)
return Class.forName(className, false, loader);
else
return Class.forName(className);
}

private void init(Object service)
throws ServletException
{
if (service instanceof Service)
((Service) service).init(getServletConfig());
else if (service instanceof Servlet)
((Servlet) service).init(getServletConfig());
}

/**
* Execute a request. The path-info of the request selects the bean.
* Once the bean's selected, it will be applied.
*/
public void service(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;

if (! req.getMethod().equals("POST")) {
res.setStatus(500, "Hessian Requires POST");
PrintWriter out = res.getWriter();

res.setContentType("text/html");
out.println("<h1>Hessian Requires POST</h1>");

return;
}

String serviceId = req.getPathInfo();
String objectId = req.getParameter("id");
if (objectId == null)
objectId = req.getParameter("ejbid");

ServiceContext.begin(req, serviceId, objectId);

try {
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();

Hessian2Input in = new Hessian2Input(is);
AbstractHessianOutput out;

SerializerFactory serializerFactory = getSerializerFactory();

in.setSerializerFactory(serializerFactory);

int code = in.read();

if (code != 'c') {
// XXX: deflate
throw new IOException("expected 'c' in hessian input at " + code);
}

int major = in.read();
int minor = in.read();

if (major >= 2)
out = new Hessian2Output(os);
else
out = new HessianOutput(os);

out.setSerializerFactory(serializerFactory);

if (objectId != null)
_objectSkeleton.invoke(in, out);
else
_homeSkeleton.invoke(in, out);

out.close();
} catch (RuntimeException e) {
throw e;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException(e);
} finally {
ServiceContext.end();
}
}
}



先看init()函数,功能还是一样,初始话一些东西,读入init-param的内容,并且load这些init-param的class

主要的还是service()函数
在service函数里边会获得request和response对象的输入和输出流,用来构造Hessian2Input和Hessian2Output,Hessian就是解析这两个东西来执行函数调用的。当然,在service里边还有一个重要的语句
Java代码
ServiceContext.begin(req, serviceId, objectId);

ServiceContext.begin(req, serviceId, objectId);
这个函数有点奇怪,我每次到这里serviceId和objectId都是空,不知道是不是历史遗留问题还存在这两个参数。
进去这个类看看

public class ServiceContext {   
private static final ThreadLocal _localContext = new ThreadLocal();

private ServletRequest _request;
private String _serviceName;
private String _objectId;
private int _count;
private HashMap _headers = new HashMap();

private ServiceContext()
{
}

/**
* Sets the request object prior to calling the service's method.
*
* @param request the calling servlet request
* @param serviceId the service identifier
* @param objectId the object identifier
*/
public static void begin(ServletRequest request,
String serviceName,
String objectId)
throws ServletException
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context == null) {
context = new ServiceContext();
_localContext.set(context);
}

context._request = request;
context._serviceName = serviceName;
context._objectId = objectId;
context._count++;
}

/**
* Returns the service request.
*/
public static ServiceContext getContext()
{
return (ServiceContext) _localContext.get();
}

/**
* Adds a header.
*/
public void addHeader(String header, Object value)
{
_headers.put(header, value);
}

/**
* Gets a header.
*/
public Object getHeader(String header)
{
return _headers.get(header);
}

/**
* Gets a header from the context.
*/
public static Object getContextHeader(String header)
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context.getHeader(header);
else
return null;
}

/**
* Returns the service request.
*/
public static ServletRequest getContextRequest()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._request;
else
return null;
}

/**
* Returns the service id, corresponding to the pathInfo of the URL.
*/
public static String getContextServiceName()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._serviceName;
else
return null;
}

/**
* Returns the object id, corresponding to the ?id= of the URL.
*/
public static String getContextObjectId()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._objectId;
else
return null;
}

/**
* Cleanup at the end of a request.
*/
public static void end()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null && --context._count == 0) {
context._request = null;

context._headers.clear();
}
}

/**
* Returns the service request.
*
* @deprecated
*/
public static ServletRequest getRequest()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._request;
else
return null;
}

/**
* Returns the service id, corresponding to the pathInfo of the URL.
*
* @deprecated
*/
public static String getServiceName()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._serviceName;
else
return null;
}

/**
* Returns the object id, corresponding to the ?id= of the URL.
*
* @deprecated
*/
public static String getObjectId()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._objectId;
else
return null;
}
}

public class ServiceContext {
private static final ThreadLocal _localContext = new ThreadLocal();

private ServletRequest _request;
private String _serviceName;
private String _objectId;
private int _count;
private HashMap _headers = new HashMap();

private ServiceContext()
{
}

/**
* Sets the request object prior to calling the service's method.
*
* @param request the calling servlet request
* @param serviceId the service identifier
* @param objectId the object identifier
*/
public static void begin(ServletRequest request,
String serviceName,
String objectId)
throws ServletException
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context == null) {
context = new ServiceContext();
_localContext.set(context);
}

context._request = request;
context._serviceName = serviceName;
context._objectId = objectId;
context._count++;
}

/**
* Returns the service request.
*/
public static ServiceContext getContext()
{
return (ServiceContext) _localContext.get();
}

/**
* Adds a header.
*/
public void addHeader(String header, Object value)
{
_headers.put(header, value);
}

/**
* Gets a header.
*/
public Object getHeader(String header)
{
return _headers.get(header);
}

/**
* Gets a header from the context.
*/
public static Object getContextHeader(String header)
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context.getHeader(header);
else
return null;
}

/**
* Returns the service request.
*/
public static ServletRequest getContextRequest()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._request;
else
return null;
}

/**
* Returns the service id, corresponding to the pathInfo of the URL.
*/
public static String getContextServiceName()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._serviceName;
else
return null;
}

/**
* Returns the object id, corresponding to the ?id= of the URL.
*/
public static String getContextObjectId()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._objectId;
else
return null;
}

/**
* Cleanup at the end of a request.
*/
public static void end()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null && --context._count == 0) {
context._request = null;

context._headers.clear();
}
}

/**
* Returns the service request.
*
* @deprecated
*/
public static ServletRequest getRequest()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._request;
else
return null;
}

/**
* Returns the service id, corresponding to the pathInfo of the URL.
*
* @deprecated
*/
public static String getServiceName()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._serviceName;
else
return null;
}

/**
* Returns the object id, corresponding to the ?id= of the URL.
*
* @deprecated
*/
public static String getObjectId()
{
ServiceContext context = (ServiceContext) _localContext.get();

if (context != null)
return context._objectId;
else
return null;
}
}


原来ServiceContext 是用来保存当前调用线程的上下文的,比如request对象等(不知道这个解释对不对)。有了这个东西就太好了,因为里边有request,就有了调用端的一切信息,呵呵。

继续回来看那个Servlet,到了真正调用的时候了,也就是这段代码


if (objectId != null)   
ctSkeleton.invoke(in, out);
else
Skeleton.invoke(in, out);

if (objectId != null)
_objectSkeleton.invoke(in, out);
else
_homeSkeleton.invoke(in, out);


跟踪invoke方法看看真面目


public void invoke(AbstractHessianInput in, AbstractHessianOutput out)   
throws Throwable
{
ServiceContext context = ServiceContext.getContext();

String header;
while ((header = in.readHeader()) != null) {
Object value = in.readObject();

context.addHeader(header, value);
}
String ip = context.getContextRequest().getRemoteAddr();
String methodName = in.readMethod();
Method method = getMethod(methodName);

if (method != null) {
}
else if ("_hessian_getAttribute".equals(methodName)) {
String attrName = in.readString();
in.completeCall();

String value = null;

if ("java.api.class".equals(attrName))
alue = getAPIClassName();
else if ("java.home.class".equals(attrName))
alue = getHomeClassName();
else if ("java.object.class".equals(attrName))
alue = getObjectClassName();

out.startReply();

out.writeObject(value);

out.completeReply();
return;
}
else if (method == null) {
out.startReply();
out.writeFault("NoSuchMethodException",
"The service has no method named: " + in.getMethod(),
null);
out.completeReply();
return;
}

Class []args = method.getParameterTypes();
Object []values = new Object[args.length];

//args[0]

for (int i = 0; i < args.length; i++){
if(i == args.length-1){
values[i] = in.readObject(args[i], ip);
}else{
values[i] = in.readObject(args[i]);
}

}


in.completeCall();

Object result = null;

try {
result = method.invoke(_service, values);
} catch (Throwable e) {
if (e instanceof InvocationTargetException)
e = ((InvocationTargetException) e).getTargetException();

log.log(Level.WARNING, e.toString(), e);

out.startReply();
out.writeFault("ServiceException", e.getMessage(), e);
out.completeReply();
return;
}

out.startReply();

out.writeObject(result);

out.completeReply();
}

public void invoke(AbstractHessianInput in, AbstractHessianOutput out)
throws Throwable
{
ServiceContext context = ServiceContext.getContext();

String header;
while ((header = in.readHeader()) != null) {
Object value = in.readObject();

context.addHeader(header, value);
}
String ip = context.getContextRequest().getRemoteAddr();
String methodName = in.readMethod();
Method method = getMethod(methodName);

if (method != null) {
}
else if ("_hessian_getAttribute".equals(methodName)) {
String attrName = in.readString();
in.completeCall();

String value = null;

if ("java.api.class".equals(attrName))
value = getAPIClassName();
else if ("java.home.class".equals(attrName))
value = getHomeClassName();
else if ("java.object.class".equals(attrName))
value = getObjectClassName();

out.startReply();

out.writeObject(value);

out.completeReply();
return;
}
else if (method == null) {
out.startReply();
out.writeFault("NoSuchMethodException",
"The service has no method named: " + in.getMethod(),
null);
out.completeReply();
return;
}

Class []args = method.getParameterTypes();
Object []values = new Object[args.length];

//args[0]

for (int i = 0; i < args.length; i++){
if(i == args.length-1){
values[i] = in.readObject(args[i], ip);
}else{
values[i] = in.readObject(args[i]);
}

}


in.completeCall();

Object result = null;

try {
result = method.invoke(_service, values);
} catch (Throwable e) {
if (e instanceof InvocationTargetException)
e = ((InvocationTargetException) e).getTargetException();

log.log(Level.WARNING, e.toString(), e);

out.startReply();
out.writeFault("ServiceException", e.getMessage(), e);
out.completeReply();
return;
}

out.startReply();

out.writeObject(result);

out.completeReply();
}


就是在这个方法里边,hessian把包装过的输入输出流当作参数传入并进行解析的,看看这个函数的第一句,正是取得ServiceContext的地方,此时应该就是把刚才Servlet里边保存的上下文取出来使用。
这个时候出现了第一个hack的地方 Java代码
String ip = context.getContextRequest().getRemoteAddr();

String ip = context.getContextRequest().getRemoteAddr();在此处我取得远程的ip地址保存起来。然后在第二个hack的地方

Class []args = method.getParameterTypes();   
Object []values = new Object[args.length];

//args[0]

for (int i = 0; i < args.length; i++){
if(i == args.length-1){
values[i] = in.readObject(args[i], ip);
}else{
values[i] = in.readObject(args[i]);
}

}

Class []args = method.getParameterTypes();
Object []values = new Object[args.length];

//args[0]

for (int i = 0; i < args.length; i++){
if(i == args.length-1){
values[i] = in.readObject(args[i], ip);
}else{
values[i] = in.readObject(args[i]);
}

}
我用这个ip地址取代最后一个参数(web服务函数的参数,即远程端调用的函数的参数)。
第三个hack的地方就是 in.readObject(args[i], ip); 这个方法。 这个方法是我自己加的,原本只有
in.readObject(args[i]); 这个方法。 这个方法就是hessian读取参数值的地方
进去看看

/**  
* Reads an object from the input stream with an expected type.
*/
public Object readObject(Class cl, String ip)
throws IOException
{
if (cl == null || cl == Object.class)
return readObject();

int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();

switch (tag) {
case 'N':
return null;

case 'M':
{
String type = readType();
Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type);

if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
return reader.readMap(this);

reader = findSerializerFactory().getDeserializer(cl);

return reader.readMap(this);
}

case 'O':
{
return readObjectDefinition(cl);
}

case 'o':
{
int ref = readInt();

ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref - 1);

return readObjectInstance(cl, def);
}

case 'V':
{
String type = readType();
int length = readLength();

Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type);

if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
return reader.readList(this, length);

reader = findSerializerFactory().getDeserializer(cl);

Object v = reader.readList(this, length);

return v;
}

case 'v':
{
int ref = readInt();
String type = (String) _types.get(ref);
int length = readInt();

Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type);

if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
return reader.readLengthList(this, length);

reader = findSerializerFactory().getDeserializer(cl);

Object v = reader.readLengthList(this, length);

return v;
}

case 'R':
{
int ref = parseInt();

return _refs.get(ref);
}

case 'r':
{
String type = readType();
String url = readString();

return resolveRemote(type, url);
}
}

if (tag >= 0)
_offset--;

Object value = findSerializerFactory().getDeserializer(cl).readObject(this);

if(value instanceof String){
value = ip;
}
return value;
}

/**
* Reads an object from the input stream with an expected type.
*/
public Object readObject(Class cl, String ip)
throws IOException
{
if (cl == null || cl == Object.class)
return readObject();

int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();

switch (tag) {
case 'N':
return null;

case 'M':
{
String type = readType();
Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type);

if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
return reader.readMap(this);

reader = findSerializerFactory().getDeserializer(cl);

return reader.readMap(this);
}

case 'O':
{
return readObjectDefinition(cl);
}

case 'o':
{
int ref = readInt();

ObjectDefinition def = (ObjectDefinition) _classDefs.get(ref - 1);

return readObjectInstance(cl, def);
}

case 'V':
{
String type = readType();
int length = readLength();

Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type);

if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
return reader.readList(this, length);

reader = findSerializerFactory().getDeserializer(cl);

Object v = reader.readList(this, length);

return v;
}

case 'v':
{
int ref = readInt();
String type = (String) _types.get(ref);
int length = readInt();

Deserializer reader;
reader = findSerializerFactory().getObjectDeserializer(type);

if (cl != reader.getType() && cl.isAssignableFrom(reader.getType()))
return reader.readLengthList(this, length);

reader = findSerializerFactory().getDeserializer(cl);

Object v = reader.readLengthList(this, length);

return v;
}

case 'R':
{
int ref = parseInt();

return _refs.get(ref);
}

case 'r':
{
String type = readType();
String url = readString();

return resolveRemote(type, url);
}
}

if (tag >= 0)
_offset--;

Object value = findSerializerFactory().getDeserializer(cl).readObject(this);

if(value instanceof String){
value = ip;
}
return value;
}
我重载了这个方法,加入了一个String类型的参数,用来把ip地址传进去,并且最后返回这个值。到了这里,hack的原理大家应该知道了--就是强行修改远程调用端的调用函数里边的最后一个参数的值(规定为String类型),把这个值设为我想要的信息,那么服务端的服务函数就会获得这个值,并且进行后续处理。
剩下的步骤就原封不动的是hessian来处理了,没有需要干涉的地方,你也就能在你的服务端service函数里边获得这个你想要的信息了。

这就是Hessian的一个普通流程,不知道分析和Hack的对不对,我在这里是调试成功了,但是还没彻底测试有没有其它bug。 至于跟Spring的结合,待会儿跟帖来说。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值