I'm using Frida to replace some win32 calls such as CreateFileW. I need to replace because I need to fundamentally change how the call works for various reasons. I'm finding that if I try to do something which indicates failure by setting a thread-local error (e.g. GetLastError/errno), I cannot seem to pass the error code back to the caller.
For example, the following code results in the output which follows.
import atexit
import os
import time
import signal
import sys
import frida
from dataclasses import dataclass
from typing import Any, Callable, cast, Union
from frida_tools.tracer import main
pid = frida.spawn(program="C:\\Windows\\System32\\notepad.exe", argv=["notepad.exe", "badfile"])
def do_attach_no_catch(pid):
session = frida.attach(pid)
script = session.create_script("""
const createFileWPtr = Module.getExportByName(null, 'CreateFileW');
const createFileWInput = ['pointer', 'int', 'int', 'pointer', 'int', 'int', 'pointer']
const createFileW = new SystemFunction(createFileWPtr, 'uint64', createFileWInput)
Interceptor.replace(createFileWPtr, new NativeCallback((fileName, access, shareMode, secAttr, createDisp, flags, templateFile) => {
send("Running createW")
const asStr = fileName.readUtf16String();
send(`Got ${asStr}`);
const result = createFileW(fileName, access, shareMode, secAttr, createDisp, flags, templateFile);
send(result)
this.lastError = result.lastError
return result.value
}, 'uint64', createFileWInput))
Interceptor.flush();
""")
def on_message(message, data):
print(message)
script.on('message', on_message)
script.load()
session.resume()
def do_attach(pid):
do_attach_no_catch(pid)
print("Starting")
do_attach(pid)
frida.resume(pid)
while True:
time.sleep(1)
Output:
{'type': 'send', 'payload': 'Running createW'}
{'type': 'send', 'payload': 'Got C:\\Windows\\Globalization\\Sorting\\sortdefault.nls'}
{'type': 'send', 'payload': {'value': '800', 'lastError': 0}}
{'type': 'send', 'payload': 'Running createW'}
{'type': 'send', 'payload': 'Got C:\\Windows\\Fonts\\staticcache.dat'}
{'type': 'send', 'payload': {'value': '836', 'lastError': 0}}
{'type': 'send', 'payload': 'Running createW'}
{'type': 'send', 'payload': 'Got C:\\Windows\\Registration\\R000000000006.clb'}
{'type': 'send', 'payload': {'value': '1000', 'lastError': 0}}
{'type': 'send', 'payload': 'Running createW'}
{'type': 'send', 'payload': 'Got C:\\Windows\\System32\\OLEACCRC.DLL'}
{'type': 'send', 'payload': {'value': '1016', 'lastError': 0}}
{'type': 'send', 'payload': 'Running createW'}
{'type': 'send', 'payload': 'Got badfile.txt'}
{'type': 'send', 'payload': {'value': '18446744073709551615', 'lastError': 2}}
{'type': 'send', 'payload': 'Running createW'}
{'type': 'send', 'payload': 'Got C:\\Windows\\System32\\imageres.dll'}
{'type': 'send', 'payload': {'value': '1180', 'lastError': 0}}
It's worth noting that notepad, at the end, pops up an error dialog saying no error happened -- i.e. it received an error code of 0. The documentation of the javascript api implies that setting this.lastError should do the trick. The docs mention that it exists in attach, and that replace is provided with a this object whose properties are similar to attach's.
I'm wondering if I'm just missing some simple integration which allows me to pass back the error code from the underlying function call, or whether this is fundamentally impossible with Frida as it stands.
Related
I added the below to the pipeline so while the pipeline is running - at some stage I want the user to choose from the parameters but the output returns with parentheses at beginning and end.
def envs = input(id: 'Upgarde', message: 'On which customer do you want to apply the upgrade?', submitter: 'admin', ok: 'Submit', parameters: [extendedChoice(defaultValue: env.ENV.split().toString(), description: '', descriptionPropertyValue: env.ENV.split().toString(), multiSelectDelimiter: '', name: 'Customers to upgrade', quoteValue: false, saveJSONParameterToFile: false, type: 'PT_MULTI_SELECT', value: env.ENV.split().toString())]).split(',')
Screenshot from the Jenkins UI:
enter image description here
Fixed by added .replace("[", "").replace("]", "")
I've setup a pipeline as code stack using jenkins groovy. For that I've coded some shared libraries to extend my CI/CD capabilities and avoid copy/paste some block code within all my pipelines code.
So I have a groovy function to add approval stage in pipelines, which I've tested using declarative pipeline in a Jenskins File successfully, but which fails when I try in my scripted pipeline function.
Here is the block code in a the declarative Jenkinsfile which works as you can see in the screenshots below.
stage('Approval') {
// no agent, so executors are not used up when waiting for approvals
when { changeset "vm-management/create-vm/**"}
agent none
steps {
script {
mail from: "$VM_EMAIL_FROM", to: "$VM_SUPPORT_EMAIL", subject: "APPROVAL REQUIRED FOR $JOB_NAME" , body: """Build $BUILD_NUMBER required an approval. Go to $BUILD_URL for more info."""
def deploymentDelay = input id: 'Deploy', message: 'Deploy to production?', parameters: [choice(choices: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24'], description: 'Hours to delay deployment?', name: 'deploymentDelay')]
sleep time: deploymentDelay.toInteger(), unit: 'HOURS'
}
}
}
But coming to try to add approval on the fly from my groovy function, it doesn't work.
def send(mail_from, mail_to, deploy_version) {
// no agent, so executors are not used up when waiting for approvals
/* when {
beforeAgent true
anyOf {
triggeredBy 'TimerTrigger'
}
}*/
script {
mail from: "${mail_from}", to: "${mail_to}", subject: "APPROVAL REQUIRED FOR $JOB_NAME" , body: """Build $BUILD_NUMBER from Deployment version ${deploy_version} required an approval. Go to $BUILD_URL for more info."""
def deploymentDelay = input id: 'Deploy', message: 'Deploy to production?', parameters: [choice(choices: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24'], description: 'Hours to delay deployment?', name: 'deploymentDelay')]
sleep time: deploymentDelay.toInteger(), unit: 'HOURS'
}
}
Jenkins keeps throwing exception like below :
groovy add manual approval using dshudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: script.call() is applicable for argument types: (org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [org.jenkinsci.plugins.workflow.cps.CpsClosure2#6097c139]
What Am I missing there please? How can I write my approval function in the groovy vars file so that I could have the expected behaviour like with the declarative pipeline stage above?
Just remove the script block, it's not needed (and isn't legal) in a scripted pipeline or a pipeline library global var
I have a Jenkins pipeline That has parameters defined via active choice parameter,
defining a default value is done by:
defaultValue: '',
you can put a string there or leave it empty which will give you the default result of the groovyScript.
I am trying to change the default parameter using a script so it will take the value using a groovy script.
This is the snippet of the relevant part of the pipeline:
parameters([
extendedChoice(
bindings: '',
defaultValue: '',
groovyClasspath: '',
groovyScript:"""
def proc = ["bash","-c","/usr/local/bin/aws s3 ls s3://Spark-Jenkins-Clusters/"].execute() | ["bash","-c","cut -c32-"].execute()
proc.waitForOrKill(10000)
return proc.text.tokenize()
""",
multiSelectDelimiter: ',',
name: 'Choose_Cluster',
description: 'This parameter is nice',
quoteValue: false,
saveJSONParameterToFile: false,
type: 'PT_SINGLE_SELECT',
visibleItemCount: 5
),
So The way to do that is to use "defaultGroovyScript",
I didn't find it in the documentation I just saw an option in the UI and tried it and luckily it worked:
This is what I finally did:
parameters([
extendedChoice(
bindings: '',
defaultGroovyScript: """
def proc = ["bash","-c","/usr/local/bin/aws s3 ls s3://Spark-Jenkins-Clusters/"].execute() | \
["bash","-c","sort"].execute() | \
["bash","-c","sed 's/PRE//g'"].execute() | \
["bash","-c","grep main"].execute() | \
["bash","-c","tail -n 1"].execute() | \
["bash","-c","tr -d '/'"].execute()
proc.waitForOrKill(10000)
return proc.text.tokenize().reverse()
""",
groovyClasspath: '',
groovyScript:"""
def proc = ["bash","-c","/usr/local/bin/aws s3 ls s3://Spark-Jenkins-Clusters/"].execute() | ["bash","-c","cut -c32-"].execute()
proc.waitForOrKill(10000)
return proc.text.tokenize()
""",
multiSelectDelimiter: ',',
name: 'Choose_Cluster',
description: 'This parameter is nice',
quoteValue: false,
saveJSONParameterToFile: false,
type: 'PT_SINGLE_SELECT',
visibleItemCount: 5
),
In Rails application I am trying to generate PDF basing on HTML page using Grover gem:
https://github.com/Studiosity/grover
PDF is generated properly, but just after that my Docker container is instantly killed. Problem doesn't occur in local environment, only inside docker.
Controller action:
def generate_pdf
grover = Grover.new('http://google.com', format: 'A4')
pdf = grover.to_pdf
send_data(pdf, filename: 'dummypdf.pdf', type: 'application/pdf')
end
config/initializers/grover.rb
Grover.configure do |config|
config.options = {
format: 'A4',
margin: {
top: '5px',
bottom: '10cm'
},
viewport: {
width: 640,
height: 480
},
prefer_css_page_size: true,
emulate_media: 'screen',
cache: false,
timeout: 0, # Timeout in ms. A value of `0` means 'no timeout'
request_timeout: 10000, # Timeout when fetching the content (overloads the `timeout` option)
convert_timeout: 20000, # Timeout when converting the content (overloads the `timeout` option, only applies to PDF conversion)
launch_args: ['--font-render-hinting=medium', '--no-sandbox', '--lang=ja', '--disable-dev-shm-usage', '--disable-gpu', '--disable-setuid-sandbox'],
wait_until: 'domcontentloaded'
}
end
Server log:
test_container_1 | 14:54:11 rails.1 | 2022-02-18 14:54:11.885970 I [7:puma server threadpool 003] {request_id: d9fc817c-1a51-4c25-80ac-da0f5c224f34} (4.743s) ProfilesController -- Completed #generate_pdf -- { :controller => "ProfilesController", :action => "generate_pdf", :params => { "kid_id" => "2" }, :format => "HTML", :method => "GET", :path => "/records/2/profile/generate_pdf", :status => 200, :view_runtime => 7.61, :allocations => 51203, :status_message => "OK" }
test_container_1 | 14:54:12 | exited with code 0
test_container_1 | 14:54:12 system | sending SIGTERM to all processes
test_container_1 | 14:54:12 | exited with code 0
test_container_1 | 14:54:12 rails.1 | - Gracefully stopping, waiting for requests to finish
test_container_1 | 14:54:12 rails.1 | Exiting
test_container_1 | 14:54:12 | exited with code 0
test_container_1 | 14:54:12 rails.1 | terminated by SIGTERM
test_container_1 | 14:54:12 webpack.1 | exited with code 0
I will be grateful for any suggestions how to deal with this issue (I also tried Dhalang gem instead of Grover but the result was the same)
I want to communicate between Elixir and Python. I don't want to use NIFs and stuff - I prefer loosely coupled using zeroMQ as this will allow me to use other languages than Python later. I am using the chumak library which is a native implementation of zeromq in Erlang, and seems well maintained. I have used it successfully in the past for pub sub.
Apart from pub-sub, I'm finding that req-rep and req-router sockets work fine. However dealer-router does not. This is really important because only dealer and router give you true async in zeromq.
Here is the python code for the router side:
import zmq
context = zmq.Context()
rout = context.socket(zmq.ROUTER)
rout.bind("tcp://192.168.1.192:8760")
Here is the Elixir req code which works fine...
iex(1)> {ok, sock1} = :chumak.socket(:req, 'reqid')
{:ok, #PID<0.162.0>}
iex(2)> {ok, _peer} = :chumak.connect(sock1, :tcp, '192.168.1.192', 8760)
{:ok, #PID<0.164.0>}
iex(3)> :chumak.send(sock1, 'hello from req socket')
:ok
.... because I get it on the Python side:
In [5]: xx = rout.recv_multipart()
In [6]: xx
Out[6]: ['reqid', '', 'hello from req socket']
However, here is what I get if I try a dealer socket on the Elixir side:
iex(4)> {ok, sock2} = :chumak.socket(:dealer, 'dealid')
{:ok, #PID<0.170.0>}
iex(5)> {ok, _peer} = :chumak.connect(sock2, :tcp, '192.168.1.192', 8760)
{:ok, #PID<0.172.0>}
iex(6)> :chumak.send(sock2, 'hello from dealer socket')
{:error, :not_implemented_yet}
iex(7)> :chumak.send_multipart(sock2, ['a', 'b', 'hello from dealer socket'])
22:13:38.705 [error] GenServer #PID<0.172.0> terminating
** (FunctionClauseError) no function clause matching in :chumak_protocol.encode_more_message/3
(chumak) /home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl:676: :chumak_protocol.encode_more_message('a', :null, %{})
(stdlib) lists.erl:1354: :lists.mapfoldl/3
(chumak) /home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl:664: :chumak_protocol.encode_message_multipart/3
(chumak) /home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_peer.erl:159: :chumak_peer.handle_cast/2
(stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:686: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:send, ['a', 'b', 'hello from dealer socket'], {#PID<0.160.0>, #Reference<0.79795089.2401763329.172383>}}}
State: {:state, :ready, '192.168.1.192', 8760, :client, [], :dealer, 'dealid', [], {3, 0}, #Port<0.4968>, {:decoder, :ready, 0, nil, nil, {:some, 3}, {:some, 0}, %{}, :null, false}, #PID<0.170.0>, {[], []}, [], false, false, false, :null, %{}}
22:13:38.710 [info] [:unhandled_handle_info, {:module, :chumak_socket}, {:msg, {:EXIT, #PID<0.172.0>, {:function_clause, [{:chumak_protocol, :encode_more_message, ['a', :null, %{}], [file: '/home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl', line: 676]}, {:lists, :mapfoldl, 3, [file: 'lists.erl', line: 1354]}, {:chumak_protocol, :encode_message_multipart, 3, [file: '/home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_protocol.erl', line: 664]}, {:chumak_peer, :handle_cast, 2, [file: '/home/tbrowne/code/elixir/chutest/deps/chumak/src/chumak_peer.erl', line: 159]}, {:gen_server, :try_dispatch, 4, [file: 'gen_server.erl', line: 616]}, {:gen_server, :handle_msg, 6, [file: 'gen_server.erl', line: 686]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 247]}]}}}]
As you can see I get this huge error on the :chumak.send_multipart, while :chumak.send doesn't even work. What's going on here?
The dealer socket works fine by the way from the Python side:
import zmq
context = zmq.Context()
deal = context.socket(zmq.DEALER)
deal.setsockopt_string(zmq.IDENTITY, u"Thomas")
deal.connect("tcp://192.168.1.192:8760")
deal.send("hello from python deal")
Now on router side:
In [5]: xx = rout.recv_multipart()
In [6]: xx
Out[6]: ['reqid', '', 'hello from req socket']
In [7]: dd = rout.recv_multipart()
In [8]: dd
Out[8]: ['Thomas', 'hello from python deal']
So I'm wondering if I have a syntax, or type error, in my Elixir chumak dealer socket, or if it's simply a bug. I have tried this on both amd64 and armv7l architectures and the problem is identical.
All the elixir code is based on the Erlang version in the chumak example for dealer-router.
My mix.exs deps looks like this:
[
{:chumak, "~> 1.2"},
{:msgpack, "~> 0.7.0"}
]
the only obvious thing I see is your use of send_multipart. Its signature in the source:
-spec send_multipart(SocketPid::pid(), [Data::binary()]) -> ok.
you are doing this:
:chumak.send_multipart(sock2, ['a', 'b', 'hello from dealer socket'])
------------
iex(2)> is_binary('a')
false
iex(3)> is_binary('hello from dealer socket')
false
Otherwise, I can not see much of a difference between your code and the example code that is in chumak's repo.