Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ERPViewer—— Web反向工程(二) #118

Open
soapgu opened this issue Mar 4, 2022 · 0 comments
Open

ERPViewer—— Web反向工程(二) #118

soapgu opened this issue Mar 4, 2022 · 0 comments
Labels

Comments

@soapgu
Copy link
Owner

soapgu commented Mar 4, 2022

  • 前言

接这篇日志 ERP网页抓包笔记——Web反向工程(一),一定要把逆向工程进行到底。
上一篇基本把ERP的登陆以及安全机制的原理整理,http登陆验证弄得差不多了。从可行性上讲已经完全没有问题了。接下来就需要最终用代码再验证下前面的成果。就像打仗,最后要占领才算是最终胜利。所以还是要借用我们毛主席这句诗词

  • 宜将剩勇追穷寇不可沽名学霸王

图片

将革命进行到底
  • 项目构建

项目的名字就叫ERPViewer,意思就是ERP系统就是是透明了(只要我输入用户密码),我想看啥就看啥。只是一个辅助软件,并不是破坏其安全的。
项目是用VS2019建的(很久没写C#了)

  1. 把baseUrl的配置设置到app.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
    </startup>
	<appSettings>
		<add key="baseUrl" value="https://erp.shgbit.com" />
	</appSettings>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  1. MainWindows.xaml
    不是重点,略

  2. 设置HttpClient

         /// <summary>
        /// 初始化HttpClient
        /// </summary>
        private void InitHttpClient()
        {
            handler = new HttpClientHandler();
            httpClient = new HttpClient(handler)
            {
                BaseAddress = new Uri(baseUrl),
                Timeout = TimeSpan.FromSeconds(10)
            };
        }

HttpClient是老熟人了,代async 的api,使用方便
这次我们用了HttpClientHandler帮忙做HttpClient的构造参数
HttpClientHandler是代理类,可以配置一系列相关参数,这次我们主要为了用它控制Cookie。
默认HttpClientHandler是开启重定向和Cookie,所以我们前面分析有几个来回的交互,类库帮我们处理了,省事好多。

  1. 实现获取双JSESSIONID和获取登陆页的验证码
  • 获取两个JSESSIONID
    由于自动跳转以及Cookie是内部实现,所以我们在调用get /jqerp/后,HttpClient都帮我完成了
MessageBox.Show("Cookie:" + handler.CookieContainer.GetCookies(new Uri(baseUrl + "/jqerp/")).Count);
MessageBox.Show("cas Cookie:" + handler.CookieContainer.GetCookies(new Uri(baseUrl + "/cas/")).Count)

可以通过HttpClientHandler的CookieContainer看的一清二楚

  • 获取验证码
    在自动跳转后,最后获取的body是一个登陆页面,
    由于登陆的验证码“藏”在里面,所以body还有用
    怎么拿出来那?

应该有这种分析html标签的库的

我用的第三方库AngleSharp
直接nuget一装就行了。
找出value也很简单,通过name来找,api是GetElementsByName,高仿js语法一看就懂,违和感很强。

         /// <summary>
        /// 1 获取jquery的JSESSSIONID存储至Cookie
        /// 2 获取登陆页面的It字段验证码
        /// </summary>
        /// <returns>It字段验证码</returns>
        private async Task<String> GetItCode()
        {
            string retValue = null;
            HttpResponseMessage response = await httpClient.GetAsync("/jqerp/");
            if (response.IsSuccessStatusCode)
            {

                var html = await response.Content.ReadAsStringAsync();
                var config = AngleSharp.Configuration.Default;
                //Create a new context for evaluating webpages with the given config
                var context = BrowsingContext.New(config);

                //Parse the document from the content of a response to a virtual request
                var document = await context.OpenAsync(req => req.Content(html));
                retValue = document.GetElementsByName("lt")[0].GetAttribute("value");
            }
            return retValue;
        }

代码实现

  1. 登陆操作
    前面“验证码”也拿到了,当然比市面上的图形验证码要简单一点
    登陆的操作模拟postman,content-type是application/x-www-form-urlencoded
    针对这种body,C#为我们提供了FormUrlEncodedContent类来操作
    我怕服务端认出我们是“假货”,所以我把浏览器上发送的body都填的一模一样(心虚)
    图片

好在有惊无险的过了。
接下来就是要拿取CUNAME了。
虽然和用户名是一致的。
按照“原著”,CUNAME是客户端通过js设置的。
但是为了起到“锻炼”的作用,必须要靠抓取。
简单一点就用正则匹配再用json解析获取
获取到CUNAME后设置到Cookie里面去

        /// <summary>
        /// 完成登陆操作
        /// 并抓取js脚本中的logonCode
        /// 把logonCode加入到key为_CUNAME的cookie中
        /// </summary>
        /// <param name="itValue"></param>
        /// <returns></returns>
        private async Task<bool> Login( string itValue ) 
        {
            bool retValue = false;
            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("lt", itValue));
            values.Add(new KeyValuePair<string, string>("username", txtUserName.Text));
            values.Add(new KeyValuePair<string, string>("password", txtPassword.Password));
            values.Add(new KeyValuePair<string, string>("execution", "e1s1"));
            values.Add(new KeyValuePair<string, string>("_eventId", "submit"));
            var content = new FormUrlEncodedContent(values);
            var response = await httpClient.PostAsync("/cas/login?service=https://erp.shgbit.com/jqerp/", content);
            var bodyString = await response.Content.ReadAsStringAsync();

            var match = Regex.Match(bodyString, @"(?<=_userInfo=).*(?=;)");
            if (match.Success)
            {
                var loginCode = JArray.Parse(match.Value).First().Value<string>("logonCode");
                this.handler.CookieContainer.Add(new System.Net.Cookie("_CUNAME", loginCode, "/jqerp", "erp.shgbit.com"));
                retValue = true;
            }
            return retValue;
        }

代码如下

  1. 获取任务数据
    前面主要任务都完成好了,这步工作反而变得非常简单了
    输入值
    Cookie
    JSESSIONID:已经获取
    CUNAME:已经设置好
    actType:loadTaskListJSON 输入固定值
        /// <summary>
        /// 获取任务信息的json
        /// </summary>
        /// <returns></returns>
        private async Task<String> GetTaskInfo() 
        {
            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("actType", "loadTaskListJSON"));
            var content = new FormUrlEncodedContent(values);
            var response = await httpClient.PostAsync("/jqerp/web/taskInfoService", content);
            var bodyString = await response.Content.ReadAsStringAsync();
            return bodyString;
        }

代码看上去是最简单的一步。

  1. 串起来!
    好了分步骤都分解完毕,串起来吧
        private async void BtnLogin_Click(object sender, RoutedEventArgs e)
        {
            var itCode = await this.GetItCode();
            if (itCode != null)
            {
                var success = await this.Login(itCode);
                if (success)
                {
                    var task = await this.GetTaskInfo();
                    MessageBox.Show(task);
                }
            }
        }
  1. 运行结果
    图片
  • 小结

反向工程不必正向工程,缺乏必要的“情报”,是属于迷雾探索型。基本靠结果反过来推过程

  • 需要把整个过程的信息采集全!
    不能从登陆开始,要从访问网络开始。好在现在的“工具”真的很强大,一个浏览器里面的开发者工具基本全部搞定了。Fiddler 也很好用可以抓客户端http的包,就是不太稳定。wireshark也不错。不过不太趁手一般我最后再用

  • 需要耐性,抽丝剥茧。但是一点突破,全局突破的感觉很爽

  • 步骤:情报收集分析 -> postman推演验证 -> 代码实现

  • 代码地址-ERPViewer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant